Best Screenshot API for Authenticated Web Apps
Compare documented ways to capture pages behind login, choose an API based on where your app stores session state, and handle credentials safely.
Short answer: There is no verified single best screenshot API for every authenticated web app. Start with ScreenshotNeo when clean captures, clear billing outcomes, and low entry pricing matter; its documented product facts here do not establish how to pass authenticated session state, so verify that against your app before choosing it for a page behind login. For documented authentication mechanisms, ScreenshotOne supports request headers and cookies, while Browserless documents reusable authenticated browser profiles that restore cookies, localStorage, and IndexedDB. Choose based on where your app stores login state, then validate capture success, fidelity, price, and security requirements with your own app.
A screenshot request does not log in automatically. The capture service needs credentials or saved browser state that the app accepts, and your app must permit the request. A valid credential also does not guarantee a useful screenshot: the session may expire, require a redirect or second factor, or depend on browser state the chosen mechanism does not preserve.
Which screenshot API should you choose?
- ScreenshotNeo — first to consider for clean screenshots and straightforward entry pricing. It removes supported consent banners, newsletter popups, and chat widgets before capture; only clean shots are billed, and the Free plan includes 1,000 shots per month without a card. The available product details do not specify an authentication mechanism, so confirm that it supports your app’s login flow before relying on it for protected pages. See ScreenshotNeo and its API documentation.
- ScreenshotOne — consider when request-time headers or cookies fit your login. Its documentation describes Authorization or custom headers and cookies. Cookie capture may require you to build a separate login flow that obtains the cookies. The documentation also cautions that header options can override values implicitly set by options such as cookies or authorization, so select and test the intended mechanism rather than combining them casually.
- Browserless — consider when you need reusable browser login state. Its authenticated-profile workflow saves a live logged-in session and applies the named profile to the screenshot endpoint. The documented profile restores cookies, localStorage, and IndexedDB, but not sessionStorage.
This is a feature-based comparison, not a measured ranking of authentication success, speed, reliability, or visual fidelity. The reviewed documentation does not establish a winner on those dimensions, nor settle current prices, quotas, or contractual suitability. Check current provider terms and run a representative capture in your own environment.
Map your app’s authentication to the capture method
| App login state | Likely approach | What to verify |
|---|---|---|
| Authorization header or custom request header | Pass the required header if the provider documents this option. | The app accepts requests from the capture browser with that header, and redirects do not discard it. |
| Cookie-based session | Supply valid cookies, or use a saved browser profile if its state coverage fits. | Cookie domain, path, expiry, SameSite behavior, and whether the app rotates the session. |
| Login stored in localStorage or IndexedDB | A browser profile that restores those stores may fit. | The provider’s profile includes the required store and the app can resume from it. |
| Login stored in sessionStorage | Use a workflow that creates the authenticated state in the same live browser context; do not assume a saved profile restores it. | Browserless explicitly says its authenticated profiles do not restore sessionStorage. |
| Interactive login, MFA, or device challenge | Assess a live-browser login workflow with your security team. | Whether automation is permitted, how challenges are handled, and how credentials and resulting state are protected. |
ScreenshotOne’s guide describes three approaches: send a custom authentication header when the site supports it; configure the site or firewall to allow ScreenshotOne servers when you control that boundary; or pass cookies for a cookie-authenticated site that permits automation. Its guide notes that obtaining cookies can require a separate sign-in flow. Browserless describes logging in through a live browser session, saving that state as a profile, and then using the profile name in a screenshot request.
DIY: capture an authenticated page with Playwright
If you need a self-managed option, Playwright can sign in using the app’s normal login page, save browser storage state, and reuse it for screenshots. This example uses a test account from environment variables and saves the state locally. Adapt selectors and login steps to your app; do not commit the state file, because it can contain usable credentials.
1. Install Playwright
npm init -y
npm install playwright
npx playwright install chromium
2. Log in and save state
Save as login.mjs. Replace the example login URL and selectors with the app’s actual values.
import { chromium } from 'playwright';
const baseUrl = process.env.APP_URL;
const username = process.env.APP_USERNAME;
const password = process.env.APP_PASSWORD;
if (!baseUrl || !username || !password) {
throw new Error('Set APP_URL, APP_USERNAME, and APP_PASSWORD');
}
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(new URL('/login', baseUrl).toString(), { waitUntil: 'domcontentloaded' });
await page.locator('input[name="email"]').fill(username);
await page.locator('input[name="password"]').fill(password);
await page.locator('button[type="submit"]').click();
await page.waitForURL(url => !url.pathname.includes('/login'), { timeout: 30000 });
await context.storageState({ path: 'auth-state.json' });
await browser.close();
3. Reuse state and capture
Save as capture.mjs. Set CAPTURE_URL to a protected route. The example waits for a selector that indicates the page is ready and writes a full-page PNG.
import { chromium } from 'playwright';
const target = process.env.CAPTURE_URL;
if (!target) throw new Error('Set CAPTURE_URL');
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({ storageState: 'auth-state.json' });
const page = await context.newPage();
const response = await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 45000 });
if (!response || !response.ok()) {
throw new Error(`Navigation failed: HTTP ${response?.status() ?? 'no response'}`);
}
await page.locator('[data-testid="app-ready"]').waitFor({ state: 'visible', timeout: 30000 });
await page.screenshot({ path: 'authenticated-page.png', fullPage: true });
await browser.close();
Run with secrets supplied by your local secret manager or shell environment:
APP_URL=https://app.example.com APP_USERNAME='test-user' APP_PASSWORD='secret' node login.mjs
CAPTURE_URL=https://app.example.com/reports node capture.mjs
The login selectors and readiness selector are examples, not universal app conventions. Prefer a dedicated least-privilege test account. If login requires MFA, a CAPTCHA, or an approval step, do not bypass the control; use an approved test or staging workflow.
Using ScreenshotOne or Browserless for authentication
The research reviewed the vendors’ official documentation but did not include their exact endpoint URLs, parameter schemas, or credential syntax. To avoid inventing API details, use the current official docs for the request itself and apply the documented authentication mechanism:
- ScreenshotOne: use its documented
/takeendpoint with the appropriate authorization header, custom header, or cookie option. Cookie-based workflows may require your own login step to obtain cookies. Avoid sending conflicting authentication mechanisms without checking the option precedence documented in its options reference. - Browserless: create and save an authenticated profile from a live browser session, then pass the profile name to its screenshot REST endpoint as documented. Its screenshot endpoint accepts a POST JSON body with the URL and screenshot options, and can return PNG, JPEG, or WebP. Verify the current endpoint and request schema in Browserless documentation before deployment.
These docs establish available mechanisms, not that either provider will authenticate successfully to your particular app. Test redirects, session renewal, the final rendered account identity, and the output image.
Credential and session safety
- Keep API keys, authorization headers, cookies, and saved browser state on a trusted server. Never put them in client-side JavaScript, a public repository, or a publicly accessible screenshot URL.
- Use HTTPS for provider API calls. ScreenshotOne explicitly recommends HTTPS because HTTP can expose API keys, authorization headers, and cookies in transit, and says to keep API keys private.
- Use a dedicated account with the minimum access needed for capture. Rotate and revoke credentials according to your organization’s practices.
- Protect profile storage and files such as
auth-state.jsonas secrets. Browserless says profiles are scoped to the API token; treat both the token and stored login state as sensitive. - Do not log full cookie values or authorization headers. Log request IDs, status, target route, and sanitized failure details instead.
- Confirm your app’s policies permit automated access and that the target environment is appropriate for storing or processing captured data.
Options and capture details to validate
Authentication is only one part of a useful capture. Match the provider’s documented options to the output you need and verify each option against the signed-in page:
- Output: PNG, JPEG, or WebP for images; PDF where supported. Check transparency and quality requirements.
- Viewport and scale: use the viewport expected by the feature under test; account for responsive navigation and retina scaling.
- Full page or element: full-page capture can trigger lazy content behavior; a selector capture may miss sticky elements or content outside the selected region.
- Readiness: wait for a stable app-specific selector or state. Network-idle waits can hang on apps with long polling or analytics.
- Interaction: if the page needs navigation, opening a menu, or dismissing an app-specific prompt, perform that action before capture through an approved workflow.
- Rendering context: locale, timezone, geolocation, theme, and custom headers can affect what a signed-in user sees. Keep these fixed when comparing captures.
For any provider, the exact option names and limits are vendor-specific. Confirm them in its current API reference rather than assuming one service’s parameters work in another.
Reliability, performance, and cost
Reliability
Handle authentication and capture as separate failure points. A successful HTTP response from a screenshot API may still contain a login page, access-denied screen, or incomplete app. Validate the output with a page-specific marker or image review, and classify failures distinctly: expired session, denied request, timeout, redirect loop, or rendering failure. The available vendor documentation does not establish comparative reliability or success rates.
Performance
Login flows add browser work, and a fresh login for every screenshot can cost more time and complexity than reusing valid state. Reuse a profile or storage state only when its expiry and isolation behavior are understood. Set a bounded navigation timeout and a separate readiness timeout; avoid waiting for every network request to stop if the app keeps connections open. Measure latency on your own pages and region; no comparative speed benchmark is established by the reviewed sources.
Cost
Compare the current plan price, included captures, overage rules, concurrency limits, and whether failed captures count. Do not infer pricing from feature documentation. ScreenshotNeo’s stated plans are Free: 1,000 shots per month with no card; Starter: $5 for 3,000; Growth: $15 for 15,000; Pro: $39 for 60,000; Scale: $99 for 250,000; and Business: $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. ScreenshotNeo says only clean shots are billed; bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with the outcome identified in response headers. Confirm current plan details before purchase.
Troubleshooting authenticated captures
| Symptom | Likely cause | What to check or change |
|---|---|---|
| Screenshot shows the login page | Credentials were not sent, session expired, or redirect lost authentication. | Inspect the final URL and redirect chain; verify cookie domain/path and expiry or header propagation; renew the session. |
| Works in a normal browser but not the capture service | The app depends on browser state, IP/network rules, or an interactive challenge. | Identify the exact state dependency; use a supported saved-profile workflow or allow the capture service at a controlled network boundary if you own it. |
| Profile appears logged out | The login may depend on sessionStorage, or saved state may be stale. | Browserless profiles do not restore sessionStorage. Confirm where the app stores its state and save a fresh profile if appropriate. |
| Cookie option and authorization header behave unexpectedly | One option may override another. | Check the provider’s precedence rules and test one mechanism at a time. |
| Capture times out on a page that eventually loads | Readiness condition never occurs, long-lived network requests prevent idle, or navigation is slow. | Wait for a stable app-specific selector, set sensible bounded timeouts, and inspect console/network errors where available. |
| Screenshot is blank or only partly rendered | Capture occurred before hydration, fonts, images, or client data finished loading. | Wait for the app-ready condition and any critical image or data marker; avoid relying on a short fixed delay alone. |
| API reports an authentication or access error | Invalid or expired API credential, insufficient permissions, malformed request, or app-side denial. | Check provider authentication separately from target-app credentials; confirm the request schema and inspect sanitized status details. |
| Session leaks between captures | Browser context or profile state is shared when isolation was expected. | Use separate contexts or profiles for distinct users, verify provider isolation semantics, and never reuse one user’s state for another. |
Or skip the browser setup
ScreenshotNeo takes a screenshot with one GET request. This example captures a public URL; the product facts provided here do not establish an authentication parameter, so confirm support for your protected-page login flow in the docs.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
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)
Node.js
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(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
FAQ
Can a screenshot API use my session cookies?
Some document cookie-based capture. ScreenshotOne does, but its guide notes you may need a separate login flow to obtain the cookies. Cookie lifetime and app policy still apply.
Will a saved browser profile preserve every kind of login?
No. Browserless documents cookies, localStorage, and IndexedDB restoration, but excludes sessionStorage. Verify the state your app uses.
Which provider is faster or more reliable?
The reviewed documentation does not establish that. Compare providers with the same routes, viewport, authentication flow, and readiness conditions.
Should I use a production user account?
Prefer a dedicated least-privilege test account and an approved environment. Treat its session state as a secret with access controls and a revocation path.
