ScreenshotNeo

BlogHow-to

How to Use a Screenshot API with a Django Site Hosted in India

Call a screenshot API safely from Django, return or store the result, and understand what India hosting does—and does not—tell you about data location.

By the ScreenshotNeo team4 October 202610 min read

Direct answer: call the screenshot provider from Django’s server side. Keep its API key in deployment secrets, send the target URL and capture settings to the provider’s documented endpoint, then handle the response as image bytes, JSON, a URL, or an asynchronous job according to that provider’s API. Hosting Django in India does not establish where the provider renders or stores screenshots.

Provider APIs differ in endpoint, authentication, request parameters, response format, errors, and retention. Use the selected provider’s current documentation as the contract. The examples below show a complete server-side integration pattern and a runnable Django endpoint; adapt the provider-specific request and response handling to match its documentation.

1. Decide what the Django endpoint should return

Before writing the view, decide how your application will use the screenshot:

  • Return image bytes: useful when a user requests a capture and your endpoint should respond with an image.
  • Return a provider URL: useful when the provider documents a hosted image URL and your app can safely expose it.
  • Queue a job: preferable for slow or asynchronous captures. Return a job reference to the client and let a worker process the result.

Check the provider’s documentation for its authentication method, request format, output modes, limits, timeout guidance, caching, and file retention. A successful API response can contain a screenshot of a sign-in page, error page, or other unexpected target state; inspect target-page status when the provider exposes it.

2. Keep credentials on the Django server

Configure the API key in the hosting platform’s secret or environment settings. Do not put a production key in a template, browser JavaScript, a public repository, or a public image URL. Some providers accept keys in query parameters; use a documented authorization header where supported. Query-string credentials can appear in logs or other places, so follow the provider’s security guidance.

# Example deployment environment
SCREENSHOT_API_KEY=replace-with-your-secret

Read the secret into Django configuration:

# settings.py
import os

SCREENSHOT_API_KEY = os.environ["SCREENSHOT_API_KEY"]

In production, configure the environment variable through your host’s secret-management settings rather than committing a populated environment file.

3. Install the HTTP client

This example uses Python’s requests package:

python -m pip install requests

4. Add a server-side Django view

The following runnable pattern validates the target URL, calls a provider endpoint from the server, applies a timeout, and returns an image response. Replace the placeholder endpoint, authentication header, request body, and response checks with the exact contract from your chosen provider. The placeholder values are not a real provider API.

# views.py
from urllib.parse import urlparse

import requests
from django.conf import settings
from django.http import HttpResponse, JsonResponse
from django.views.decorators.http import require_POST

# Replace this with the endpoint in your provider's current documentation.
SCREENSHOT_ENDPOINT = "https://provider.example/api/screenshot"
ALLOWED_HOSTS = {"example.com", "www.example.com"}


def validate_target_url(value):
    parsed = urlparse(value)
    if parsed.scheme != "https" or not parsed.hostname:
        raise ValueError("Use a valid HTTPS target URL.")
    if parsed.hostname.lower() not in ALLOWED_HOSTS:
        raise ValueError("This target host is not allowed.")
    return value


@require_POST
def capture(request):
    target_url = request.POST.get("url", "")
    try:
        target_url = validate_target_url(target_url)
    except ValueError as exc:
        return JsonResponse({"error": str(exc)}, status=400)

    headers = {
        # Change the header format to the provider's documented auth scheme.
        "Authorization": f"Bearer {settings.SCREENSHOT_API_KEY}",
        "Accept": "image/png",
    }
    payload = {
        "url": target_url,
        "format": "png",
        "viewport_width": 1440,
        "viewport_height": 900,
        "full_page": True,
    }

    try:
        upstream = requests.post(
            SCREENSHOT_ENDPOINT,
            json=payload,
            headers=headers,
            timeout=(5, 90),
        )
    except requests.Timeout:
        return JsonResponse({"error": "Screenshot provider timed out."}, status=504)
    except requests.RequestException:
        return JsonResponse({"error": "Could not reach screenshot provider."}, status=502)

    if not upstream.ok:
        # Avoid forwarding provider response text if it might include secrets.
        return JsonResponse(
            {"error": "Screenshot provider returned an error.",
             "provider_status": upstream.status_code},
            status=502,
        )

    content_type = upstream.headers.get("Content-Type", "").split(";", 1)[0].lower()
    if content_type not in {"image/png", "image/jpeg", "image/webp"}:
        # JSON, redirects, and async jobs need their provider-specific handling.
        return JsonResponse({"error": "Unexpected provider response format."}, status=502)

    response = HttpResponse(upstream.content, content_type=content_type)
    response["Content-Disposition"] = 'inline; filename="screenshot"'
    return response

Wire it into your URL configuration:

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

urlpatterns = [
    path("api/capture/", capture, name="capture"),
]

The allowlist is an application policy example. For user-submitted URLs, validate destinations carefully and avoid allowing arbitrary internal or private-network targets. Apply appropriate authentication, authorization, rate limits, and CSRF protections for your application.

5. Match request and response handling to the provider

Do not assume that one provider’s integration pattern works unchanged with another. Confirm each item in its current API reference:

Contract detail What to verify
Endpoint and method GET versus POST, endpoint version, and whether options belong in query parameters or JSON.
Authentication Bearer token, API-key header, or another documented method. Prefer a header when supported.
Request options Parameter names, types, defaults, supported formats, and size or duration limits.
Response Raw image bytes, JSON with base64 or a URL, redirect, or job ID.
Target status and errors How to distinguish a rendered error page from a failed capture request.
Retention and access How long files remain available, whether URLs are public, and how deletion works.

For example, the reviewed references document different contracts: [Screenshot API](https://screenshot-api.org/docs/) describes GET and POST capture endpoints and a CDN URL or redirect workflow; [Screenshot-api.net](https://screenshot-api.net/docs) documents bearer authentication and a raw-image response; and [ScreenshotAPI](https://screenshotapi.readme.io/reference/take-a-screenshot-post) describes alternate response modes. Those details are provider-specific, not shared defaults.

6. Choose rendering options deliberately

Use only options supported by your selected provider. Common controls and their effects include:

  • Viewport width and height: set the browser dimensions to match the layout you need to inspect.
  • Full-page capture: captures beyond the initial viewport when supported. Long pages may take more time or produce larger files.
  • Output format: PNG, JPEG, or WebP can affect file size, transparency, and image quality. Confirm supported formats and defaults.
  • Wait condition or delay: allow client-rendered content to appear. A fixed delay can waste time or still miss slow content; selector or load-condition waits may be more appropriate if available.
  • Selector capture or selector wait: target a specific element or wait for it to appear when the provider supports those controls.
  • Cookies and headers: use only when needed for pages requiring a session or custom request context. Treat these values as sensitive.
  • Cache controls: understand whether a cached response can be returned and how the provider defines its cache lifetime.

The reviewed documentation describes these kinds of controls across providers, but names and behavior vary. See [Screenshot-api.net’s reference](https://screenshot-api.net/docs) for its documented dimensions, full-page option, format, delay, cookies, headers, and timeout, and the [ScreenshotAPI POST reference](https://screenshotapi.readme.io/reference/take-a-screenshot-post) for its documented response modes and capture controls.

7. Handle JSON, URL, and asynchronous responses

The image-returning example above intentionally rejects non-image responses. If the provider returns JSON, parse its documented fields and choose the right flow:

  • Base64 image: decode only the documented field, validate the resulting media type and size, then return or store the bytes.
  • Hosted image URL: decide whether to store the URL, proxy the content, or return it. Confirm access controls, retention, and whether the URL is safe to expose.
  • Job ID: persist the job reference and process it using the documented polling or webhook mechanism. Do not hold a normal Django request open indefinitely while waiting.

[Domain Snapshot API’s documentation](https://domainscan.in/docs/domain-snapshot) describes both synchronous and asynchronous capture and machine-readable error codes for that provider. It also states a median 4–8 second synchronous time for its endpoint; that is a vendor claim, not an independent benchmark or a general expectation for screenshot APIs.

8. Return, store, or serve the screenshot

For a direct image response, set the correct content type and avoid trusting an arbitrary upstream header. For stored screenshots, use storage with access controls appropriate to the content and your application. If exposing a provider-hosted URL, confirm whether it is public, signed, or temporary and how long it remains valid. Retention and deletion rules vary by provider and plan.

One [ScreenshotAPI.net getting-started page](https://websitescreenshotapi.net/docs/) shows a 24-hour retention example for its service. Treat that as provider-specific information and verify current terms; it does not establish retention for other providers.

9. India hosting, processing location, and data residency

Running Django on an India-hosted server says where that server is hosted. It does not, by itself, establish where a third-party browser renders the page, where the image is stored, or which region serves a returned CDN URL. The reviewed API references do not verify India-region rendering or storage for the services they describe.

If location matters for your application, ask the provider to confirm rendering region, storage region, subprocessors, retention, and any contractual commitments. Check the provider’s current terms and your organization’s applicable requirements; do not infer a compliance conclusion from your Django server’s location alone.

10. Security and reliability checklist

  • Store the API key in deployment secrets and rotate it using your provider’s process.
  • Validate the target URL, permitted schemes, and allowed destinations for your use case.
  • Set connection and read timeouts. Return a controlled error when the provider is unavailable.
  • Do not expose credentials in browser code, logs, templates, or public URLs.
  • Limit access to your capture endpoint and consider rate limits and quotas to prevent unexpected use.
  • For long-running captures, use a background worker or provider-supported asynchronous job flow.
  • Record request IDs and safe diagnostic metadata. Avoid logging API keys, cookies, authorization headers, or sensitive page content.
  • Check the target page status when available; a valid image may depict a page-level error or sign-in screen.
  • Confirm output URL access, retention, cache behavior, and deletion expectations.

11. Performance, reliability, and cost

Screenshot capture requires a browser to load and render the target page, so response time depends on the target, selected options, provider, and whether the request is synchronous or asynchronous. Full-page images and extra waiting can increase work. Set timeouts based on the provider’s documented behavior and your user-facing latency budget; move work out of the request path when captures can take too long.

Provider pricing and quotas are not interchangeable. Before launch, check what counts toward usage, how retries are charged, whether cache hits are billed, the maximum capture size or duration, and what happens at quota limits. Avoid automatic retries for non-retryable errors or without considering duplicate charges. The references reviewed do not establish a general cost, latency, retention period, or India-region capability across providers.

12. Troubleshooting

Symptom Likely cause What to check or fix
401 or 403 from provider Missing, invalid, expired, or incorrectly formatted credential; account or permission issue. Check the provider’s required auth method and header format, secret configuration, and account access. Do not paste the key into client code.
400 response Wrong parameter names, unsupported options, malformed URL, or request sent in the wrong format. Compare method, casing, JSON versus query parameters, and option values with the current provider reference.
Django returns 502 Provider returned an error or an unexpected response format. Log a safe request identifier and upstream status; check whether the provider returns JSON, a redirect, or a job ID instead of image bytes.
Django returns 504 or times out Network delay, slow target page, or capture duration beyond the configured timeout. Review provider timeout guidance, use appropriate connect/read timeouts, and move longer work to an asynchronous flow.
Image is blank or incomplete Target content may be delayed, blocked, client-rendered, or outside the captured viewport. Check page status, wait conditions, viewport, full-page support, and whether the target requires cookies or headers.
Screenshot shows a sign-in or error page The browser rendered the page successfully, but the target site served that state. Check target-page status and authentication requirements. A successful screenshot response does not prove the target page returned the intended content.
Screenshot URL stops working Provider-specific retention, expiration, or access control. Check current retention and URL access terms; copy the image into storage you control if your use case requires longer retention.
Capture works locally but fails after deployment Missing secret, outbound network restriction, different timeout, or deployment configuration mismatch. Check the host’s secret settings and outbound connectivity, and inspect sanitized error metadata.

13. Or skip the browser setup

ScreenshotNeo provides a screenshot API and MCP server for developers. A single GET request can return PNG, JPEG, WebP, or PDF. Its [API documentation](https://screenshotneo.com/docs/) covers the endpoint and options. This Python example saves a screenshot response:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response reports page verdict and billing headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is available on every plan.

Sign up for 1,000 free screenshots a month, with no card required.

14. FAQ

Can Django return a screenshot directly to a browser?

Yes. If the provider returns image bytes, return them from a Django response with the matching image content type. If it returns a URL or job reference, handle that documented response instead.

Should a screenshot request run inside a normal Django view?

It can for bounded, quick captures. For slow or asynchronous work, queue a task or use the provider’s job workflow so the web request does not wait indefinitely.

Does an India-hosted Django app mean screenshots stay in India?

No. Confirm rendering, storage, delivery regions, and retention directly with the provider when those details matter.

Can I use any provider’s API key as a Bearer token?

No. Authentication formats differ. Use the method documented by the provider you selected.

What should I do if an image is valid but shows the wrong page?

Check the target page’s status and browser state, including authentication, waits, cookies, viewport, and capture options.