Django Screenshot API Integration

Capture website screenshots in Django with ScreenshotAPI. No Selenium or ChromeDriver required. Production-ready Python examples.

Last updated: 2026-03-25

Try ScreenshotAPI free

5 free credits. No credit card required.

Start for free

Capture Website Screenshots in Django with ScreenshotAPI

Adding website screenshot functionality to a Django application has traditionally meant installing Selenium, managing ChromeDriver binaries, and wrestling with headless browser configuration on production servers. These dependencies add hundreds of megabytes to your deployment, require careful version pinning, and fail unpredictably on platforms like Heroku or AWS Elastic Beanstalk.

A Django screenshot API integration with ScreenshotAPI eliminates all of that complexity. Your Django view makes one HTTP request, and ScreenshotAPI returns a pixel-perfect image. No browser binaries, no system dependencies, no container bloat.

Quick Start

  1. Sign up for ScreenshotAPI and copy your API key. You receive 5 free credits on signup.
  2. Install the Python SDK.
  3. Add your API key to Django settings.
  4. Create a view that captures screenshots.

Installation

Install the SDK with pip:

bash
pip install screenshotapi

Add the API key to your Django settings:

python
# settings.py SCREENSHOTAPI_KEY = env('SCREENSHOTAPI_KEY')

And in your .env file:

bash
SCREENSHOTAPI_KEY=sk_live_xxxxx

Basic Example

Create a view that accepts a URL and returns the screenshot:

python
# views.py import requests from django.conf import settings from django.http import HttpResponse, JsonResponse API_BASE = 'https://screenshotapi.to/api/v1/screenshot' def screenshot_view(request): url = request.GET.get('url') if not url: return JsonResponse({'error': 'url parameter is required'}, status=400) response = requests.get( API_BASE, params={ 'url': url, 'width': 1440, 'height': 900, 'type': 'png', }, headers={'x-api-key': settings.SCREENSHOTAPI_KEY}, timeout=30, ) if response.status_code != 200: return JsonResponse({'error': 'Screenshot capture failed'}, status=502) return HttpResponse( response.content, content_type='image/png', headers={'Cache-Control': 'public, max-age=86400'}, )

Wire it up in your URL configuration:

python
# urls.py from django.urls import path from . import views urlpatterns = [ path('api/screenshot/', views.screenshot_view, name='screenshot'), ]

Django Screenshot Utility Module

For reuse across views, extract a utility function with retry logic and proper error handling:

python
# utils/screenshot.py import time import requests from django.conf import settings API_BASE = 'https://screenshotapi.to/api/v1/screenshot' class ScreenshotError(Exception): pass def capture_screenshot( url: str, width: int = 1440, height: int = 900, image_type: str = 'png', quality: int | None = None, full_page: bool = False, color_scheme: str | None = None, wait_until: str = 'networkidle', retries: int = 2, ) -> bytes: params = { 'url': url, 'width': width, 'height': height, 'type': image_type, 'waitUntil': wait_until, } if quality is not None: params['quality'] = quality if full_page: params['fullPage'] = 'true' if color_scheme: params['colorScheme'] = color_scheme last_error = None for attempt in range(retries + 1): try: response = requests.get( API_BASE, params=params, headers={'x-api-key': settings.SCREENSHOTAPI_KEY}, timeout=30, ) response.raise_for_status() return response.content except requests.RequestException as exc: last_error = exc if attempt < retries: time.sleep(1 * (attempt + 1)) raise ScreenshotError(f'Failed after {retries + 1} attempts: {last_error}')

Usage in any view:

python
from utils.screenshot import capture_screenshot, ScreenshotError def my_view(request): try: image_bytes = capture_screenshot( url='https://example.com', width=1200, height=630, image_type='webp', quality=85, ) except ScreenshotError: return JsonResponse({'error': 'Screenshot unavailable'}, status=503) return HttpResponse(image_bytes, content_type='image/webp')

Saving Screenshots to Django Models

Store screenshots as file fields for persistent access:

python
# models.py from django.db import models class SiteScreenshot(models.Model): url = models.URLField() image = models.ImageField(upload_to='screenshots/') captured_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-captured_at']
python
# views.py from django.core.files.base import ContentFile from utils.screenshot import capture_screenshot from .models import SiteScreenshot def capture_and_store(request): url = request.POST.get('url') if not url: return JsonResponse({'error': 'url is required'}, status=400) image_bytes = capture_screenshot(url=url, image_type='webp', quality=80) screenshot = SiteScreenshot(url=url) filename = f'{url.replace("https://", "").replace("/", "_")}.webp' screenshot.image.save(filename, ContentFile(image_bytes), save=True) return JsonResponse({ 'id': screenshot.id, 'image_url': screenshot.image.url, 'captured_at': screenshot.captured_at.isoformat(), })

Background Tasks with Celery

For bulk screenshot capture or slow pages, offload the work to a Celery task:

python
# tasks.py from celery import shared_task from django.core.files.base import ContentFile from utils.screenshot import capture_screenshot from screenshots.models import SiteScreenshot @shared_task(bind=True, max_retries=3) def capture_screenshot_task(self, url: str): try: image_bytes = capture_screenshot(url=url, image_type='webp', quality=80) except Exception as exc: self.retry(exc=exc, countdown=10) return screenshot = SiteScreenshot(url=url) filename = f'{url.replace("https://", "").replace("/", "_")}.webp' screenshot.image.save(filename, ContentFile(image_bytes), save=True) return screenshot.id

Trigger it from a view:

python
from .tasks import capture_screenshot_task def queue_screenshot(request): url = request.POST.get('url') task = capture_screenshot_task.delay(url) return JsonResponse({'task_id': task.id, 'status': 'queued'})

Django REST Framework Endpoint

If you use DRF, wrap the screenshot logic in a proper API view:

python
# api/views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers, status from django.http import HttpResponse as DjangoHttpResponse from utils.screenshot import capture_screenshot, ScreenshotError class ScreenshotSerializer(serializers.Serializer): url = serializers.URLField() width = serializers.IntegerField(default=1440, min_value=320, max_value=3840) height = serializers.IntegerField(default=900, min_value=200, max_value=2160) type = serializers.ChoiceField(choices=['png', 'jpeg', 'webp'], default='png') full_page = serializers.BooleanField(default=False) class ScreenshotAPIView(APIView): def get(self, request): serializer = ScreenshotSerializer(data=request.query_params) serializer.is_valid(raise_exception=True) data = serializer.validated_data try: image = capture_screenshot( url=data['url'], width=data['width'], height=data['height'], image_type=data['type'], full_page=data['full_page'], ) except ScreenshotError: return Response( {'error': 'Screenshot capture failed'}, status=status.HTTP_502_BAD_GATEWAY, ) return DjangoHttpResponse( image, content_type=f'image/{data["type"]}', headers={'Cache-Control': 'public, max-age=3600'}, )

Production Tips

Caching with Django Cache Framework

Cache screenshot responses to avoid redundant API calls:

python
from django.core.cache import cache def get_screenshot_cached(url: str, **kwargs) -> bytes: cache_key = f'screenshot:{url}:{hash(frozenset(kwargs.items()))}' cached = cache.get(cache_key) if cached: return cached image = capture_screenshot(url=url, **kwargs) cache.set(cache_key, image, timeout=3600) return image

Input Validation

Always validate URLs before passing them to the API. Reject private IP ranges and non-HTTP schemes to prevent SSRF:

python
from urllib.parse import urlparse import ipaddress def is_valid_screenshot_url(url: str) -> bool: parsed = urlparse(url) if parsed.scheme not in ('http', 'https'): return False try: ip = ipaddress.ip_address(parsed.hostname) return ip.is_global except ValueError: return True # hostname, not IP

Deployment

ScreenshotAPI works on every Django hosting platform without additional configuration. No Chromium binary, no Node.js sidecar, no special Docker layers. Check the pricing page to choose the right credit package for your volume.

Further Reading

Frequently asked questions

Do I need Selenium or ChromeDriver to take screenshots in Django?

No. ScreenshotAPI handles browser rendering on its infrastructure. Your Django app makes a single HTTP request and receives the screenshot image. No system binaries required.

Can I store screenshots in Django's media storage?

Yes. Download the image bytes from ScreenshotAPI and save them using Django's default_storage or a FileField on your model. This works with any storage backend including S3.

How do I run screenshot capture as a background task?

Use Celery or Django-Q to offload the API call. Pass the URL and options to a task, capture the screenshot, and save the result to your database or file storage.

What Python version does the SDK support?

The ScreenshotAPI Python SDK supports Python 3.8 and above. It uses the requests library under the hood, which is compatible with virtually all Django deployments.

Related resources

Start capturing screenshots today

Create a free account and get 5 credits to try the API. No credit card required. Pay only for what you use.