ScreenshotNeo

BlogHow-to

Custom Request Headers for URL Screenshots

Pass authorization, cookies, language, and device headers to URL screenshot requests safely, with cURL, Python, Node.js, and troubleshooting.

By the ScreenshotNeo team1 October 20269 min read

Short answer: send custom headers as part of the screenshot request before the renderer opens the page. Use headers for bearer tokens, API keys, cookies, Referer, User-Agent, and Accept-Language. Confirm how your provider formats headers, whether they reach redirects and subresources, and whether the final response status is exposed.

This guide covers authenticated pages, existing sessions, localized screenshots, mobile layouts, security controls, provider syntax differences, complete cURL/Python/Node.js examples, and the failure modes that produce a login page or an error image instead of the requested content.

1. What custom request headers do

A screenshot service loads the target URL in a browser-like environment. Custom headers add request metadata to that navigation. The origin can use that metadata to authenticate the request, select a language, choose a device layout, validate a referrer, or restore a session.

Header Typical use Example value
Authorization Bearer-token or basic authentication Bearer eyJ...
X-API-Key Vendor-specific API-key authentication sk_live_...
Cookie Reuse an authenticated session or consent state session=abc123; locale=en-GB
Referer Test a flow that checks the referring origin https://app.example.com/
User-Agent Request a bot, browser, or device-specific layout Mozilla/5.0 ...
Accept-Language Request localized content de-DE,de;q=0.8,en;q=0.5

Headers are not the same as JavaScript variables. A header must be attached to the HTTP request made by the renderer. Adding window.headers in page JavaScript does not authenticate the initial navigation.

2. Choose the correct header format

There is no universal wire format. Read the provider’s API documentation before copying an example.

API style Request shape Important detail
JSON array header: [{"name":"Authorization","value":"Bearer token"}] ScreenshotCenter documents one object per header.
Repeated query parameter header=Authorization: Bearer token Screenshot API documents repeatable header parameters and a POST object form.
Semicolon-separated string Authorization: Bearer token; Accept-Language: fr Some APIs parse one string into multiple headers.
Headers object {"headers":{"Authorization":"Bearer token"}} HTML/CSS to Image accepts a headers parameter and splits entries at the first colon.

Colons inside a value, such as those in a URL or token, must remain part of the value. Use a structured JSON or POST form when the service supports it; it avoids ambiguity and keeps secrets out of the URL.

3. cURL: send headers to a screenshot endpoint

The following generic pattern uses a repeatable header parameter. Replace the endpoint and parameter names with those documented by your provider.

curl -G 'https://example-screenshot-api.test/capture' \
  --data-urlencode 'url=https://app.example.com/account' \
  --data-urlencode 'header=Authorization: Bearer YOUR_TOKEN' \
  --data-urlencode 'header=Accept-Language: en-US,en;q=0.9' \
  --data-urlencode 'header=User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/125 Safari/537.36' \
  -o account.png

For a JSON POST API, keep the headers in the request body instead:

curl -X POST 'https://example-screenshot-api.test/capture' \
  -H 'Content-Type: application/json' \
  --data @request.json \
  -o account.png
{
  "url": "https://app.example.com/account",
  "headers": {
    "Authorization": "Bearer YOUR_TOKEN",
    "Accept-Language": "en-US,en;q=0.9",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/125 Safari/537.36"
  }
}

4. Python: authenticated and localized captures

Use a request library that lets you set a timeout. Keep credentials in environment variables rather than source code or shell history.

import os
import requests

url = "https://app.example.com/account"
headers = {
    "Authorization": f"Bearer {os.environ['APP_SCREENSHOT_TOKEN']}",
    "Accept-Language": "en-US,en;q=0.9",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/125 Safari/537.36",
}

response = requests.get(
    "https://example-screenshot-api.test/capture",
    params={"url": url, "header": [f"{name}: {value}" for name, value in headers.items()]},
    timeout=90,
)
response.raise_for_status()
with open("account.png", "wb") as image:
    image.write(response.content)

If the provider expects a JSON object rather than repeated parameters:

payload = {"url": url, "headers": headers}
response = requests.post(
    "https://example-screenshot-api.test/capture",
    json=payload,
    timeout=90,
)
response.raise_for_status()
open("account.png", "wb").write(response.content)

5. Node.js: send headers without exposing them in a URL

const token = process.env.APP_SCREENSHOT_TOKEN;

const payload = {
  url: 'https://app.example.com/account',
  headers: {
    Authorization: `Bearer ${token}`,
    'Accept-Language': 'en-US,en;q=0.9',
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/125 Safari/537.36'
  }
};

const res = await fetch('https://example-screenshot-api.test/capture', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify(payload)
});

if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await require('node:fs').promises.writeFile('account.png', image);

6. Cookies, login pages, and protected content

For a session-based application, send the complete cookie string expected by the origin:

Cookie: session=SESSION_VALUE; csrf=CSRF_VALUE; locale=en-US

Copy only the cookies needed for the target host. A cookie from a different domain or path may be ignored. Session cookies can expire while a capture job is queued, so use a short queue delay and refresh credentials when the application requires it.

Token authentication and browser sessions are different. A bearer token may authenticate the first document request while the application then calls an API that needs a separate cookie or CSRF token. If the resulting image is a login page, inspect the page status and the browser’s network requirements rather than repeatedly changing the screenshot dimensions.

7. Header scope: navigation, redirects, and subresources

Ask whether the service sends your headers only to the initial target request or to every HTTP/HTTPS transaction. Browshot documents propagation to all HTTP/HTTPS transactions, while Screenshot API documents headers sent only to the target host. This distinction affects pages that load protected CSS, images, fonts, or API data from another origin.

  • Initial request only: enough when the HTML is self-contained or the page sets a session cookie immediately.
  • Target host: safer for secrets and usually sufficient for same-origin assets.
  • All transactions: can load protected subresources, but increases the risk of leaking an Authorization header to an unintended host.

Redirects deserve special attention. A redirect from https://app.example.com to an identity provider may drop, transform, or reject credentials. Restrict capture to an approved host when the service supports that control, and never assume a secret is safe across redirects.

8. Verify what was actually captured

A successful HTTP response from the screenshot service does not prove that the requested page rendered. The image may show a 401 page, a 403 page, a consent wall, or an application error.

  1. Check the provider’s final document status metadata. Screenshot API exposes this as X-Page-Status.
  2. Inspect whether the response says the page was billed, cached, blocked, or failed when those headers are available.
  3. Open the image and look for a login form, access-denied message, missing fonts, or an empty application shell.
  4. Reproduce the same URL and headers with a normal browser or an HTTP client to confirm the credentials themselves work.

9. Security checklist

  • Store tokens and cookies in a secret manager or environment variable.
  • Prefer short-lived, least-privilege credentials created specifically for capture.
  • Use POST or encrypted secret storage instead of putting credentials in query strings.
  • Redact authorization values from application logs, tracing, CI output, and error reports.
  • Check whether the provider logs request parameters or stores rendered images.
  • Restrict headers to the target host when that option exists.
  • Do not place passwords, one-time codes, or private customer data in a screenshot that will be shared publicly.
  • Confirm that automated capture is allowed and that your account is authorized to access the protected page.

10. Reliability and performance

Headers rarely dominate capture time. Browser startup, DNS, TLS, JavaScript execution, fonts, images, and network-idle waits usually matter more. Keep the request deterministic so retries produce comparable images.

Concern Practical approach
Timeouts Set a client timeout longer than the provider’s render window and handle timeout responses explicitly.
Expired sessions Generate or refresh cookies close to capture time; avoid long-lived browser exports.
Rate limits Use bounded concurrency, exponential backoff for transient failures, and an idempotent job key where supported.
Dynamic pages Wait for a selector or network idle when the authenticated content is rendered after navigation.
Large assets Block unnecessary ads, trackers, or resource types if your provider supports it, but do not block required API calls or fonts.
Consistency Pin User-Agent, Accept-Language, timezone, viewport, and cookies for visual comparisons.

11. Common errors and fixes

Symptom Likely cause Fix
Image shows a 401 page Missing, expired, or malformed Authorization header Confirm the exact scheme, token lifetime, and header syntax; inspect final status metadata.
Image shows a 403 page Insufficient permission, IP policy, bot protection, or wrong Referer Test the same credentials directly, add the required Referer, and verify the renderer’s network is allowed.
Header appears ignored Wrong parameter name or provider expects JSON instead of a string Use the documented format and verify the outgoing request with a safe, redacted trace.
Login works in a browser but not in capture Authentication depends on multiple cookies, JavaScript, or a CSRF flow Supply the required cookie set and wait for the post-login selector; a static bearer header may be insufficient.
Localized text is wrong Accept-Language was omitted or the app uses a profile cookie Send Accept-Language and the locale cookie, then keep timezone and region settings consistent.
Mobile layout does not appear User-Agent alone does not change viewport or device metrics Configure the provider’s viewport/device option as well as User-Agent when available.
Protected images or CSS are missing Headers reach only the initial navigation Choose a provider that propagates headers to required subresources, or use a page flow that exchanges the header for a session cookie.
Secrets appear in logs Credentials were placed in a query string or verbose debug output Move secrets to POST bodies or environment variables and redact request logging.
Capture is blank JavaScript has not rendered, a resource was blocked, or the page timed out Increase the render wait, wait for a selector, allow required requests, and inspect the final status.

12. Or skip the browser setup

ScreenshotNeo is a website screenshot API with custom headers, cookies, user-agent, authorization, timezone, and geolocation options. It accepts a URL and returns PNG, JPEG, WebP, or PDF; see the ScreenshotNeo API documentation for the header option names and full configuration.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Before capture, ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers identify the page verdict and whether it was billed. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.

13. Cost planning

Header support itself is rarely the main cost driver. Billing depends on the screenshot service’s plan, image or PDF volume, asynchronous jobs, and cache policy. Cache deterministic captures when the page and headers are unchanged, but never cache a response containing private data in a shared location. For batch jobs, record the URL, a redacted header fingerprint, final status, verdict, and billing result so retries do not create unexplained usage.

14. FAQ

Can I send a password in a custom header?

Only if the target application explicitly defines such a header. Prefer the application’s documented token or session mechanism, and never put a raw password in a URL.

Should I use cookies or Authorization?

Use the mechanism the origin expects. Cookies are appropriate for an existing browser session; Authorization is appropriate for token-protected endpoints. Some applications require both.

Why does a screenshot service return an image when authentication failed?

Many services capture whatever document the browser finally rendered, including a 401, 403, or login page. Check final status metadata and inspect the image.

Do custom headers change the screenshot’s visual size?

No. Headers influence the response and application state. Viewport, device preset, scale, and page dimensions control the image geometry.

Can headers authenticate third-party assets?

Only when the provider propagates them to those requests and the target host accepts them. Verify scope before sending credentials broadly.