Need to capture website screenshots from PHP? Whether you're building a link preview tool, a website monitoring dashboard, or generating thumbnails for a CMS — this guide shows you the fastest way to go from URL to PNG image in PHP.
We'll cover the easy approach (REST API, works on any shared hosting) and the manual approach (headless Chrome via exec), so you can pick what fits your setup.
The simplest approach: call the URLSnap REST API using PHP's built-in file_get_contents or cURL. No browser, no extra software — works on shared hosting.
Register at urlsnap.dev to get your free API key (20 screenshots/day, no credit card required).
<?php
function takeScreenshot(string $url, string $apiKey, string $outputFile = 'screenshot.png'): bool {
$apiUrl = 'https://urlsnap.dev/api/screenshot?' . http_build_query([
'url' => $url,
'api_key' => $apiKey,
'width' => 1280,
'height' => 800,
'format' => 'png',
'full_page' => 'false',
]);
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 60,
]);
$image = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || !$image) {
error_log("Screenshot failed: HTTP $httpCode");
return false;
}
file_put_contents($outputFile, $image);
return true;
}
// Usage
$apiKey = 'YOUR_API_KEY';
$success = takeScreenshot('https://example.com', $apiKey, '/tmp/shot.png');
if ($success) {
echo "Screenshot saved!\n";
} else {
echo "Failed to capture screenshot.\n";
}
<?php
function takeScreenshotSimple(string $url, string $apiKey): ?string {
$apiUrl = 'https://urlsnap.dev/api/screenshot?' . http_build_query([
'url' => $url,
'api_key' => $apiKey,
'format' => 'png',
]);
$context = stream_context_create([
'http' => [
'timeout' => 60,
'ignore_errors' => true,
]
]);
$image = @file_get_contents($apiUrl, false, $context);
// Check HTTP response code
preg_match('/HTTP\/\d\.\d (\d+)/', $http_response_header[0] ?? '', $m);
$code = intval($m[1] ?? 0);
return ($code === 200 && $image) ? $image : null;
}
$apiKey = 'YOUR_API_KEY';
$image = takeScreenshotSimple('https://github.com', $apiKey);
if ($image) {
file_put_contents('github_shot.png', $image);
echo "Done! " . strlen($image) . " bytes\n";
}
To capture the entire page (scrolled height), add full_page=true:
$apiUrl = 'https://urlsnap.dev/api/screenshot?' . http_build_query([
'url' => 'https://example.com',
'api_key' => $apiKey,
'full_page' => 'true',
'width' => 1280,
]);
In a Laravel or WordPress context, save to your public uploads directory:
<?php
// Laravel example
use Illuminate\Support\Facades\Storage;
$image = takeScreenshot('https://example.com', $apiKey);
if ($image) {
$filename = 'screenshots/' . md5('https://example.com') . '.png';
Storage::disk('public')->put($filename, $image);
$url = Storage::url($filename);
echo "Accessible at: $url\n";
}
// WordPress example
$upload_dir = wp_upload_dir();
$filepath = $upload_dir['path'] . '/screenshot-' . time() . '.png';
file_put_contents($filepath, $image);
$url = $upload_dir['url'] . '/screenshot-' . time() . '.png';
URLSnap also supports URL → PDF. Same approach, different endpoint:
<?php
function urlToPdf(string $url, string $apiKey, string $outputFile = 'page.pdf'): bool {
$apiUrl = 'https://urlsnap.dev/api/pdf?' . http_build_query([
'url' => $url,
'api_key' => $apiKey,
'format' => 'A4',
'landscape' => 'false',
]);
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
]);
$pdf = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($code !== 200 || !$pdf) return false;
file_put_contents($outputFile, $pdf);
return true;
}
urlToPdf('https://urlsnap.dev', $apiKey, '/tmp/page.pdf');
echo "PDF saved!\n";
The free plan allows 20 requests/day. Check the response headers to stay informed:
<?php
$ch = curl_init($apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true); // Include headers in output
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
// Parse useful headers
preg_match('/X-Rate-Limit-Remaining: (\d+)/i', $headers, $m);
$remaining = intval($m[1] ?? -1);
preg_match('/X-Rate-Limit-Hint: (.+)/i', $headers, $h);
$hint = trim($h[1] ?? '');
if ($httpCode === 429) {
echo "Daily limit reached. Upgrade at urlsnap.dev/#pricing\n";
} elseif ($remaining >= 0 && $remaining < 5) {
echo "Warning: only $remaining free requests left today.\n";
if ($hint) echo $hint . "\n";
}
If you control your server and prefer not to use an external API, you can call Chromium directly from PHP. Note: this requires root/sudo access, significant RAM (~300 MB per tab), and won't work on shared hosting.
<?php
function takeScreenshotLocal(string $url, string $outputFile): bool {
// Requires Chromium installed: apt install chromium-browser
$safeUrl = escapeshellarg($url);
$safeOut = escapeshellarg($outputFile);
$cmd = "/usr/bin/chromium-browser "
. "--headless --no-sandbox --disable-gpu "
. "--screenshot=$safeOut "
. "--window-size=1280,800 "
. "$safeUrl 2>/dev/null";
exec($cmd, $output, $exitCode);
return $exitCode === 0 && file_exists($outputFile);
}
$ok = takeScreenshotLocal('https://example.com', '/tmp/local-shot.png');
echo $ok ? "Screenshot saved locally.\n" : "Failed.\n";
URLSnap API — Works on shared hosting, no setup, 20 free req/day, scales instantly.
Local Chromium — Free at scale but needs dedicated server, ops overhead, RAM-heavy.
Free tier — 20 requests/day, no credit card required.
Get Free API Key →Yes. The URLSnap API approach uses standard PHP HTTP functions — no shell access or special extensions needed. Local Chromium requires a VPS.
Absolutely. Use wp_remote_get() or plain cURL inside your plugin/theme. Save the image to the uploads directory and attach it to any post.
Not yet — but the API is simple enough that the code above is all you need. One function, one HTTP call.
PNG, JPEG (screenshot), and PDF. All configurable via query parameters.