How to Pass Cookies to a Screenshot API for a Logged-In Page
Pass a valid, correctly scoped session cookie in your screenshot API’s format, wait for the protected content to render, then verify the result is the signed-in page.
To screenshot a page that requires login, send the screenshot service a valid session cookie in the format its API accepts, scoped to the protected site’s domain and path where supported. Then wait for the page’s authenticated content to render and check that the image shows that content rather than a login or error page. Cookie formats differ by provider, so use the provider’s current API reference.
Use this only for pages and accounts you are authorized to access. Session cookies are credentials: send only the values needed for the page and handle them according to your own credential-management standards.
1. Identify the authentication method
First confirm how the destination authenticates the page:
- Session cookie: use the cookie issued after a user signs in. This is the common case for a web dashboard.
- Authorization header: use a bearer token or another supported header if the application authenticates requests that way.
- HTTP Basic Authentication: use the screenshot provider’s dedicated username/password option if it supports one.
A cookie cannot substitute for a bearer token or Basic Authentication unless the target application accepts it. Cloudflare Browser Rendering documents both cookies and an authenticate object for Basic Authentication; ScreenshotOne documents cookies, custom headers, and Basic Authentication alternatives. See the Cloudflare screenshot API reference and ScreenshotOne’s authenticated pages guide.
2. Get the right cookie and scope
- Sign in through the application’s normal flow using an account you are permitted to use.
- Find the session cookie in your browser’s storage or through the application’s authorized login flow.
- Copy only the cookie or cookies the protected page needs. Preserve the exact name and value.
- Check the cookie’s domain and path. The domain must match the target host according to the provider’s cookie rules, and the path must cover the requested page. Include attributes such as
Secure,HttpOnly,SameSite, or expiration when the provider accepts them and they are relevant. - Check whether the cookie is still valid. Many session cookies expire, rotate, or are revoked after sign-out or a security event.
Do not assume that a browser’s cookie export can be pasted unchanged into every screenshot API. The API may expect a structured array, one formatted string per cookie, or a semicolon-separated header-like value.
3. Cloudflare Browser Rendering example
Cloudflare’s screenshot endpoint accepts a JSON request with a URL and a cookies array. Its documented example uses cookie objects with name, value, domain, and path. The API reference also lists optional cookie properties including expiration, httpOnly, sameSite, and secure. Replace all placeholders with values from an authorized session. The value shown here is deliberately a placeholder and will not authenticate.
curl -X POST "https://api.cloudflare.com/client/v4/accounts/<ACCOUNT_ID>/browser-rendering/screenshot" \
-H "Authorization: Bearer <CLOUDFLARE_API_TOKEN>" \
-H "Content-Type: application/json" \
--data '{
"url": "https://example.com/protected-page",
"cookies": [
{
"name": "session_id",
"value": "REPLACE_WITH_AUTHORIZED_SESSION_VALUE",
"domain": "example.com",
"path": "/",
"secure": true,
"httpOnly": true
}
],
"gotoOptions": {
"waitUntil": "networkidle2"
},
"waitForSelector": {
"selector": "[data-page=\"account-dashboard\"]",
"visible": true,
"timeout": 30000
}
}' \
--output authenticated-page.png
Use the actual account ID and API token from your Cloudflare account. Choose a selector that exists only after the signed-in view is ready, if possible. The endpoint and supported options are documented in the Cloudflare screenshot API reference; see also its guidance on waiting for JavaScript-heavy pages.
Python request
This example uses Python’s requests package to send the same JSON request and save the response body. Install the dependency with python -m pip install requests. Check the HTTP status before treating the output as a valid image; API errors can also return a response body.
import os
import requests
account_id = os.environ["CLOUDFLARE_ACCOUNT_ID"]
api_token = os.environ["CLOUDFLARE_API_TOKEN"]
payload = {
"url": "https://example.com/protected-page",
"cookies": [
{
"name": "session_id",
"value": os.environ["TARGET_SESSION_COOKIE"],
"domain": "example.com",
"path": "/",
"secure": True,
"httpOnly": True,
}
],
"gotoOptions": {"waitUntil": "networkidle2"},
"waitForSelector": {
"selector": '[data-page="account-dashboard"]',
"visible": True,
"timeout": 30000,
},
}
endpoint = (
"https://api.cloudflare.com/client/v4/accounts/"
f"{account_id}/browser-rendering/screenshot"
)
response = requests.post(
endpoint,
headers={
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json",
},
json=payload,
timeout=90,
)
response.raise_for_status()
content_type = response.headers.get("content-type", "")
if "image/" not in content_type:
raise RuntimeError(f"Expected an image, got {content_type}: {response.text[:500]}")
with open("authenticated-page.png", "wb") as image_file:
image_file.write(response.content)
Set CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN, and TARGET_SESSION_COOKIE in your execution environment. Keep actual credentials out of source control and logs.
Node.js request
This uses the built-in fetch available in current Node.js releases. It writes the response as an image only after checking the response status and content type.
import { writeFile } from "node:fs/promises";
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
const apiToken = process.env.CLOUDFLARE_API_TOKEN;
const sessionCookie = process.env.TARGET_SESSION_COOKIE;
if (!accountId || !apiToken || !sessionCookie) {
throw new Error("Set CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN, and TARGET_SESSION_COOKIE");
}
const endpoint = `https://api.cloudflare.com/client/v4/accounts/${accountId}/browser-rendering/screenshot`;
const response = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com/protected-page",
cookies: [{
name: "session_id",
value: sessionCookie,
domain: "example.com",
path: "/",
secure: true,
httpOnly: true,
}],
gotoOptions: { waitUntil: "networkidle2" },
waitForSelector: {
selector: '[data-page="account-dashboard"]',
visible: true,
timeout: 30000,
},
}),
});
if (!response.ok) {
throw new Error(`Screenshot API returned ${response.status}: ${(await response.text()).slice(0, 500)}`);
}
const contentType = response.headers.get("content-type") ?? "";
if (!contentType.startsWith("image/")) {
throw new Error(`Expected an image, got ${contentType}`);
}
await writeFile("authenticated-page.png", Buffer.from(await response.arrayBuffer()));
4. Adapt the cookie format to the provider
The shape above is specific to Cloudflare. For example, ScreenshotOne documents its cookies option as a formatted string containing the name/value and attributes such as domain, path, HttpOnly, Secure, and SameSite. Its query-string encoding rules matter because cookie attributes contain separators. Another provider may accept semicolon-separated name=value pairs and scope them to the target host. Follow that provider’s current documentation; do not copy a cookie payload from one service into another unchanged.
| Provider format described in its docs | What to send | Important check |
|---|---|---|
| Cloudflare Browser Rendering | JSON array of cookie objects | Use the documented property names and cookie scope. |
| ScreenshotOne | Formatted cookie string(s) in its cookies option |
URL-encode the query parameter correctly and retain relevant attributes. |
| Other screenshot APIs | Provider-specific; sometimes semicolon-separated pairs | Verify host scope, redirect behavior, and final-page status in that vendor’s docs. |
See the official ScreenshotOne options reference for its cookie and header formats. Those rules are ScreenshotOne-specific.
5. Wait for the authenticated view and verify it
Authentication and rendering are separate problems. A valid cookie can sign the browser in, while the screenshot still captures too early for a client-rendered dashboard. Cloudflare notes that default page-load behavior can return empty or incomplete results for JavaScript-heavy pages and single-page applications.
- Use a provider-supported navigation wait such as
networkidle0ornetworkidle2when the page loads data asynchronously. - Prefer waiting for a stable selector that identifies the authenticated view. This can finish sooner and more reliably than waiting for every network request to stop.
- Use a bounded timeout. A page with analytics, polling, or a persistent connection may never become fully idle.
- Inspect the returned image. If the provider returns final document status or page metadata, check it too; a successful screenshot request does not prove that the page is authenticated.
Cloudflare documents waitForSelector and navigation wait choices in its screenshot API reference and explains the issue in its Browser Run FAQ. The Screenshot API documentation describes how a final 401 or 403 can indicate a login or error page rather than the requested content.
6. Other relevant capture settings
Start with the smallest request that reliably captures the signed-in view. Add settings only when the page calls for them, and check the chosen provider’s option names and limits.
- Selector capture: capture a dashboard panel instead of a full page if that is all you need.
- Viewport: set width and height to match the layout you intend to inspect. Responsive dashboards may render differently at mobile and desktop widths.
- Full-page capture: use when content extends below the initial viewport; lazy-loaded sections may need scrolling or additional waiting supported by the provider.
- Output format: choose PNG for crisp interface text, or a compressed format if smaller files matter and the API supports it.
- Headers and user agent: use only when the application requires them or renders different content based on them. A custom user agent does not necessarily bypass bot protection.
- Basic Authentication: use the provider’s dedicated authentication option where available rather than placing credentials in a URL.
7. Security, reliability, performance, and cost
Protect session credentials
A session cookie can grant the same access as the signed-in browser session. Keep it in a secret store or protected runtime configuration according to your organization’s standards. Avoid putting it in source code, public client-side code, URLs that may be logged, issue reports, or debug output. Use a short-lived or purpose-limited account where your system supports that approach, and revoke the session when it is no longer needed.
Expect sessions to expire
Cookie validity is controlled by the target application. A capture pipeline should treat a login page, authorization error, or missing authenticated selector as a failed capture, then refresh or reacquire credentials through an authorized login flow. Do not retry indefinitely with the same expired cookie.
Control waits and retries
Selector waits often reduce unnecessary waiting, while network-idle waits can be helpful when content appears only after API calls. Use explicit timeouts and bounded retries for transient navigation failures. Repeatedly launching captures against an invalid session adds latency and may trigger the target’s security controls.
Understand billing before scaling
Pricing, retry billing, caching, and failed-capture treatment depend on the selected service. Check its current pricing and billing documentation before running large batches. For ScreenshotNeo, only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses identify the page verdict and billing state in headers. Its plans include 1,000 free shots per month with no card, then paid options starting at $5 for 3,000 shots. See ScreenshotNeo and its API documentation.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The screenshot shows a login page | The cookie is expired, wrong, incomplete, or scoped to a different domain or path. | Acquire a fresh session through an authorized flow. Check cookie name/value and match the target host and path. |
| The screenshot shows an access-denied page | The site rejected the session or the rendering request; some sites also apply bot checks. | Confirm the account is authorized and the cookie is current. Check the final page/status if available. Do not assume changing the user agent defeats bot checks. |
| The API returns 400 or reports invalid input | Cookie fields or serialization do not match that provider’s schema; JSON may also be malformed. | Compare the request to that API’s current reference. Use a structured object only where the API expects one; encode string formats as documented. |
| The API returns 401 or 403 | The screenshot API credential may be missing or insufficient, or the target page itself returned an authorization response. | Distinguish the API’s HTTP response from the final target page status. Check the API token and inspect page metadata or the image. |
| The image is blank or missing dashboard data | Capture occurred before JavaScript or data requests completed, or the selector wait does not identify the loaded view. | Wait for a known authenticated-content selector or use an appropriate navigation wait; verify the selector exists on that page. |
| The request times out | The target is slow, has persistent network activity, or the chosen wait condition cannot complete. | Set a reasonable bounded timeout and use a specific selector wait where possible. Avoid requiring total network idle for pages that poll continuously. |
| It works on one host but not after redirect | Cookie scope or provider header behavior may not cover the redirected host. | Check the redirect destination and its cookie domain. Some services restrict custom headers to the original host; verify the provider’s rule. |
| It works locally but fails in production | Production may be missing the secret, using a stale session, or encoding the cookie differently. | Confirm secret injection and encoding without printing the value. Compare request structure and target URL across environments. |
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its API supports cookies and the other screenshot parameters developers commonly use; check the documentation for the current cookie parameter format. The basic one-call request is:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
ScreenshotNeo removes cookie and consent banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshot tools. 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
See the ScreenshotNeo API docs for authenticated capture options, then sign up free for 1,000 screenshots a month with no card.
FAQ
Can I reuse the cookie copied from my browser?
Only while it remains valid and only if the screenshot provider sends it with a scope accepted by the target. Many sessions expire or rotate, so production workflows generally need an authorized way to obtain fresh credentials.
Should I send every cookie for the domain?
No. Start with the session cookie or minimum set required by the application. Extra cookies complicate debugging and expose more credential material.
Does a successful API response mean I captured the logged-in page?
No. It means the screenshot operation returned successfully. Verify the image or final-page metadata to distinguish the intended content from a login, error, or bot-check page.
Can I use a cookie to get around a site’s access controls?
No. Use credentials only for pages you are authorized to access, and respect the application’s access rules.


