Site-Shot

Tutorial ·Apr 4, 2026 ·7 min read

How to Take a Website Screenshot with PHP

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_contents with allow_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_code or user_agent value 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.

← All articles