Screenshot API · PHP

Screenshot API for PHP.

Shotium is a hosted screenshot API you call from PHP with the cURL extension you already have: no headless Chrome on the server, no chrome-php process management. Send a URL, get back PNG, JPEG or WebP bytes — with full-page capture, template-based OG images, and HMAC-signed URLs you can embed in public HTML.

How do I take a screenshot in PHP?

One request with the cURL extension — no Composer packages required. Sign in, create an API key, and this runs as-is:

<?php
$ch = curl_init(
    'https://api.shotium.com/v1/screenshot?' . http_build_query([
        'url' => 'https://example.com',
        'format' => 'png',
    ])
);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SHOTIUM_KEY')],
    CURLOPT_TIMEOUT => 60,
]);
$image = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) !== 200) {
    throw new RuntimeException('render failed');
}
file_put_contents('shot.png', $image);

Full-page screenshots and viewport control

Set full_page to capture the entire scroll height (up to 20,000px). Pass the string 'true' — http_build_query turns PHP booleans into 1 and 0:

$query = http_build_query([
    'url' => 'https://news.ycombinator.com',
    'full_page' => 'true',
    'format' => 'webp',
    'quality' => 90,
    'width' => 1280,
]);
$ch = curl_init("https://api.shotium.com/v1/screenshot?$query");

Generate OG images from templates

POST typed parameters into one of five built-in templates and get a finished 1200×630 social card back — no HTML to write:

$ch = curl_init('https://api.shotium.com/v1/og-image');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('SHOTIUM_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'template' => 'blog',
        'params' => ['title' => 'Shipping fast without breaking things', 'author' => 'Ada L.'],
        'format' => 'png',
    ]),
    CURLOPT_TIMEOUT => 60,
]);
$image = curl_exec($ch);
file_put_contents('og.png', $image);

How do I embed OG images without exposing my API key?

Sign the query string with your signing secret instead of sending your key. PHP's rawurlencode is already RFC 3986, and ksort with SORT_STRING gives byte-order keys — both match the server's canonical form exactly:

$params = ['template' => 'minimal', 'title' => 'Less, but better.', 'uid' => 'YOUR-UID'];
ksort($params, SORT_STRING);

$pairs = [];
foreach ($params as $k => $v) {
    $pairs[] = rawurlencode($k) . '=' . rawurlencode($v);
}
$canonical = implode('&', $pairs);

$sig = hash_hmac('sha256', $canonical, getenv('SHOTIUM_SIGNING_SECRET'));
$url = "https://api.shotium.com/v1/og-image?$canonical&sig=$sig";

Error handling

Every error is RFC 9457 problem+json with a stable type field you can branch on. Failed renders are never billed:

$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($code !== 200) {
    $problem = json_decode($body, true); // RFC 9457 problem+json
    if ($problem['type'] === 'rate_limited') {
        // honor the Retry-After header, then retry
    } elseif ($problem['type'] === 'render_timeout') {
        // site took >30s — retry or skip
    }
}

Parameters at a glance

ParamDefaultNotes
urlrequiredhttp(s) URL to render
width / height1280 × 800Viewport, up to 3840 × 2160
full_pagefalseFull scroll height, ≤20,000px
formatpngpng | jpeg | webp
quality801–100, lossy formats only

Full reference — auth, OG templates, rate limits, error table — in the docs. Template parameters live on /og-templates.

Frequently asked questions

Do I need a headless browser to take screenshots in PHP?

No. Packages like chrome-php or Panther need Chrome installed and supervised on your server. Shotium runs the browsers server-side — from PHP it's one call with the cURL extension you already have.

How do I take a full-page screenshot in PHP?

Pass full_page => 'true' through http_build_query — use the string, since PHP encodes booleans as 1/0 and the API expects true. The renderer scrolls, waits for lazy-loaded content, and captures up to 20,000px; add format => 'webp' to keep tall pages small.

Does the Shotium API work with Laravel or Symfony?

Yes — it's plain HTTPS, so Laravel's Http facade, Symfony HttpClient and Guzzle all work unchanged. The examples use the raw cURL extension only to stay dependency-free.

How should PHP code react to a failed render?

Read CURLINFO_RESPONSE_CODE before writing the body to disk — curl_exec returns the error JSON just as happily as image bytes. A non-200 body is RFC 9457 problem+json: rate_limited means honor Retry-After, render_timeout means the target took over 30 seconds. Failed renders are never billed.

Try it on your own pages

Sign-up is GitHub OAuth and comes with 100 free render credits — no card. Failed renders never bill. Plans from $15/month on pricing.

Also available for: Python · Node.js · Ruby · Go · Java · cURL