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.
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
- Sign up for ScreenshotAPI and copy your API key. You receive 5 free credits on signup.
- Install the Python SDK.
- Add your API key to Django settings.
- Create a view that captures screenshots.
Installation
Install the SDK with pip:
bashpip install screenshotapi
Add the API key to your Django settings:
python# settings.py SCREENSHOTAPI_KEY = env('SCREENSHOTAPI_KEY')
And in your .env file:
bashSCREENSHOTAPI_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:
pythonfrom 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:
pythonfrom .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:
pythonfrom 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:
pythonfrom 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
- How to Take Screenshots with Python covers the full range of Python screenshot techniques.
- The Python SDK reference documents all available parameters and response formats.
- See the Flask integration for a lighter-weight Python alternative.
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.