Python and PHP Clients for Screenshot APIs
Learn the request flow, SDKs, signing, rendering options, errors, and production patterns for screenshot APIs in Python and PHP.
Short answer: A screenshot API client follows four steps: keep credentials on the server, send a target URL and render options, receive image bytes or a generated URL, and save or display the result. Python integrations commonly use an official SDK or signed HTTP requests. PHP integrations commonly use Composer SDKs or signed requests. The same design works with cURL, Node.js, and other HTTP clients.
This guide explains the provider-neutral flow first, then shows Python and PHP implementations, request signing, rendering controls, asynchronous jobs, failure handling, and production considerations. Package versions, quotas, prices, uptime claims, and terms change, so check each provider’s current documentation before deployment.
1. How a screenshot API request works
- Create an account and obtain the access key. Providers that sign requests also issue a secret key.
- Send the page URL plus options such as viewport size, output format, full-page mode, delay, selector, cookies, or device scale.
- Choose a synchronous response (image bytes or JSON) or an asynchronous job with polling or a webhook.
- Check the HTTP status and response headers before writing the response to storage.
| Decision | Typical choices | Why it matters |
|---|---|---|
| Authentication | Access key; access key plus secret and HMAC signature | Secrets must stay on your server, never in browser code. |
| Response | Binary image, generated render URL, JSON with a result link | Binary is simple for downloads; URLs are useful for HTML and caching. |
| Execution | Synchronous, asynchronous with polling, asynchronous with webhook | Async jobs avoid long request timeouts for complex pages or PDFs. |
| Output | PNG, JPEG, WebP, AVIF, SVG, PDF, HTML (provider dependent) | Pick the format required by your storage, browser, or document pipeline. |
2. Python: SDK and HTTP approaches
Official SDK pattern
ScreenshotOne documents an official Python SDK and simple HTTP requests. Its documented flow installs the package, creates a client with an access key and secret key, builds TakeOptions, and either generates a take URL or downloads the stream. The SDK examples include PNG output, viewport dimensions, cookie-banner blocking, and chat blocking. See the provider’s current Python documentation before pinning a version.
python -m pip install screenshotone
import os
from pathlib import Path
from screenshotone import Client, TakeOptions
client = Client(
os.environ["SCREENSHOTONE_ACCESS_KEY"],
os.environ["SCREENSHOTONE_SECRET_KEY"],
)
options = TakeOptions(
url="https://example.com",
format="png",
viewport_width=1440,
viewport_height=900,
full_page=True,
block_cookie_banners=True,
block_chats=True,
)
# Option A: create a signed URL for an img tag or CDN fetch.
render_url = client.generate_take_url(options)
print(render_url)
# Option B: request the image and save the returned stream.
response = client.take(options)
Path("example.png").write_bytes(response.read())
Python with a signed HTTP request
Some services expose a signed URL instead of requiring a package. Urlbox documents constructing a URL-encoded option string, creating an HMAC-SHA256 token with the API secret, and requesting a PNG endpoint. Keep the secret in an environment variable.
import hashlib
import hmac
import os
from pathlib import Path
from urllib.parse import urlencode
import requests
api_key = os.environ["URLBOX_API_KEY"]
secret = os.environ["URLBOX_API_SECRET"]
params = {
"url": "https://example.com",
"format": "png",
"width": 1440,
"height": 900,
"full_page": "true",
}
query = urlencode(params)
token = hmac.new(secret.encode(), query.encode(), hashlib.sha256).hexdigest()
endpoint = f"https://api.urlbox.com/v1/{api_key}/{token}/png?{query}"
r = requests.get(endpoint, timeout=90)
r.raise_for_status()
Path("example.png").write_bytes(r.content)
Python with a simple access-key endpoint
ApiFlash documents a GET endpoint that accepts access_key and url. By default it returns image data; selecting response_type=json returns JSON containing result links.
import os
from pathlib import Path
import requests
params = {
"access_key": os.environ["APIFLASH_ACCESS_KEY"],
"url": "https://example.com",
"format": "png",
"width": 1440,
"height": 900,
}
r = requests.get("https://api.apiflash.com/v1/urltoimage", params=params, timeout=90)
r.raise_for_status()
Path("example.png").write_bytes(r.content)
3. PHP: Composer SDKs and signed requests
ScreenshotOne Composer SDK
ScreenshotOne documents a PHP SDK installed with Composer. Its examples use Client and TakeOptions, generate a URL, or save the image directly with file_put_contents. The documented options include full-page rendering, delay, and geolocation.
composer require screenshotone/sdk:^1.0
<?php
require __DIR__ . '/vendor/autoload.php';
use ScreenshotOne\Client;
use ScreenshotOne\TakeOptions;
$client = new Client(
getenv('SCREENSHOTONE_ACCESS_KEY'),
getenv('SCREENSHOTONE_SECRET_KEY')
);
$options = new TakeOptions(
url: 'https://example.com',
format: 'png',
fullPage: true,
delay: 2,
geolocationLatitude: 40.7128,
geolocationLongitude: -74.0060
);
$renderUrl = $client->generateTakeUrl($options);
file_put_contents('example.png', file_get_contents($renderUrl));
Urlbox Composer SDK
Urlbox documents a Composer package that creates a client from credentials, signs a render URL, and lets you embed that URL in an image tag.
composer require urlbox/screenshots
<?php
require __DIR__ . '/vendor/autoload.php';
use Urlbox\Urlbox;
$urlbox = Urlbox::fromCredentials(
getenv('URLBOX_API_KEY'),
getenv('URLBOX_API_SECRET')
);
$renderUrl = $urlbox->generateSignedUrl([
'url' => 'https://example.com',
'format' => 'png',
'width' => 1440,
'height' => 900,
'full_page' => true,
]);
header('Content-Type: image/png');
readfile($renderUrl);
PHP with a generic HTTP request
<?php
$url = 'https://example.com';
$endpoint = 'https://api.apiflash.com/v1/urltoimage?' . http_build_query([
'access_key' => getenv('APIFLASH_ACCESS_KEY'),
'url' => $url,
'format' => 'png',
]);
$context = stream_context_create([
'http' => [
'method' => 'GET',
'timeout' => 90,
'ignore_errors' => true,
],
]);
$bytes = file_get_contents($endpoint, false, $context);
if ($bytes === false) {
throw new RuntimeException('Screenshot request failed');
}
file_put_contents(__DIR__ . '/example.png', $bytes);
4. cURL and Node.js equivalents
cURL
curl -G "https://api.apiflash.com/v1/urltoimage" \
--data-urlencode "access_key=$APIFLASH_ACCESS_KEY" \
--data-urlencode "url=https://example.com" \
--data-urlencode "format=png" \
-o example.png
Node.js
const fs = require('node:fs/promises');
const q = new URLSearchParams({
access_key: process.env.APIFLASH_ACCESS_KEY,
url: 'https://example.com',
format: 'png',
width: '1440',
height: '900'
});
const res = await fetch(`https://api.apiflash.com/v1/urltoimage?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
await fs.writeFile('example.png', Buffer.from(await res.arrayBuffer()));
5. Rendering options you should plan for
| Option | Use it when | Edge cases |
|---|---|---|
| Viewport width and height | You need repeatable desktop or mobile layouts. | Responsive breakpoints can change the page dramatically. |
| Device scale or retina scale | You need sharper text on high-density displays. | File size and processing time increase. |
| Full-page capture | You need the entire document rather than the visible viewport. | Very long pages can exceed provider limits or create huge images. |
| Delay, selector wait, or network idle | Content is rendered after JavaScript executes. | A network-idle wait may never finish on pages with analytics or sockets. |
| CSS selector capture | You need one chart, card, or component. | Selectors can break when the site’s markup changes. |
| Cookies, headers, and user agent | You must render a locale, authenticated view, or feature flag. | Never expose session cookies in generated public URLs. |
| Blocking ads, trackers, chats, and resource types | You want stable, uncluttered output. | Blocking a required script can leave a blank component. |
| Format | PNG for lossless UI, JPEG for photos, WebP or AVIF for smaller files, PDF for documents. | Confirm alpha-channel and animation support before relying on it. |
6. Synchronous, asynchronous, and webhook workflows
A direct render link returns the image in the request. This is convenient for thumbnails and server-side downloads. Urlbox also documents POST requests that can run synchronously or asynchronously, with polling or webhooks, and can return JSON or binary data.
- Submit the URL and options with a stable idempotency key if the provider supports one.
- For a synchronous call, enforce a client timeout and handle non-2xx responses.
- For an async call, persist the job ID and poll with backoff or register a webhook endpoint.
- Verify webhook authenticity when the provider supplies a signature.
- Store the original options and provider response metadata for debugging.
7. Error handling and troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 401 or 403 | Wrong key, missing signature, expired token, or secret exposed incorrectly. | Load credentials from environment variables, regenerate compromised keys, and compare signing inputs byte for byte. |
| 400 invalid URL | Unescaped query characters or an unsupported scheme. | Use a URL encoder and send an absolute HTTPS URL. |
| Timeout | Slow origin, infinite network activity, or an excessive wait condition. | Use a bounded delay, selector wait, or timeout; block unnecessary resources; move large jobs to async mode. |
| Blank image | JavaScript failed, content requires authentication, or a required resource was blocked. | Open the URL from the capture environment, add required headers or cookies, and remove overly broad blocking rules. |
| Cookie banner still visible | The provider does not recognize that banner or the option was omitted. | Use a provider’s cookie-blocking feature when available, or hide the banner with a targeted CSS selector. |
| Element not found | Selector changed or the element appears after asynchronous rendering. | Wait for the selector, confirm casing and escaping, and capture the parent container if markup is unstable. |
| Corrupt downloaded file | JSON error text was saved as an image. | Check status and content type before writing bytes; log the response body on errors. |
| PHP memory exhaustion | Large full-page image loaded entirely into memory. | Prefer streaming downloads, reduce scale, or use an async URL and stream it to storage. |
| Python package import error | Package not installed in the active virtual environment. | Activate the environment, install the documented package, and pin a verified version. |
8. Reliability, performance, and cost
- Set explicit connect and total timeouts. Retry only transient network and 5xx failures, with exponential backoff and a retry limit.
- Use a queue for bulk captures so one slow origin does not block every web request.
- Cache identical URL-plus-options requests where freshness permits. Include all rendering options in the cache key.
- Prefer WebP or AVIF for image-heavy pipelines when your consumers support them; use PNG when exact pixels or transparency matter.
- Full-page mode, high device scale, PDFs, and JavaScript-heavy pages consume more time and storage than a viewport screenshot.
- Control concurrency to avoid rate limits and to prevent your own workers from exhausting memory.
- Track status code, provider request ID, elapsed time, output bytes, and whether the response was an image or an error.
- Prices, quotas, package versions, and uptime statements are provider-specific and volatile. Recheck the current account dashboard and documentation before forecasting spend.
9. Or skip the browser setup: ScreenshotNeo
ScreenshotNeo is a website screenshot API and MCP server. A GET request returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
Use the ScreenshotNeo API documentation for the complete option list. The API supports full-page capture with lazy images loaded, CSS-element capture, dark mode, 12 device presets or custom viewports, retina scale, PDF paper sizes and page ranges, HTML/CSS rendering, custom CSS and JavaScript, click and wait actions, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, async jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs also work for easier migration.
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)
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}`);
ScreenshotNeo also includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
10. Security checklist
- Store access keys and signing secrets in environment variables or a secret manager.
- Keep screenshot requests on a trusted backend when credentials are required.
- Redact query strings, cookies, authorization headers, and signed URLs from logs.
- Use short-lived or restricted credentials when the provider supports them.
- Validate user-supplied target URLs to prevent internal-network access through your capture service.
- Set retention rules for screenshots that may contain private or regulated data.
11. FAQ
Do I need Playwright or Selenium?
No. A hosted screenshot API runs the browser-rendering step for you. You still need to understand waits, authentication, responsive layouts, and page failures.
Should I return bytes or a URL from my application?
Return bytes when your server immediately stores or transforms the image. Return a signed URL when a browser, CDN, or image tag can fetch it directly.
Is an SDK required?
No. SDKs reduce boilerplate, but every provider can be used through an HTTP client if you implement its authentication and response rules.
When should I use PDF output?
Use PDF when pagination, paper size, margins, or a printable document matter. Use an image format for thumbnails, previews, and visual regression artifacts.
How do I make captures repeatable?
Fix the viewport, device scale, timezone, locale, cookies, user agent, wait condition, and resource-blocking rules. Cache only when the page’s freshness requirements allow it.


