Capture Website Screenshots in Django with the Rendex API

Django handles views, models, and forms. Browser automation is not on that list. When a Django app needs to capture website screenshots, you have two choices: run a headless Chromium process in-process (memory pressure, crash recovery, dependency management) or call an API that does it for you.
This guide covers two concrete patterns using the Rendex Python SDK: a view that captures on demand, and a management command for scheduled batch jobs.
Prerequisites
- Django 4.2 or later, Python 3.10 or later
- A Rendex API key. The free tier gives you 100 calls per month, no credit card required. Get one at rendex.dev/login.
pip install rendexStep 1: Add the Key to Django Settings
Store the API key as an environment variable. Never hard-code it in source:
# settings.py
import os
RENDEX_API_KEY = os.environ.get("RENDEX_API_KEY", "")Keys use the rdx_ prefix, a single segment with no environment suffix. Set the variable before starting Django:
# Get your key at https://rendex.dev/login
export RENDEX_API_KEY=rdx_your_keyStep 2: Capture in a Django View
The SDK is synchronous and returns raw bytes. A POST view that accepts a URL and streams back the screenshot:
from django.conf import settings
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.http import require_POST
from rendex import Rendex, RendexApiError, RendexNetworkError
@require_POST
def capture_screenshot(request):
target_url = request.POST.get("url")
if not target_url:
return HttpResponseBadRequest("url is required")
rendex = Rendex(settings.RENDEX_API_KEY)
try:
result = rendex.screenshot(
target_url,
full_page=True,
format="png",
)
except RendexApiError as exc:
return HttpResponseBadRequest(f"Capture failed: {exc}")
except RendexNetworkError as exc:
return HttpResponse(f"Network error: {exc}", status=503)
return HttpResponse(
result.image,
content_type="image/png",
headers={"Content-Disposition": 'attachment; filename="screenshot.png"'},
)result.image is raw bytes. Write it to an ImageField, upload to S3, or stream it as shown above. result.metadata carries the width, height, format, and capture timestamp from the response headers.
Wire it up in urls.py:
from django.urls import path
from . import views
urlpatterns = [
path("screenshot/", views.capture_screenshot, name="capture_screenshot"),
]Step 3: Capture a Specific Element
Pass a CSS selector parameter to crop the capture to a single element on the page:
result = rendex.screenshot(
target_url,
selector="#dashboard-chart", # any valid CSS selector
format="png",
)The API returns an image clipped to the bounding box of the matched element. If the selector does not exist on the page, the call raises a 400 error. Test the selector in browser DevTools before using it in code.
Step 4: Batch Capture with a Management Command
Scheduled capture jobs should run outside the web process. A management command keeps capture logic away from request workers and can be wired to a cron task or a Celery beat schedule:
# myapp/management/commands/refresh_snapshots.py
from django.core.management.base import BaseCommand
from django.conf import settings
from rendex import Rendex, RendexApiError
from myapp.models import Page
class Command(BaseCommand):
help = "Capture screenshots for Page records missing a snapshot"
def handle(self, *args, **options):
rendex = Rendex(settings.RENDEX_API_KEY)
pages = Page.objects.filter(snapshot="")
for page in pages:
try:
result = rendex.screenshot(
page.url,
width=1280,
height=800,
format="png",
full_page=False,
)
# Write result.image to your storage of choice
self.stdout.write(f"OK {page.url}")
except RendexApiError as exc:
self.stderr.write(f"FAIL {page.url}: {exc}")Run it with python manage.py refresh_snapshots. The SDK raises RendexApiError for 4xx/5xx API responses and RendexNetworkError for connection failures, so you can handle them separately in your error reporting.
cURL Reference
The underlying REST endpoint if you prefer raw HTTP over the SDK:
# Get your API key at https://rendex.dev/login
curl -X POST https://api.rendex.dev/v1/screenshot \
-H "Authorization: Bearer rdx_your_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"fullPage": true,
"format": "png",
"width": 1280,
"height": 800
}' --output screenshot.pngREST parameters use camelCase. The Python SDK takes snake_case kwargs and converts them automatically: full_page=True becomes fullPage: true in the request body.
Troubleshooting
401 Unauthorized: The RENDEX_API_KEY environment variable is missing or the key is invalid. Confirm it starts with rdx_ and is set before Django starts.
408 Timeout: The target page took too long to load. Add best_attempt=True to return whatever rendered before the timeout rather than erroring out:
result = rendex.screenshot(
target_url,
best_attempt=True, # return partial render on timeout
timeout=20000, # milliseconds
)400 Selector not found: The selector passed to selector= does not match any element on the page. Verify it in browser DevTools, or remove the selector parameter to capture the full page instead.
View is slow: Screenshot capture blocks the Django request thread for the full duration of the call. For production, move the capture to a background task (Celery, Django-Q, or django-rq). The view creates the job and returns immediately; the worker calls the API and saves the result.
Next Steps
Test capture options without writing any code using the free screenshot tool. The full parameter list, including dark mode, cookie injection, custom headers, viewport sizing, and wait strategies, is in the SDK reference.
For more Python patterns, including Playwright comparisons and additional SDK examples, see How to Capture Website Screenshots with Python.
Ready to build? Get a free API key at rendex.dev. The free tier includes 100 calls per month and no credit card is required.