How to Capture a Full-Page Screenshot with ApiFlash in Python
Capture and save a full-page website screenshot with ApiFlash in Python. Learn the request options, page readiness controls, and common error fixes.
To capture an entire webpage with ApiFlash in Python, send a request to https://api.apiflash.com/v1/urltoimage with your access key, a complete target URL, and full_page=true. ApiFlash returns image bytes by default; save the response body in binary mode. With full-page capture enabled, the height setting is ignored. ApiFlash Screenshot API documentation
Capture a full page with Python
This standard-library example uses GET and URL-encodes the query parameters. Replace the key and target URL before running it. It writes the default JPEG response to screenshot.jpeg.
from urllib.parse import urlencode
from urllib.request import urlopen
endpoint = "https://api.apiflash.com/v1/urltoimage"
params = {
"access_key": "YOUR_ACCESS_KEY",
"url": "https://example.com",
"full_page": "true",
}
request_url = f"{endpoint}?{urlencode(params)}"
with urlopen(request_url, timeout=90) as response:
image_bytes = response.read()
with open("screenshot.jpeg", "wb") as image_file:
image_file.write(image_bytes)
print("Saved screenshot.jpeg")
Keep the access key private: do not commit it to source control or expose it in client-side code. Load it from an environment variable or secret manager in a deployed application. ApiFlash says the key is obtained from its user dashboard and included with each API call.
Python with requests and basic error reporting
If your project already uses requests, this version makes the same GET request and reports HTTP failures before writing the response. Install the dependency with python -m pip install requests.
import os
import requests
endpoint = "https://api.apiflash.com/v1/urltoimage"
params = {
"access_key": os.environ["APIFLASH_ACCESS_KEY"],
"url": "https://example.com",
"full_page": "true",
}
try:
response = requests.get(endpoint, params=params, timeout=90)
response.raise_for_status()
except requests.Timeout as exc:
raise SystemExit(f"Capture timed out: {exc}")
except requests.HTTPError as exc:
# The response body may contain details about invalid parameters or capture errors.
print("ApiFlash response:", response.text[:1000])
raise SystemExit(f"ApiFlash returned HTTP {response.status_code}") from exc
with open("screenshot.jpeg", "wb") as image_file:
image_file.write(response.content)
print("Saved screenshot.jpeg")
ApiFlash also accepts POST form data. GET is convenient for a short request; POST can keep a long parameter set out of the URL. The examples here use GET, which is the documented endpoint pattern. Avoid logging request URLs because they contain the access key.
Equivalent cURL request
curl -G "https://api.apiflash.com/v1/urltoimage" \
-d access_key=YOUR_ACCESS_KEY \
--data-urlencode url=https://example.com \
-d full_page=true \
-o screenshot.jpeg
Equivalent Node.js request
This Node.js example uses built-in fetch. It checks the HTTP status and writes the binary response to a file.
import { writeFile } from 'node:fs/promises';
const params = new URLSearchParams({
access_key: process.env.APIFLASH_ACCESS_KEY,
url: 'https://example.com',
full_page: 'true',
});
const response = await fetch(
`https://api.apiflash.com/v1/urltoimage?${params}`,
{ signal: AbortSignal.timeout(90000) }
);
if (!response.ok) {
const detail = await response.text();
throw new Error(`ApiFlash returned HTTP ${response.status}: ${detail}`);
}
await writeFile('screenshot.jpeg', Buffer.from(await response.arrayBuffer()));
console.log('Saved screenshot.jpeg');
Choose capture and output options
The endpoint accepts ordinary HTTP parameters. The most relevant choices for a full-page capture are summarized here; check the official documentation for the current parameter list and plan availability.
| Need | Setting or behavior | Notes |
|---|---|---|
| Entire document | full_page=true |
This is the setting that requests a full-page image. height is ignored in this mode. |
| Target page | url |
Supply a complete URL with http:// or https://. URL encoding is important in a GET query string; the Python examples handle it. |
| Image format | format |
Supported formats listed in the documentation are jpeg (the default), png, and webp. |
| JPEG or WebP size/quality tradeoff | quality |
Use this for quality control when choosing JPEG or WebP. Consult the API documentation for accepted values. |
| Wait for page readiness | wait_for, wait_until |
The default wait is network_idle. A selector wait can fail if its element does not appear within 15 seconds. |
| Trigger lazy content | scroll_page=true |
ApiFlash describes scrolling through the page before capture to trigger animations or lazy-loaded elements. |
| Return metadata or links | response_type=json |
Changes the response shape from the default raw image data to JSON. |
| Bypass or tune caching | fresh=true, ttl |
fresh requests a fresh capture; ttl controls cache duration. |
| Viewport width | width |
Controls the viewport width. In full-page mode, setting the height does not limit the captured document. |
Set the response format deliberately
The raw image response is simplest when the goal is a file. For example, add "format": "png" to the Python parameter dictionary and save to screenshot.png. Use PNG when preserving sharp edges or avoiding lossy compression matters; use JPEG or WebP when a smaller image is more useful. If you set response_type=json, do not write the response body directly as an image: parse the JSON according to the documented response structure instead.
Make full-page captures reliable
- Start with a known public URL. Confirm it loads in a browser and include its scheme. Private pages may require access behavior that the endpoint can support only if documented for your plan and request.
- Decide when the page is ready. The default is
network_idle. Pages with analytics, polling, or long-running requests may not reach network idle in a useful time. The documentation recommendswait_fororwait_untilwhen a more reliable page-ready condition is needed than an arbitrary delay. - Wait for meaningful content. If the main content appears after initial load, wait for a stable CSS selector with
wait_for. The capture aborts with an error if the selector does not appear within 15 seconds, so choose an element that reliably exists on the target. - Consider lazy-loaded sections. Set
scroll_page=truewhen content or images load as the page is scrolled. Without it, lower sections may be present as blank placeholders in the resulting image. - Inspect the result. Check the top, middle, and bottom of long captures for missing content, sticky overlays, or unexpected dimensions. Fix the readiness or scrolling condition before increasing concurrency.
Full-page capture asks the service to render a tall document, so a successful HTTP response alone does not prove every deferred widget or image finished loading. A deliberate readiness condition and a visual check of representative output are useful when capture quality matters.
Handle errors and quota limits
| HTTP status | Likely cause | What to do |
|---|---|---|
| 400 | Invalid parameters or a target URL ApiFlash cannot capture. | Check parameter names and values, URL-encode the target, confirm it includes a scheme, and open the target independently. |
| 401 | Invalid or revoked access key. | Copy the current key from the dashboard and verify the application is loading the intended secret. |
| 402 | Monthly quota exhausted. | Check usage and the plan quota; reduce unnecessary captures or change the plan if needed. |
| 403 | The plan does not support a requested feature. | Remove that option or confirm the plan supports it. |
| 429 | Too many requests. | Reduce request concurrency and retry with backoff. ApiFlash documents a leaky-bucket limit of 20 requests per second with a burst size of 400; requests beyond the burst can be terminated with 429. |
| Successful status, unusable image | The page was not ready, lazy content did not load, or the response type was changed. | Check the output format and response type, add a suitable readiness condition, or enable scrolling for lazy content. |
ApiFlash documents quota headers on successful responses and provides a quota endpoint. Its FAQ says cached screenshots and failed captures do not count toward monthly quota. The API documentation also says identical failed capture attempts are rate-limited to five per hour. Use these details when designing retry behavior: a repeated request is not always a productive retry.
Performance, reliability, and cost considerations
- Request time: Full pages take more rendering work than viewport images, especially when they contain many sections or deferred assets. Set a client timeout appropriate to your workflow and handle timeout separately from HTTP errors.
- Throughput: Keep parallel requests within the documented rate limit. Use bounded concurrency and exponential backoff for 429 responses instead of launching immediate retries.
- Cache behavior: Reuse a cached result when the page has not changed and freshness is unimportant. Use
fresh=truewhen you need to bypass a cached screenshot, and setttlaccording to how often the target changes. - Quota: Track usage from response headers or the quota endpoint. The research dossier notes that failed captures and cache hits do not count toward quota according to ApiFlash’s FAQ, but confirm current account terms before building a cost forecast.
- Output size: JPEG and WebP quality settings can help manage image size. Select format based on whether fidelity, transparency needs, or storage and transfer size matter for your use case.
- Pricing: Plan prices and quotas change. The research dossier recorded a 2026-10-03 snapshot, but it is not included here as a current quote; check ApiFlash’s plans page before budgeting.
Or skip the browser setup
If you need a screenshot endpoint without wiring capture behavior into your own browser setup, ScreenshotNeo takes a URL in one GET request and returns PNG, JPEG, WebP, or PDF. Its API documentation covers the request options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.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://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned HTTP ${res.status}`);
await import('node:fs/promises').then(({ writeFile }) => writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed, and an MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for ScreenshotNeo free and get 1,000 screenshots a month with no card.
Frequently asked questions
Does full-page mode capture beyond the browser viewport?
Yes. Set full_page=true to request the whole page. The configured height is ignored in this mode.
Why is the bottom of my screenshot incomplete?
The page may load lower content only after scrolling, or the capture may begin before content is ready. Try scroll_page=true for lazy-loaded content and a selector or wait condition for readiness.
Can I get JSON instead of image bytes?
Yes. Set response_type=json. The default is image data; JSON changes the response shape, so handle it as JSON rather than saving it as an image.
Can I control the screenshot’s image type?
The documented formats are JPEG (the default), PNG, and WebP. Set the format in the request and use a matching output filename.
Can I safely retry a failed capture?
Retry transient timeouts or rate limits with a delay and bounded backoff. First correct invalid parameters, keys, or inaccessible target URLs; identical failed attempts are subject to a documented rate limit.


