PHP is one of the most popular languages for web development, and capturing website screenshots is a common task for CMS plugins, monitoring tools, and link preview generators. This guide shows you how to take a screenshot of any website using PHP and the Site-Shot API.
There is no composer require line in this post, and that is deliberate: Site-Shot's official SDKs cover Node.js and Python only. In PHP you call the HTTP API directly — and because the API is a single GET that returns the image bytes, the cURL block below is not a workaround for a missing library, it is the entire integration.
Prerequisites
- PHP 7.4+ with the cURL extension enabled (
file_get_contentswithallow_url_fopen, or Guzzle, work just as well) - A Site-Shot API key — API plans start at $5/mo for 2,000 screenshots (pricing). The free browser tool at site-shot.com needs no signup and no key, but it's a browser tool, not an API tier
Basic Screenshot Capture
<?php
$params = http_build_query([
'url' => 'https://example.com',
'userkey' => 'YOUR_API_KEY',
'width' => 1280,
'height' => 1024,
'format' => 'png',
]);
$ch = curl_init("https://api.site-shot.com/?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 70);
$image = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200 && $image) {
file_put_contents('screenshot.png', $image);
echo "Screenshot saved.\n";
} else {
echo "Error: HTTP {$httpCode}\n";
}
The API renders the page in a real Chromium browser and returns the raw image bytes. Save them to a file or serve them straight to the browser.
Keep the key out of the source file in anything you deploy — read it from the environment with getenv('SITESHOT_API_KEY'), or from a constant defined in wp-config.php or your framework's config. The rest of this post does that.
Full Page Screenshot
To capture the entire scrollable page rather than just the viewport, ask for full_size and cap the result with max_height (up to 20,000 pixels):
$params = http_build_query([
'url' => 'https://example.com',
'userkey' => getenv('SITESHOT_API_KEY'),
'full_size' => 1,
'max_height' => 15000,
'format' => 'png',
]);
The API scrolls through the whole document before capturing, which also triggers lazy-loaded images.
Screenshot from a Specific Country
Pass a two-letter ISO 3166-1 country code and the API routes the render through an IP in that country, with a matching language, time zone, and geolocation:
$params = http_build_query([
'url' => 'https://whatismycountry.com',
'userkey' => getenv('SITESHOT_API_KEY'),
'country' => 'DE',
'strict_country' => 1,
'no_ads' => 1,
'no_cookie_popup' => 1,
]);
Use ISO codes only — country=DE, not the country's full name, which is not a valid value and silently renders from the US instead. The same silent US fallback happens when the requested country has no capacity at that moment; strict_country=1 turns that into an honest failure, returned in-band as country_unavailable in the JSON body rather than an image. Country routing is included with any paid plan.
JSON, Metadata and Rendered HTML
When you need more than the image — the target's HTTP status, its response headers, or the HTML as rendered — ask for the JSON result with response_type=json:
<?php
$params = http_build_query([
'url' => 'https://example.com',
'userkey' => getenv('SITESHOT_API_KEY'),
'response_type' => 'json',
'source_code' => 1,
]);
$ch = curl_init("https://api.site-shot.com/?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 70);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (!empty($data['error'])) {
die("API error: " . $data['error']);
}
echo $data['response']['status_code'], "\n"; // 200
print_r($data['response']['headers']); // [['name' => ..., 'value' => ...], ...]
echo substr($data['source_code'], 0, 200), "\n"; // rendered HTML
$base64 = explode(',', $data['image'], 2);
file_put_contents('screenshot.png', base64_decode(end($base64)));
The envelope carries image (a base64 data URL), response (the target's status_code and headers), screenshot_parameters (every value the renderer actually used), and source_code when you ask for it. A failed capture arrives in-band as a JSON body with an error field — country_unavailable, for instance — which is why that check comes before the decode.
Serving Screenshots in a Web Application
Generate and serve a thumbnail screenshot directly to the browser:
<?php
$targetUrl = $_GET['url'] ?? '';
if (empty($targetUrl)) {
http_response_code(400);
die('Missing url parameter');
}
$params = http_build_query([
'url' => $targetUrl,
'userkey' => getenv('SITESHOT_API_KEY'),
'width' => 1280,
'height' => 800,
'format' => 'jpeg',
'scaled_width' => 400,
]);
$ch = curl_init("https://api.site-shot.com/?{$params}");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 70);
$image = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200 && $image) {
header('Content-Type: image/jpeg');
header('Cache-Control: public, max-age=3600');
echo $image;
} else {
http_response_code(502);
die('Screenshot failed');
}
Cache the result, and don't let arbitrary visitor input reach the API unchecked — every request that gets through is a render against your quota, and the capture takes seconds, not milliseconds.
WordPress Integration
For a plugin or theme, wrap the call in a helper that uses WordPress's own HTTP layer and reads the key from a constant in wp-config.php:
function site_shot_capture($url, $options = []) {
$defaults = [
'url' => $url,
'userkey' => defined('SITE_SHOT_API_KEY') ? SITE_SHOT_API_KEY : '',
'width' => 1280,
'height' => 1024,
'format' => 'png',
];
$params = http_build_query(array_merge($defaults, $options));
$response = wp_remote_get(
"https://api.site-shot.com/?{$params}",
['timeout' => 70]
);
if (is_wp_error($response)) {
return false;
}
return wp_remote_retrieve_body($response);
}
// Usage:
$image = site_shot_capture('https://example.com', ['full_size' => 1]);
if ($image) {
file_put_contents('/tmp/screenshot.png', $image);
}
A render can take tens of seconds, so call this from a background job (WP-Cron, Action Scheduler) rather than during a page load, and store the result in the media library instead of re-capturing on every request.
Where the honest limits are
- There is no official PHP SDK. Site-Shot's two official SDKs are Node.js (
npm install site-shot-sdk) and Python (pip install site-shot); in PHP — as in Go, Ruby, Java and C# — you call the same HTTP endpoint directly. There is no Composer package to look for, and the cURL block above is the whole integration. - The API always needs a key. The no-signup path is the browser tool, not the API.
- Options travel in the query string. A very long
javascript_codeoruser_agentvalue can push the URL past practical length limits of about 8 KB. - Images only. Site-Shot returns PNG or JPEG — no PDF and no video output.
- A capture is not instant. Renders take seconds; a 70-second cURL timeout is a realistic setting, which is why the web examples above cache and the WordPress one belongs in a background job.
Parameter Reference
| Parameter | Description | Example |
|---|---|---|
url |
Target web page | https://example.com |
userkey |
Your API key | abc123 |
width |
Viewport width (100–8000) | 1280 |
height |
Viewport height (100–20000) | 1024 |
full_size |
Capture full page | 1 |
max_height |
Height cap for full-page captures | 15000 |
format |
Output format | png or jpeg |
scaled_width |
Resize output image | 400 |
delay_time |
Wait before capture (ms) | 2000 |
response_type |
Response format | image or json |
source_code |
Include the rendered HTML | 1 |
country |
Proxy country (two-letter ISO code) | DE |
strict_country |
Fail instead of falling back to the US | 1 |
no_ads / no_cookie_popup |
Strip ads / cookie banners | 1 |
Check the full API documentation for all available parameters. For scheduled captures without code, see automatic daily screenshots; for the viewport-vs-full-page decision, see full page vs viewport screenshots; for country routing in depth, see screenshots from another country.
FAQ
Is there an official PHP SDK for Site-Shot?
No. Site-Shot's official SDKs cover Node.js (npm install site-shot-sdk) and Python (pip install site-shot) only — there is no Composer package, and in PHP you call the HTTP API directly. That is a single GET to https://api.site-shot.com/ carrying your userkey and options as query parameters, which PHP's cURL extension (or file_get_contents, or Guzzle) handles in a few lines, and the API returns the image bytes.
Do I need an API key to take website screenshots with PHP?
For the Site-Shot API, yes — every request carries your userkey, and keys come with any paid plan (from $5/mo for 2,000 screenshots). For a quick one-off capture without code, the free in-browser tool at site-shot.com needs no signup and no key at all.
How do I capture a full-page screenshot in PHP?
Add full_size=1 to the request and cap the height with max_height (up to 20,000 pixels). The API scrolls the whole document — triggering lazy-loaded content — and returns one tall image.
Can I schedule these captures to run automatically?
Yes, two ways: run your PHP script from cron or CI, or skip the code entirely — Site-Shot's built-in scheduled captures re-photograph a URL on a cadence you choose and file every shot in your library, included with any paid plan. See automatic daily screenshots.
Capture your first screenshot free in your browser — no signup — at site-shot.com, or compare API plans on the pricing page.