Capture a web page as an image from PHP. Plain cURL, no Composer package required.
<?php
$params = http_build_query([
'url' => 'https://example.com',
'access_key' => getenv('SCREENSHOTLINE_KEY'),
'format' => 'png',
'viewport_width' => 1280,
'block_ads' => 'true',
'block_cookie_banners' => 'true',
]);
$ch = curl_init('https://api.screenshotline.com/take?' . $params);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status !== 200) {
$err = json_decode($body, true);
throw new RuntimeException($err['error']['message']);
}
file_put_contents('screenshot.png', $body);
| Parameter | Default | What it does |
|---|---|---|
| url | required | The page to capture. http/https only; private and reserved addresses are refused. |
| format | png | png, jpeg, webp or pdf. |
| full_page | false | Capture the whole scrollable page, scrolling first so lazy images load. |
| viewport_width | 1280 | Viewport width in pixels. |
| viewport_height | 800 | Viewport height in pixels. |
| selector | — | Capture only the element matching this CSS selector. |
| block_ads | false | Block ad networks and collapse the empty slot they leave behind. |
| block_cookie_banners | false | Remove consent banners, including ones injected after load. |
| color_scheme | light | Render the page as light or dark. |
| delay | 0 | Extra wait before capture, in ms. |
| cache | false | Serve a cached image when one exists. Cache hits are never billed. |
| fail_on_blank | false | Return 502 instead of an image when the capture looks blank. |
Failures return JSON with a stable code. Successful captures return the
image bytes directly, with no envelope.
{
"error": {
"code": "invalid_access_key",
"message": "Unknown or revoked API key."
}
}
Common codes: missing_access_key (401), invalid_access_key (403),
quota_exceeded (402), render_timeout (504),
blank_capture (502).
The headline use case is putting a capture straight into an <img src>. A raw
key in public HTML is a billing incident waiting to happen, so sign the URL instead: the
signature covers every parameter, and a tampered URL is refused.
node tools/sign.js "https://example.com" full_page=true
Set REQUIRE_SIGNATURE=true to refuse unsigned requests entirely.
Every response carries your position, so you never need a second request to check:
X-Quota-Limit: 2000
X-Quota-Used: 417
X-Quota-Remaining: 1583
X-Quota-Period: 2026-09