DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

Django Screenshot API: Capture Web Pages from Your Python Backend

Django Screenshot API: Capture Web Pages from Your Python Backend

You're building a Django application. At some point, you need to capture a screenshot or generate a PDF:

  • Your users want to preview their work before publishing
  • You need to generate report PDFs from HTML templates
  • You're building a monitoring tool that needs page snapshots
  • You need social preview images for shared content
  • You're capturing pages for analysis or archival

You google "Django screenshot" and find options:

  1. Selenium — heavyweight, slow, requires browser binary
  2. Puppeteer (via PyPuppeteer) — possible but adds Node.js dependency to Python project
  3. wkhtmltopdf — limited CSS support, requires system binary
  4. Build it yourself — maintenance nightmare

There's a simpler way: a screenshot API.

The Django Problem: Screenshots Without Extra Dependencies

Django is excellent at building web applications, but screenshot/PDF generation requires external tools. Here's why the common options fall short:

Option 1: Selenium (Heavyweight)

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

def capture_screenshot(url):
    # Setup headless Chrome
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')
    options.add_argument('--disable-gpu')

    driver = webdriver.Chrome(options=options)
    driver.get(url)

    # Wait for content to load
    WebDriverWait(driver, 10).until(
        EC.presence_of_all_elements_located((By.TAG_NAME, "body"))
    )

    time.sleep(2)  # Additional wait for JS rendering
    screenshot = driver.save_screenshot('screenshot.png')
    driver.quit()
    return screenshot

# Problems:
# - Slow initialization (~2-5 seconds per screenshot)
# - Requires Chrome binary + ChromeDriver
# - Memory-intensive
# - Hard to scale
# - Threading/async complications
Enter fullscreen mode Exit fullscreen mode

Option 2: PyPuppeteer (Node.js in Python)

import asyncio
from pyppeteer import launch

async def capture_screenshot(url):
    browser = await launch()
    page = await browser.newPage()
    await page.goto(url)
    await page.screenshot({'path': 'screenshot.png'})
    await browser.close()

asyncio.run(capture_screenshot('https://example.com'))

# Problems:
# - Requires Node.js installed
# - Async-only (adds complexity to sync Django code)
# - Still memory-intensive
# - Dependency management nightmare
Enter fullscreen mode Exit fullscreen mode

Option 3: PageBolt API (Simple HTTP)

import requests

def capture_screenshot(url):
    response = requests.post('https://api.pagebolt.dev/take_screenshot', 
        headers={'Authorization': f'Bearer {PAGEBOLT_KEY}'},
        json={'url': url}
    )
    return response.json()['imageUrl']

# Benefits:
# - One HTTP request
# - No extra dependencies (just requests)
# - Fast (~1-2 seconds)
# - Works in any Django view, task, or background job
# - Scales instantly
Enter fullscreen mode Exit fullscreen mode

Django View Examples

Example 1: Screenshot on Demand

Create a view that returns a screenshot as an image:

# views.py
from django.http import HttpResponse
from django.views import View
import requests
import os

PAGEBOLT_KEY = os.getenv('PAGEBOLT_API_KEY')

class CaptureScreenshotView(View):
    def post(self, request):
        url = request.POST.get('url')

        if not url:
            return HttpResponse('URL required', status=400)

        # Call PageBolt API
        response = requests.post('https://api.pagebolt.dev/take_screenshot',
            headers={
                'Authorization': f'Bearer {PAGEBOLT_KEY}',
                'Content-Type': 'application/json'
            },
            json={
                'url': url,
                'width': 1280,
                'height': 720
            }
        )

        if response.status_code != 200:
            return HttpResponse('Screenshot failed', status=500)

        image_url = response.json()['imageUrl']

        # Download the image and return it
        image_response = requests.get(image_url)
        return HttpResponse(image_response.content, content_type='image/png')

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('api/screenshot/', views.CaptureScreenshotView.as_view(), name='screenshot'),
]
Enter fullscreen mode Exit fullscreen mode

Usage:

curl -X POST http://localhost:8000/api/screenshot/ \
  -d "url=https://example.com" \
  -o screenshot.png
Enter fullscreen mode Exit fullscreen mode

Example 2: Generate PDF from Template

Capture a rendered Django template as a PDF:

# views.py
from django.http import HttpResponse
from django.template.loader import render_to_string
import requests
import os

PAGEBOLT_KEY = os.getenv('PAGEBOLT_API_KEY')

class GenerateInvoicePDFView(View):
    def get(self, request, invoice_id):
        from .models import Invoice

        invoice = Invoice.objects.get(id=invoice_id)

        # Render Django template to HTML
        html_content = render_to_string('invoices/template.html', {
            'invoice': invoice,
            'request': request,
        })

        # Send HTML to PageBolt
        response = requests.post('https://api.pagebolt.dev/generate_pdf',
            headers={
                'Authorization': f'Bearer {PAGEBOLT_KEY}',
                'Content-Type': 'application/json'
            },
            json={
                'html': html_content,
                'format': 'A4',
                'margin': '10mm'
            }
        )

        if response.status_code != 200:
            return HttpResponse('PDF generation failed', status=500)

        pdf_url = response.json()['pdfUrl']

        # Download and return PDF
        pdf_response = requests.get(pdf_url)
        return HttpResponse(
            pdf_response.content,
            content_type='application/pdf',
            headers={'Content-Disposition': 'attachment; filename="invoice.pdf"'}
        )
Enter fullscreen mode Exit fullscreen mode

Example 3: Async Celery Task

Capture screenshots asynchronously for long-running jobs:

# tasks.py
from celery import shared_task
import requests
import os

PAGEBOLT_KEY = os.getenv('PAGEBOLT_API_KEY')

@shared_task
def capture_page_screenshot(url, callback_url=None):
    """Capture a screenshot and optionally POST results to a callback."""

    response = requests.post('https://api.pagebolt.dev/take_screenshot',
        headers={
            'Authorization': f'Bearer {PAGEBOLT_KEY}',
            'Content-Type': 'application/json'
        },
        json={'url': url, 'width': 1280, 'height': 720}
    )

    if response.status_code != 200:
        return {'error': 'Screenshot failed'}

    image_url = response.json()['imageUrl']

    # Store result
    from .models import CapturedScreenshot
    screenshot = CapturedScreenshot.objects.create(
        original_url=url,
        image_url=image_url
    )

    # Notify callback if provided
    if callback_url:
        requests.post(callback_url, json={'screenshot_id': screenshot.id, 'image_url': image_url})

    return {'screenshot_id': screenshot.id, 'image_url': image_url}

# views.py
from django.http import JsonResponse
from .tasks import capture_page_screenshot

class RequestScreenshotView(View):
    def post(self, request):
        url = request.POST.get('url')
        callback_url = request.POST.get('callback_url')

        # Queue async task
        task = capture_page_screenshot.delay(url, callback_url)

        return JsonResponse({
            'task_id': task.id,
            'message': 'Screenshot capture queued'
        })
Enter fullscreen mode Exit fullscreen mode

Creating a Reusable Utility

Create a helper module for consistent API calls:

# pagebolt_utils.py
import requests
import os
from typing import Optional

class PageBoltClient:
    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.getenv('PAGEBOLT_API_KEY')
        self.base_url = 'https://api.pagebolt.dev'

    def _request(self, endpoint: str, payload: dict) -> dict:
        """Make a request to PageBolt API."""
        response = requests.post(
            f'{self.base_url}/{endpoint}',
            headers={
                'Authorization': f'Bearer {self.api_key}',
                'Content-Type': 'application/json'
            },
            json=payload
        )

        if response.status_code != 200:
            raise Exception(f'PageBolt API error: {response.text}')

        return response.json()

    def screenshot(self, url: str, **options) -> str:
        """Take a screenshot of a URL. Returns image URL."""
        payload = {'url': url, **options}
        result = self._request('take_screenshot', payload)
        return result['imageUrl']

    def screenshot_html(self, html: str, **options) -> str:
        """Take a screenshot of HTML. Returns image URL."""
        payload = {'html': html, **options}
        result = self._request('take_screenshot', payload)
        return result['imageUrl']

    def pdf(self, url: str, **options) -> str:
        """Generate a PDF from a URL. Returns PDF URL."""
        payload = {'url': url, **options}
        result = self._request('generate_pdf', payload)
        return result['pdfUrl']

    def pdf_html(self, html: str, **options) -> str:
        """Generate a PDF from HTML. Returns PDF URL."""
        payload = {'html': html, **options}
        result = self._request('generate_pdf', payload)
        return result['pdfUrl']

# Usage in views:
from .pagebolt_utils import PageBoltClient

client = PageBoltClient()
image_url = client.screenshot('https://example.com')
pdf_url = client.pdf_html(html_content, format='A4')
Enter fullscreen mode Exit fullscreen mode

Configuration

In your settings.py:

# settings.py
import os

# PageBolt API Key
PAGEBOLT_API_KEY = os.getenv('PAGEBOLT_API_KEY', '')
Enter fullscreen mode Exit fullscreen mode

In your .env:

PAGEBOLT_API_KEY=your_api_key_here
Enter fullscreen mode Exit fullscreen mode

Why PageBolt for Django

Aspect Selenium PyPuppeteer wkhtmltopdf PageBolt API
Setup Browser binary + WebDriver Node.js + npm System binary API key
Integration Complex, requires threading Async-only Simple but limited Simple HTTP
Scaling Hard (more servers = more browsers) Hard (memory) Medium Automatic
CSS Support ✅ Full ✅ Full ⚠️ Limited ✅ Full
Speed Slow (~2-5s) Slow (~2-3s) Medium (~1s) Fast (~1-2s)
Dependencies Heavy (browser binary) Heavy (Node.js) Medium (system lib) None (HTTP)
Celery-friendly ⚠️ Tricky ⚠️ Async only ✅ Yes ✅ Yes

Getting Started

1. Get API key (free tier: 100 requests/month)

# Visit pagebolt.dev, create account, copy key
Enter fullscreen mode Exit fullscreen mode

2. Install requests (probably already installed)

pip install requests
Enter fullscreen mode Exit fullscreen mode

3. Set environment variable

export PAGEBOLT_API_KEY=your_key_here
Enter fullscreen mode Exit fullscreen mode

4. Use in your Django app

# views.py
import requests
import os

PAGEBOLT_KEY = os.getenv('PAGEBOLT_API_KEY')

def my_view(request):
    response = requests.post('https://api.pagebolt.dev/take_screenshot',
        headers={'Authorization': f'Bearer {PAGEBOLT_KEY}'},
        json={'url': 'https://example.com'}
    )
    return HttpResponse(response.json()['imageUrl'])
Enter fullscreen mode Exit fullscreen mode

Next Steps

  • Try PageBolt free — 100 requests/month, no credit card.
  • Pick your use case — document previews, PDF generation, screenshots. Start with one.
  • Copy the utility class above — drop it into your project and reuse everywhere.
  • Scale with confidence — API handles the infrastructure, you handle the features.

Stop fighting Selenium and Chrome binaries. Start capturing screenshots in Django.


PageBolt: Screenshots and PDFs for Django, effortlessly. Get started free →

Top comments (0)