ScreenshotNeo

BlogHow-to

How to Test Screenshot API Output Locally Before Deploying an Integration

Verify authentication, response formats, image output, and failure handling locally before deploying a screenshot API integration.

By the ScreenshotNeo team4 October 202610 min read

Test against the exact screenshot API endpoint your integration will use. Check the documented authentication and response format, save and validate image bytes or inspect the expected JSON fields, and exercise failure handling with mocked responses. Keep one small live request as a smoke test for credentials, networking, and the provider’s actual behavior.

There is no universal screenshot API response format. Some services return raw image bytes; others return JSON containing an image URL or base64 data, or redirect to an image. Confirm the provider’s contract before choosing how to read the response. For example, ScreenshotEngine documents raw file bytes, while Screenshot API documents JSON and redirect modes and ScreenshotAPI documents JSON/base64 and redirect modes.

1. Read the API contract before writing the test

Record these details from the current documentation for your chosen provider. Defaults, authentication, and response formats differ and can change.

  • Endpoint and HTTP method.
  • Authentication location, such as a bearer header or a query parameter.
  • Required request parameters and capture options.
  • Success status and response representation: raw bytes, JSON, a URL, or a redirect.
  • Success content type and documented response fields.
  • Failure statuses and error response format, including authentication, quota, rendering, and selector errors where documented.

A successful HTTP status by itself does not prove that the response is a usable screenshot. Check the status, then validate the response according to its documented type. ScreenshotEngine specifically warns that a successful raw-image response should not be parsed as JSON.

2. Make a controlled local request

  1. Choose a public, stable test URL or a page you control. Avoid personal information and login credentials.
  2. Keep the target URL, viewport, output format, full-page setting, and readiness condition fixed while debugging.
  3. Load credentials from an environment variable or your local secret mechanism. Do not commit keys or print them in diagnostic logs.
  4. Send one request using the exact endpoint, method, auth, and response mode that production will use.
  5. Save raw image bytes to a file. If the API returns JSON, validate its documented fields and retrieve the image only as the provider specifies.
  6. Open or decode the result, inspect its dimensions, and check that the expected page loaded without clipping, blank content, or premature capture.

When a request option could change what appears in the result, test it explicitly. Common controls include output type, viewport, full-page capture, a selector, a delay, or a page-load wait condition. The exact names and defaults are provider-specific.

3. Run a live smoke test with curl

For raw image output, pass the provider’s documented auth and capture parameters, save the body to a file, and inspect the status and content type. This shell example is a template: replace the endpoint, auth header, and parameters with those documented by your provider.

export SCREENSHOT_API_KEY='YOUR_API_KEY'
export SCREENSHOT_API_URL='https://YOUR_PROVIDER_ENDPOINT'

curl --fail-with-body --silent --show-error \
  -D response-headers.txt \
  -H "Authorization: Bearer $SCREENSHOT_API_KEY" \
  --get "$SCREENSHOT_API_URL" \
  --data-urlencode 'url=https://example.com' \
  --data-urlencode 'format=png' \
  --output screenshot.png

file screenshot.png

This command assumes that the provider accepts GET parameters, bearer authentication, and raw PNG output. Change those details to match the actual contract. If the provider expects POST with JSON, use that documented request instead. For a JSON response, save or inspect the JSON and validate its fields; do not name JSON data with an image extension.

Inspect response-headers.txt for the HTTP status and Content-Type. For binary output, expect the documented image type, such as image/png or image/jpeg. For JSON output, expect the provider’s documented JSON type and fields. If redirects are part of the API contract, decide whether the client should follow them and test that behavior deliberately.

4. Validate output in Python

This example assumes raw image bytes from a GET endpoint using bearer authentication and query parameters. Change the request and expected MIME type for your provider. The optional Pillow check verifies that the file decodes and reports its dimensions.

import os
from pathlib import Path

import requests

endpoint = os.environ["SCREENSHOT_API_URL"]
api_key = os.environ["SCREENSHOT_API_KEY"]

response = requests.get(
    endpoint,
    headers={"Authorization": f"Bearer {api_key}"},
    params={"url": "https://example.com", "format": "png"},
    timeout=90,
)

response.raise_for_status()
content_type = response.headers.get("Content-Type", "").split(";", 1)[0].lower()
if content_type != "image/png":
    raise ValueError(f"Expected image/png, got {content_type or 'no Content-Type'}")

output = Path("screenshot.png")
output.write_bytes(response.content)
print(f"Saved {output} ({len(response.content)} bytes)")

try:
    from PIL import Image
except ImportError:
    print("Install Pillow to validate decoding and dimensions: python -m pip install Pillow")
else:
    with Image.open(output) as image:
        image.verify()
    with Image.open(output) as image:
        print(f"Decoded image: {image.format}, {image.width}x{image.height}")

Install the dependencies with python -m pip install requests pillow. If the API returns JSON, call response.json() only after checking the documented JSON content type and status, then validate the expected fields before using a URL or base64 value. Do not assume every provider includes the same fields.

5. Validate output in Node.js

This runnable example uses Node.js with built-in fetch. It assumes bearer authentication and raw PNG output. Save it as smoke-test.mjs and run it with the endpoint and key in environment variables.

const endpoint = process.env.SCREENSHOT_API_URL;
const apiKey = process.env.SCREENSHOT_API_KEY;

if (!endpoint || !apiKey) {
  throw new Error("Set SCREENSHOT_API_URL and SCREENSHOT_API_KEY");
}

const url = new URL(endpoint);
url.searchParams.set("url", "https://example.com");
url.searchParams.set("format", "png");

const response = await fetch(url, {
  headers: { Authorization: `Bearer ${apiKey}` },
  signal: AbortSignal.timeout(90_000),
});

if (!response.ok) {
  const detail = await response.text();
  throw new Error(`Screenshot API returned ${response.status}: ${detail}`);
}

const contentType = (response.headers.get("content-type") ?? "")
  .split(";", 1)[0]
  .toLowerCase();
if (contentType !== "image/png") {
  throw new Error(`Expected image/png, got ${contentType || "no Content-Type"}`);
}

const bytes = Buffer.from(await response.arrayBuffer());
if (bytes.length === 0) throw new Error("Screenshot response was empty");

const { writeFile } = await import("node:fs/promises");
await writeFile("screenshot.png", bytes);
console.log(`Saved screenshot.png (${bytes.length} bytes)`);

For JSON output, parse the body as JSON only after validating the response status and expected content type, then assert the provider’s documented fields. For redirect responses, test how your chosen client handles redirects; behavior can depend on client defaults and provider contract.

6. Separate repeatable unit tests from the live check

Use mocks to test your application’s response handling without depending on a paid, rate-limited, or unavailable external service. Keep the live test small: it covers the real key, network path, request shape, and provider behavior that mocks cannot confirm.

Mock the cases your integration handles

  • Expected successful image content type and non-empty body.
  • Unexpected content type or malformed JSON.
  • Unauthorized credentials.
  • Invalid request parameters.
  • Rate limit or quota response.
  • Render failure, timeout, or selector-not-found response when the provider documents those cases.

Assert that your application reports useful errors and does not treat an error body as an image. For JSON or URL workflows, include missing fields, invalid URLs, and retrieval failures. Screenshot API’s documentation describes example classes including invalid requests, unauthorized keys, rate limits, render failures, and selector errors; use the chosen provider’s own documented statuses and payloads.

Use golden images for visual regressions, not HTTP contract checks

A golden-image test compares a screenshot with an approved reference image. It can catch visual changes, but it does not replace checks for authentication, response type, or error handling. Keep the environment and capture settings consistent. Android Developers notes that local screenshots can differ from Linux CI because of low-level rendering and environment changes, and recommends limiting test combinations. Small comparison tolerances can reduce noise but may also hide real changes. See Android Developers’ screenshot testing guide; its Android-specific tooling is not a website screenshot API client.

7. ScreenshotNeo: test a real capture with one request

ScreenshotNeo is a website screenshot API and MCP server. Its endpoint returns an image or PDF from one GET request. Start with this cURL request and replace the target URL if needed. See the ScreenshotNeo API documentation for request options and response details.

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}`);

Check the status and response headers, then open or decode the saved output as described above. ScreenshotNeo’s response includes X-Page-Verdict and X-Billed headers so you can see the page outcome and billing result.

Cookie and consent banners are accepted or removed before capture, along with known newsletter popups and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

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

8. Troubleshooting

Symptom Likely cause What to check or change
401 or 403 Missing, invalid, or misplaced credentials; key lacks access. Check the provider’s required auth method and environment variable. Confirm the key is for the intended account without printing it to logs.
400 or 422 Missing or invalid target URL or capture parameter. Compare the request with the provider’s required fields, parameter names, and encoding rules.
429 or quota error Rate limit or plan quota reached. Read the provider’s documented retry and quota behavior. Avoid tight retry loops; use a bounded retry policy only for errors documented as transient.
JSON parse error on a successful response The endpoint returned raw image bytes, or the response is not the JSON mode you expected. Check status and Content-Type before parsing. Save binary output as bytes; use JSON parsing only for a documented JSON response.
Image file is empty or cannot be decoded Error content was saved as an image, the response was truncated, or the content type does not match the requested format. Inspect status, headers, and a short sanitized error body. Check that the client reads the full byte response and that the provider supports the requested format.
Image is blank or missing dynamic content The capture happened before the page was ready, or the site returned a bot check or failure page. Use the provider’s documented selector, delay, or readiness option. Confirm the returned page content and verdict if available.
Element capture fails The selector does not match or the element appears after capture begins. Verify the selector in a browser and configure a documented wait-for-selector option where supported.
Unexpected dimensions or clipping Viewport, full-page setting, device scale, or page layout differs from assumptions. Set these inputs explicitly and inspect the decoded image dimensions. Check whether full-page capture is supported for the selected mode.
Local works, deployment fails Different secrets, egress rules, runtime timeout, or environment behavior. Compare endpoint, key configuration, network access, timeout budget, and request parameters without exposing secrets.
Visual snapshot differs in CI Rendering engine, fonts, OS, or other environment differences. Pin the environment where practical and use a deliberate image comparison tolerance. Review Android Developers’ notes on local and Linux CI rendering differences.

9. Performance, reliability, and cost

  • Keep live checks small. One controlled request can verify the external integration; mocked tests keep ordinary test runs independent of network availability and provider quotas.
  • Choose a realistic timeout. Rendering depends on the target page and provider behavior. Set a client timeout that fits your application and handle timeout errors explicitly.
  • Avoid uncontrolled retries. Retrying every failure can multiply latency and cost. Follow the provider’s documented retry guidance, and distinguish transient network or rate-limit responses from invalid input and authentication errors.
  • Limit visual test combinations. Each viewport, browser-like environment, and capture option can introduce another source of output variation and image storage.
  • Keep artifacts useful and safe. Store only the output needed to diagnose or compare behavior. Test pages should not expose private data, and logs should not contain API keys.
  • Check billing semantics. Providers differ in how they count successful calls, failed renders, caching, and retries. Read the current plan and billing documentation before using a live test in a frequent CI job.

Frequently asked questions

Should I use a real website or a page I control?

Use a stable public page for an initial smoke test, or a page you control when you need predictable content. Avoid pages containing personal or authenticated data.

Do I need a golden-image test?

Only when visual regressions matter to the integration. Golden images check appearance; status, content type, decoding, and error tests check the API contract.

Can every successful response be saved directly as PNG?

No. First identify whether the chosen endpoint returns image bytes, JSON, a URL, or a redirect, and confirm which image formats it supports.

How often should the live smoke test run?

Run it when validating the integration or deployment path. Keep high-frequency unit tests mocked so they do not rely on external availability or consume provider quota.