Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use PHP to call a hosted screenshot API, then save the returned image or PDF. The request normally contains a target URL and credentials; the response is binary PNG, JPEG, WebP, or PDF data. You can integrate through a Composer SDK when a provider supplies one, or use PHP’s cURL extension for a provider-neutral REST integration. This guide shows both approaches, explains the options that matter, and provides a complete ScreenshotNeo request you can run immediately.

What a PHP screenshot API does

A screenshot API runs a browser in the provider’s infrastructure. Your PHP application sends a URL and capture settings; the service loads the page, renders JavaScript, and returns an image or PDF. Your code can write those bytes to disk, stream them to a user, attach them to a report, or store them in object storage.

This is different from taking a screenshot of the server running PHP. A normal PHP process has no visual browser and cannot render modern pages by itself. A hosted API supplies the browser and its fonts, JavaScript engine, networking, and rendering environment.

What you need

  • PHP with the cURL extension enabled (or a Composer HTTP client).
  • An account and credential for the provider.
  • A URL that the provider can reach. Private intranet pages require supported headers, cookies, authentication, or network access.
  • A writable destination if you intend to save the result.

The provider-neutral PHP implementation

The following function uses a GET request and saves the response. Replace the endpoint, parameter names, and authentication method with those in your provider’s current documentation. Do not assume that options or limits are interchangeable between services.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Minimal cURL function

<?php

function captureScreenshot(string $endpoint, array $query, string $outputPath): void
{
    $ch = curl_init($endpoint . '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986));
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_CONNECTTIMEOUT => 15,
        CURLOPT_TIMEOUT => 90,
        CURLOPT_HTTPHEADER => ['Accept: image/png,image/jpeg,image/webp,application/pdf'],
    ]);

    $body = curl_exec($ch);
    if ($body === false) {
        $error = curl_error($ch);
        curl_close($ch);
        throw new RuntimeException('Transport error: ' . $error);
    }

    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?: '';
    curl_close($ch);

    if ($status < 200 || $status >= 300) {
        throw new RuntimeException("Screenshot API returned HTTP {$status}: {$body}");
    }
    if ($body === '' || (stripos($contentType, 'image/') === false && stripos($contentType, 'application/pdf') === false)) {
        throw new RuntimeException('The response was not an image or PDF. Check the endpoint and credentials.');
    }
    if (file_put_contents($outputPath, $body) === false) {
        throw new RuntimeException('Could not write ' . $outputPath);
    }
}

captureScreenshot(
    'https://api.example.com/screenshot',
    ['api_key' => getenv('SCREENSHOT_API_KEY'), 'url' => 'https://example.com', 'format' => 'png'],
    __DIR__ . '/shot.png'
);

Keep the key in an environment variable or secret manager, not in source control or a browser-delivered page. Validate or allow-list user-supplied URLs to reduce server-side request forgery risk, and reject schemes such as file: unless your provider explicitly supports them.

POST requests and JSON options

Advanced settings are often POST-only. A typical pattern is:

$payload = [
    'url' => 'https://example.com',
    'format' => 'webp',
    'full_page' => true,
    'wait_until' => 'networkidle',
];

$ch = curl_init('https://api.example.com/screenshot');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($payload, JSON_THROW_ON_ERROR),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'Authorization: Bearer ' . getenv('SCREENSHOT_API_KEY'),
    ],
    CURLOPT_TIMEOUT => 90,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$err = curl_error($ch);
curl_close($ch);
if ($body === false || $status < 200 || $status >= 300) {
    throw new RuntimeException($err ?: "HTTP {$status}: {$body}");
}
file_put_contents(__DIR__ . '/shot.webp', $body);

Authentication differs: one service may use query parameters, another an x-api-key header, and another access plus secret keys. Follow the selected provider’s documentation exactly.

Composer SDKs: when they help

Several providers publish Composer packages, including screenshotone/sdk, screenshotmachine/screenshotmachine-php, and screenshotapi/sdk. Install the package with Composer, create its client with your credentials, set the URL and capture options, then either generate a request URL or download the returned bytes. Package names, PHP versions, method names, and dependencies change, so confirm the current README and lock a tested version in composer.lock.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The ScreenshotAPI package listing describes PHP 8.1+ and an API key sent in an x-api-key header; treat those requirements as version-sensitive and verify them before deployment. An SDK can provide typed options and URL encoding, but direct cURL is often easier to audit and keeps your integration independent of a vendor’s release cycle.

Capture options to decide before coding

Providers expose different names and capabilities. Build your request around the output your application actually needs.

Requirement Questions to answer
Viewport or full page Do you need only the visible viewport, or the entire document including content below the fold? Full-page mode may require lazy images to be loaded.
Output PNG preserves detail and transparency; JPEG is smaller for photographs; WebP can reduce size; PDF is appropriate for documents. Confirm which formats and page controls are supported.
Timing Can the API wait for a selector, a fixed delay, or network idle? A premature capture produces missing charts and images.
Rendering context Do you need a viewport preset, custom width and height, device pixel ratio (retina), dark mode, timezone, or geolocation?
Page changes Can you run JavaScript, click an element, inject CSS, hide selectors, or block ads, trackers, requests, and resource types?
Access control Are custom headers, cookies, a user agent, or an Authorization header required for the page?
Scale Do you need caching with a chosen TTL, asynchronous jobs and signed webhooks, bulk capture, usage reporting, or signed links for public <img> tags?

Geolocation, batch capture, PDF settings, selectors, and output formats are provider-specific. A feature shown by one vendor is not evidence that another supports it. Compare PHP requirements, dependencies, authentication, current limits, pricing, and reliability before committing.

ScreenshotNeo: a PHP-ready API

ScreenshotNeo provides a GET endpoint at https://api.screenshotneo.com/v1/shot. It returns PNG, JPEG, WebP, or PDF. The service accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and whether the request was billed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

PHP example

Save this as capture.php, set YOUR_API_KEY, and change the URL. The full parameter reference is in the ScreenshotNeo documentation.

<?php

$target = 'https://stripe.com';
$params = [
    'access_key' => getenv('SCREENSHOTNEO_API_KEY') ?: 'YOUR_API_KEY',
    'url' => $target,
];

$url = 'https://api.screenshotneo.com/v1/shot?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_CONNECTTIMEOUT => 15,
    CURLOPT_TIMEOUT => 90,
]);
$bytes = curl_exec($ch);
if ($bytes === false) {
    throw new RuntimeException(curl_error($ch));
}
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$verdict = curl_getinfo($ch, CURLINFO_HEADER_OUT);
curl_close($ch);
if ($status !== 200) {
    throw new RuntimeException("ScreenshotNeo returned HTTP {$status}");
}
file_put_contents(__DIR__ . '/shot.webp', $bytes);

For production logging, capture response headers such as X-Page-Verdict and X-Billed with a header callback; these tell you whether the page was considered clean and whether it consumed a shot. Choose the output format, viewport, full-page behavior, wait condition, selectors, custom CSS or JavaScript, blocking rules, cookies, headers, timezone, geolocation, PDF paper and margins, resizing, caching TTL, or asynchronous delivery using the documented parameters.

Equivalent requests in cURL, Python, and Node.js

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
require('fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));

Scaling and reliability

Make captures deterministic

  • Set an explicit viewport and output format rather than relying on defaults.
  • Wait for a meaningful selector or network idle when JavaScript builds the page.
  • Use full-page mode only when needed; it can trigger additional image loading.
  • Hide timestamps, rotating banners, and animations with custom CSS when consistent visual diffs matter.
  • Use a cache TTL for repeated, unchanged URLs and store the resulting URL or bytes with your own metadata.

Handle failures safely

Set both connect and total timeouts. Retry transient transport errors with capped exponential backoff, but do not blindly retry authentication failures or deterministic 4xx responses. For asynchronous jobs, verify signed webhook requests before accepting a result. Record the target URL, non-secret option set, HTTP status, provider request identifier if supplied, verdict, billed flag, and elapsed time.

Control cost

Batch endpoints can reduce request overhead when a provider supports them; the reviewed REST documentation describes a POST batch endpoint, while the permitted batch size and pricing must be confirmed in the live documentation. Caching, resizing, and selecting WebP can reduce storage and bandwidth. Keep a per-user quota so an exposed endpoint cannot generate unlimited captures.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common errors and fixes

401 or 403 authentication errors

Check the key name, header versus query authentication, account status, and whether the key is being sent to the correct regional or versioned endpoint. Never print secrets in exception messages.

400 invalid URL or option

URL-encode query values, use an absolute URL with a scheme, and remove options unsupported by that provider or HTTP method. Advanced options are frequently POST-only.

Timeouts or blank images

The page may depend on slow JavaScript, a blocked third-party resource, a bot challenge, or an unavailable private network. Increase the documented wait or timeout within service limits, wait for a selector, block nonessential resources, or provide required headers and cookies. A hosted API cannot bypass a page that requires an inaccessible network.

Missing images or below-the-fold content

Enable full-page capture and lazy-image loading if offered. Wait for the image selector or network idle, and ensure the page is not hiding content behind a click or consent dialog.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Corrupt output

Inspect HTTP status and Content-Type before writing bytes. Error responses are often JSON or HTML, not image data. Save the response body separately while diagnosing.

PHP cURL unavailable

Enable the cURL extension in the PHP runtime used by the web server or use a Composer HTTP client. Verify with php -m in the same environment that runs the application.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Choosing among PHP screenshot APIs

Start with compatibility: supported PHP version, Composer dependencies, authentication model, and whether the provider offers a maintained SDK. Then verify the rendering controls your page needs—full page, formats, PDF, selectors, delays, geolocation, CSS, cookies, and custom headers. Finally compare current quotas, prices, latency, failure handling, cache behavior, and support. The available documentation demonstrates these feature dimensions but does not establish a neutral ranking of other vendors’ price, reliability, or performance.

For a clean default result, usage-aware billing, and an API that also serves AI workflows, ScreenshotNeo is the first service to try: it removes consent clutter before capture, bills only clean shots, and includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Its plans are Free (1,000 shots per month, no card), Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000; yearly billing gives two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to get 1,000 screenshots a month without a card.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Or skip the browser setup

Instead of installing Playwright or maintaining a browser worker, call ScreenshotNeo from PHP:

<?php
$q = http_build_query([
    'access_key' => 'YOUR_API_KEY',
    'url' => 'https://stripe.com',
], '', '&', PHP_QUERY_RFC3986);
$bytes = file_get_contents('https://api.screenshotneo.com/v1/shot?' . $q);
file_put_contents('shot.webp', $bytes);

Cookie banners, newsletter popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. The MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Sign up free.

Frequently Asked Questions

Can PHP take a webpage screenshot without a hosted API?

PHP alone does not provide a browser renderer. You can operate a separate browser automation service, but a hosted screenshot API is usually simpler to deploy and maintain.

Should I use PNG, JPEG, WebP, or PDF?

Use PNG for sharp UI or transparency, JPEG for photographic pages, WebP for compact web delivery, and PDF when the result is a document rather than an image.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How do I capture a page behind authentication?

Use a provider that supports custom headers, cookies, or Authorization and send only the minimum credentials needed. Confirm that the provider’s security and retention terms meet your requirements.

Why is my screenshot different on each request?

Animations, rotating content, timezones, fonts, ads, and asynchronous data can change rendering. Set a fixed viewport and timezone, wait for a selector or network idle, and inject CSS to disable motion where supported.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.