How to Use a Screenshot API with Python Requests
Use Python requests to call a screenshot API, configure a capture, handle JSON or image responses, and diagnose common errors.
Short answer: Python’s requests library sends an HTTP request to a screenshot API; the provider’s browser loads the page and returns either image bytes or metadata such as a URL to the image. Install requests, keep your API key in an environment variable, send the request format your provider documents, check the HTTP status, and handle the response according to its documented content type.
Screenshot APIs are not interchangeable. Endpoint paths, authentication headers, parameter names, supported options, and response formats differ. The example below uses the documented contract for Screenshot API: a JSON POST request authenticated with a bearer token that returns JSON containing screenshotUrl. Treat its fields as provider-specific, not as a universal API format.
1. Install requests and set your API key
Use a virtual environment if this is a project dependency, then install requests:
python -m venv .venv
source .venv/bin/activate
python -m pip install requests
On Windows PowerShell, activate the environment with .venv\Scripts\Activate.ps1. Set the key outside your source code. For example, in a Unix-like shell:
export SCREENSHOT_API_KEY='your_api_key'
In PowerShell:
$env:SCREENSHOT_API_KEY = 'your_api_key'
Do not commit keys to source control or print them in error logs. Prefer a secret manager for deployed applications. The provider recommends header-based authentication rather than placing credentials in a query string.
2. Make a complete screenshot request
This runnable script requests a full-page PNG of https://example.com at a 1280 by 720 viewport, checks for HTTP errors, parses the JSON response, and prints the returned screenshot URL:
import os
import requests
api_key = os.environ["SCREENSHOT_API_KEY"]
endpoint = "https://api.screenshot-api.org/api/v1/screenshot"
response = requests.post(
endpoint,
headers={"Authorization": f"Bearer {api_key}"},
json={
"url": "https://example.com",
"viewport": {"width": 1280, "height": 720},
"format": "png",
"fullPage": True,
},
timeout=30,
)
response.raise_for_status()
result = response.json()
print(result["screenshotUrl"])
Save it as screenshot.py and run python screenshot.py after setting the environment variable. The endpoint, bearer header, request fields, and JSON response shown here follow Screenshot API’s documentation. The timeout and status check are client-side safeguards. The example is an adaptation of the documented contract, not a claim that it has been executed.
For a provider that returns raw image bytes, the last steps differ: check the status and save response.content to a file. Do not call response.json() on a successful raw-image response. Always follow the selected provider’s response contract.
3. Choose the capture options your provider supports
For Screenshot API, the documented output formats are PNG, JPEG, WebP, and PDF. Its documented controls include viewport width and height, full-page capture, device scale factor, navigation wait strategy, image quality, element selection, waiting for a selector, delay after page load, dark mode, and blocking ads or cookie banners. Some advanced options are POST-only.
These names and capabilities belong to that provider’s API. Check its current documentation for accepted values, defaults, and plan limits before relying on an option. In particular:
- Viewport and full page: A viewport sets the browser’s visible width and height. Full-page capture requests the entire page, which can take longer and produce a larger file.
- Format and quality: PNG is lossless; JPEG and WebP may reduce file size, with quality settings applying where supported. Confirm whether quality is accepted for your chosen format.
- Wait behavior: A navigation wait strategy, selector wait, or post-load delay can help pages that render content asynchronously. Longer waits increase request duration.
- Element selection: Capture a specific element when you need a component rather than the whole page. A missing or late-rendering selector can cause the request to fail.
- Device scale factor and dark mode: Use a higher scale factor for denser output if supported. Enable dark mode only when you want the page rendered with its dark appearance.
- Blocking: Ad or cookie-banner blocking can change what appears in the result. Verify the behavior against your use case and the provider’s documented options.
Cloudflare offers a separate Browser Rendering screenshot operation at an account-scoped endpoint. Its documented controls include navigation waits, viewport, full-page capture, clipping, and image encoding; its API token requires an accepted permission such as Browser Rendering Write. This is a different integration and request contract, not a drop-in source of fields for the Screenshot API example. See the Cloudflare Browser Rendering documentation.
4. Handle errors and response formats
raise_for_status() raises an exception for unsuccessful HTTP status codes. For production code, catch request exceptions and show a useful, sanitized message:
import requests
try:
response = requests.post(
endpoint,
headers={"Authorization": f"Bearer {api_key}"},
json=payload,
timeout=30,
)
response.raise_for_status()
except requests.Timeout:
print("The screenshot request timed out")
except requests.HTTPError as exc:
# The body may contain a useful provider error; never log the API key.
detail = exc.response.text[:1000] if exc.response is not None else str(exc)
print(f"Screenshot API returned an HTTP error: {detail}")
except requests.RequestException as exc:
print(f"Could not reach the screenshot API: {exc}")
else:
print(response.headers.get("Content-Type"))
Use the actual response contract after the status check. Screenshot API documents JSON metadata with a screenshotUrl field. ScreenshotEngine documents successful responses containing raw image bytes and recommends inspecting Content-Type. A raw-byte client can save the body like this:
response.raise_for_status()
content_type = response.headers.get("Content-Type", "")
if not content_type.startswith("image/"):
raise ValueError(f"Expected an image response, got {content_type!r}")
with open("capture", "wb") as output:
output.write(response.content)
Choose a filename extension that matches the returned media type, or map the provider’s documented content types to extensions. For large responses, use stream=True and write chunks to disk rather than keeping the complete body in memory. Do not infer that an HTTP 200 response is always an image: some APIs return JSON or a PDF.
5. cURL and Node.js equivalents
These examples use Screenshot API’s documented POST endpoint, bearer token, JSON body, and JSON response. Set SCREENSHOT_API_KEY in the environment first.
cURL
curl --fail-with-body \
--request POST \
--url "https://api.screenshot-api.org/api/v1/screenshot" \
--header "Authorization: Bearer $SCREENSHOT_API_KEY" \
--header "Content-Type: application/json" \
--data '{"url":"https://example.com","viewport":{"width":1280,"height":720},"format":"png","fullPage":true}'
Node.js
const apiKey = process.env.SCREENSHOT_API_KEY;
if (!apiKey) throw new Error("Set SCREENSHOT_API_KEY first");
const response = await fetch(
"https://api.screenshot-api.org/api/v1/screenshot",
{
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://example.com",
viewport: { width: 1280, height: 720 },
format: "png",
fullPage: true,
}),
signal: AbortSignal.timeout(30_000),
},
);
if (!response.ok) {
throw new Error(`Screenshot API returned ${response.status}: ${await response.text()}`);
}
const result = await response.json();
console.log(result.screenshotUrl);
6. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
401 |
Missing, malformed, or invalid API key. | Confirm the environment variable is set and the authorization header uses the provider’s required scheme. Rotate a key if it was exposed. |
400 |
Invalid JSON, unsupported field, or malformed target URL. | Validate the URL and request body against the provider’s current API reference. Do not mix option names from another service. |
422 for a selector request |
The requested element was not found, possibly because it appeared late or the selector is wrong. | Check the selector against the rendered page and use a supported selector wait if available. |
429 |
Rate limit or monthly quota reached. | Read response headers and account usage, reduce request volume, and follow the provider’s retry guidance. Avoid immediate retry loops. |
502 |
The rendering service could not capture the page. | Check whether the target loads in a normal browser, simplify options, and retry only when appropriate under the provider’s guidance. |
| JSON parsing error | The response is raw image bytes, an error body, or another format. | Check status and Content-Type first; parse JSON only when the documented response is JSON. |
| Timeout | The site is slow, the capture waits too long, or the client timeout is too short. | Set a finite timeout appropriate to your workflow, avoid unnecessarily long waits, and handle timeouts as retryable only when the provider’s guidance permits. |
| Blank or incomplete capture | The page needs more time, lazy-loads content, or requires interaction/authentication. | Use a documented wait strategy or selector, and confirm the provider supports the page behavior and required credentials. |
Screenshot API’s documentation lists 401, 400, 429, 502, and 422 for the cases above. It states that its free plan allows 60 requests per minute and 500 screenshots per month and that response headers expose rate-limit and quota information. Treat these as that provider’s published plan details; check the current docs before deployment.
7. Reliability, performance, and cost
- Use bounded timeouts: A request without a timeout can wait indefinitely. Set one that fits the expected render time and handle timeout exceptions.
- Retry selectively: Authentication and invalid-request errors need a fix, not a retry. For throttling or transient rendering failures, use the provider’s retry guidance and any rate-limit headers. Do not assume retries are free.
- Control concurrency: Parallel captures can reduce batch time but may exceed account rate limits. Keep concurrency bounded and monitor quota headers or usage data.
- Reduce output where possible: A smaller viewport, selected element, or compressed format can reduce transfer and storage, if supported and suitable for the task. Full-page captures can be larger and slower.
- Watch quota and billing: A request may consume a screenshot quota even if your application later fails to save or process the result. Confirm billing semantics, retention, and retry charging in the chosen provider’s documentation.
- Protect sensitive pages: If a capture needs cookies, authorization, or private URLs, follow the provider’s security guidance and avoid logging credentials or sensitive response content.
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. A single GET request returns a PNG, JPEG, WebP, or PDF; the supported parameter names other screenshot APIs use also work, which can make switching easier. See the ScreenshotNeo API documentation.
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)
For this GET endpoint, the response body is written directly to a file. ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers say which page verdict occurred and whether it was billed. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.
9. FAQ
Does Python render the page?
No. requests sends the HTTP call. The screenshot provider runs the browser-rendering work.
Can I use GET instead of POST?
Some providers document both GET and POST, and Screenshot API documents a basic GET request. Use POST when the provider requires or recommends a JSON body for structured or advanced options.
Why does one screenshot API return JSON and another return bytes?
Each service defines its own response contract. One may return metadata and a hosted image URL; another may return the image itself. Check status and content type, then follow that API’s docs.
Can I use this endpoint with another provider’s API key?
No. Endpoint, authentication, request fields, and response handling are provider-specific. Change the full integration to match the provider you select.


