ScreenshotNeo

BlogHow-to

How to Generate a Website Screenshot with a REST API

Learn the REST screenshot request flow, how to save image bytes safely, tune capture options, handle errors, and choose a provider-specific API contract.

By the ScreenshotNeo team4 October 20269 min read

A website screenshot REST API renders a URL on a hosted browser and returns the result over HTTP. The basic flow is: authenticate on your server, send the target URL and capture options to the provider’s documented endpoint, check the response status and content type, then save the returned bytes or parse a documented JSON result. Endpoints, field names, authentication, and response formats differ by provider, so do not combine examples from different services.

This guide shows a provider-specific DIY request, explains how to handle binary responses and common failure cases, and then gives a one-call option with ScreenshotNeo.

1. Choose and verify the API contract

Before writing an integration, check the provider’s current quickstart and reference. Record the exact endpoint, supported HTTP methods, authentication method, accepted option names, success response type, error shape, and any result-file retention period.

Contract detail Why it matters
Endpoint and method Some providers support GET and POST; others document only one method or different options for each.
Authentication Know whether the key belongs in a header or query parameter and keep it out of public browser code.
Success response The response may be raw image or PDF bytes, a redirect, or JSON containing a result URL and metadata.
Capture options Viewport, full-page mode, format, waits, selectors, and interactions are provider-specific.
Retention and quotas Hosted result URLs may expire; usage limits and prices vary by provider and plan.

For example, ScreenshotEngine documents POST https://api.screenshotengine.com/v1/screenshot, bearer authentication, JSON input, and raw file bytes on successful requests. The request below is specifically for ScreenshotEngine; it is not a universal endpoint or field contract. See its quickstart and parameter reference.

2. Make a REST request and save the result

Start with a public URL that you control or are authorized to capture. Keep the service API key in a server-side secret store or protected environment variable. In this example, SCREENSHOTENGINE_API_KEY is an environment variable. The API key authenticates your request to ScreenshotEngine; it does not authenticate you to the target website.

cURL

curl --fail-with-body \
  -X POST "https://api.screenshotengine.com/v1/screenshot" \
  -H "Authorization: Bearer ${SCREENSHOTENGINE_API_KEY}" \
  -H "Content-Type: application/json" \
  -H "Accept: image/png" \
  --data '{"url":"https://example.com","format":"png","fullPage":true}' \
  --output screenshot.png

--fail-with-body makes cURL return a failure status for HTTP errors while retaining the error body for inspection. A successful ScreenshotEngine response contains the file bytes directly, so the output file should be handled as binary.

Python

import os
import requests

api_key = os.environ["SCREENSHOTENGINE_API_KEY"]
endpoint = "https://api.screenshotengine.com/v1/screenshot"
payload = {
    "url": "https://example.com",
    "format": "png",
    "fullPage": True,
}

response = requests.post(
    endpoint,
    headers={
        "Authorization": f"Bearer {api_key}",
        "Accept": "image/png",
    },
    json=payload,
    timeout=90,
)

if not response.ok:
    raise RuntimeError(
        f"Screenshot request failed: HTTP {response.status_code}: "
        f"{response.text[:1000]}"
    )

content_type = response.headers.get("Content-Type", "")
if "image/" not in content_type:
    raise RuntimeError(f"Expected image bytes, received {content_type!r}")

with open("screenshot.png", "wb") as output:
    output.write(response.content)

Node.js

import { writeFile } from "node:fs/promises";

const apiKey = process.env.SCREENSHOTENGINE_API_KEY;
if (!apiKey) throw new Error("Set SCREENSHOTENGINE_API_KEY first");

const response = await fetch("https://api.screenshotengine.com/v1/screenshot", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Accept": "image/png",
  },
  body: JSON.stringify({
    url: "https://example.com",
    format: "png",
    fullPage: true,
  }),
  signal: AbortSignal.timeout(90_000),
});

if (!response.ok) {
  const detail = (await response.text()).slice(0, 1000);
  throw new Error(`Screenshot request failed: HTTP ${response.status}: ${detail}`);
}

const contentType = response.headers.get("content-type") ?? "";
if (!contentType.includes("image/")) {
  throw new Error(`Expected image bytes, received ${contentType}`);
}

await writeFile("screenshot.png", Buffer.from(await response.arrayBuffer()));

These examples expect ScreenshotEngine’s documented binary success response. Its error responses are JSON. Check the HTTP status before writing a file, and inspect Content-Type as an additional guard. If a different provider documents JSON on success, parse its documented result structure instead of saving the JSON as an image.

3. Set capture options deliberately

Common options control what portion of the page is rendered and how the output is produced. Use only names and values supported by the selected provider; even similarly named options may differ in casing or behavior.

Option area What to decide Practical guidance
Viewport Width and height of the browser viewport Set dimensions that match the intended layout. A viewport screenshot captures the visible area, not necessarily the entire document.
Full page Whether to capture beyond the initial viewport Use the provider’s full-page setting when you need a long page. Lazy-loaded images and infinite-scroll content may need special handling.
Format PNG, JPEG, WebP, or PDF where supported Choose the documented format and match the file extension and downstream consumer to it.
Wait behavior Delay, selector wait, or network-idle condition Prefer a specific selector or a short delay when the page needs client-side rendering. Excessive waits add latency.
Page selection Element or selector capture Use selector capture for a component or card when supported; handle a missing selector as a failed capture.
PDF controls Paper size, orientation, margins, or page ranges PDF options are separate from image viewport behavior. Check the provider’s PDF documentation.

ScreenshotEngine documents full-page capture using height: "full", viewport presets, selector capture, waits, and PDF paper/orientation settings. It also notes that its device presets set viewport dimensions; they do not emulate a full physical device, touch input, user agent, or pixel density. Its parameter names are case-sensitive, and some GET and POST options use different naming conventions. Check the provider’s reference before adapting an example.

4. Handle target-page access and response variants

A public page is the simplest case. If the target needs a login, distinguish two separate credentials:

  • Screenshot API key: authorizes your application to call the rendering service.
  • Target-site credentials: cookies, target authorization headers, or a login session that grants access to the page being captured.

Do not assume a provider can sign in or reuse your browser session. ScreenshotEngine’s documented endpoint accepts a public URL and does not expose custom cookies, target Authorization headers, or login scripts. Screenshot API’s documentation shows cookie, header, and basic-auth controls, but sending secrets to a hosted rendering provider requires checking that provider’s data-handling terms. For pages requiring complex interaction or an already-authenticated browser profile, local browser automation may fit better than a simple screenshot endpoint.

Also do not assume a successful HTTP status means the intended page appeared. A renderer can capture a login screen, access-denied page, or redirect destination successfully. Where available, inspect final page status or page metadata, and review output during integration.

Some APIs return a hosted file URL or a redirect instead of file bytes. If so, follow only the documented response flow, parse the correct JSON fields, and account for expiry and retention. For example, WebsiteScreenshotAPI documents a URL and expiry field in its result object. That behavior is specific to that service.

5. Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server for developers. Its GET endpoint returns an image or PDF from one request. See the ScreenshotNeo API documentation for the endpoint’s request options and response behavior.

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,
)
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(`Screenshot request failed: HTTP ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', bytes));

ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with X-Page-Verdict and X-Billed headers identifying the result. Its MCP server offers take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.

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

6. Troubleshoot common failures

Symptom Likely cause Fix
401 or 403 from the API Missing, invalid, expired, or incorrectly placed service key Check the provider’s required auth scheme and environment variable. Rotate a key if it was exposed.
400 response Invalid URL, unsupported option, wrong value type, or case mismatch Compare the payload to that provider’s current endpoint reference. Do not mix field names from other services.
Saved file is JSON or cannot be opened An error body was saved as an image, or the provider returns JSON on success Check status and Content-Type first. Read JSON only when the provider documents it as the success format.
Image shows a login or error page The target requires access, blocked the renderer, or redirected elsewhere Inspect the final page or status where available. Confirm supported target credentials and use them only under appropriate provider terms.
Screenshot is clipped Only the viewport was captured Enable documented full-page capture. For long or lazy-loaded pages, check the provider’s behavior and wait controls.
Blank, incomplete, or stale page Capture happened before rendering completed, scripts failed, or a cache returned an older result Try a documented selector wait or measured delay; verify the target manually and review cache controls if available.
Timeouts or slow requests Slow target page, excessive full-page work, or too-long waits Set explicit client timeouts, reduce unnecessary capture area, and retry only transient failures with bounded backoff.
Unexpectedly high usage Retries, duplicate requests, or ignored cache behavior Track request volume and provider usage, choose cache settings deliberately, and avoid unbounded automatic retries.

7. Performance, reliability, and cost

  • Limit work: viewport captures are usually smaller jobs than full-page captures; capture only the area and format your use case needs.
  • Wait with intent: selector waits can be more predictable than arbitrary long delays when the page exposes a stable element. Every wait increases response time.
  • Use explicit timeouts: set a client timeout suitable for rendering, and distinguish a client timeout from a provider-side render failure.
  • Retry carefully: retry transient network or service errors with a small bounded backoff. Avoid retrying permanent invalid-request and authorization errors.
  • Make downstream writes safe: write to a temporary file and rename after validation when partial files would be harmful.
  • Budget from real usage: compare current quota and price against expected successful captures, retries, and any billable failure rules. Provider prices and retention policies change; check current plan pages rather than relying on old comparisons.
  • Protect keys and target secrets: store service keys server-side, avoid logging credentials, and treat target cookies or authorization headers as sensitive data.

For ScreenshotNeo, only clean shots are billed: bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Its response identifies the page verdict and billing outcome in headers. Plans are Free for 1,000 shots/month, 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. Review the docs for request options and your usage API for tracking consumption.

8. Frequently asked questions

Can I call a screenshot REST API from browser JavaScript?

Only if the provider explicitly supports a safe browser-side authorization design. A secret API key in frontend code can be copied and abused, so a server-side proxy is the usual pattern.

Does an API key let the renderer access a private website?

No. The API key authenticates the call to the screenshot provider. Access to the target page requires separate target-site authentication, if the provider supports it.

Should I use GET or POST?

Follow the provider contract. POST with a JSON body is convenient for multiple settings and can keep credentials out of the URL when header auth is supported. GET is simple when documented, but query-string credentials can appear in logs.

Why does a device preset not look exactly like a phone screenshot?

A preset may set only viewport dimensions. Confirm whether the provider also emulates a user agent, pixel density, browser behavior, or touch input.

Sources