Screenshot API for PHP: Complete Integration Guide

Published April 2026 • 12 min read • PHP, Integration

PHP powers a substantial portion of the web, including a large fraction of SaaS platforms, CMS systems, and e-commerce applications. If you are building on Laravel, Symfony, or plain PHP and need screenshot, scraping, or PDF generation capabilities, SnapAPI provides a clean REST API that integrates in minutes. This guide covers everything from a basic cURL integration to a production-ready Laravel service class with response caching and background job handling.

Basic Integration with cURL

The simplest way to call SnapAPI from PHP is using the built-in cURL extension. The following example captures a screenshot and saves the result URL to a variable:

<?php
$apiKey = "YOUR_SNAPAPI_KEY";
$url = "https://example.com";

$ch = curl_init("https://snapapi.pics/api/screenshot");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Content-Type: application/json",
        "X-API-Key: " . $apiKey,
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "url" => $url,
        "full_page" => true,
        "format" => "png",
    ]),
]);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
echo "Screenshot URL: " . $data["url"];
?>

Using Guzzle HTTP Client

For Laravel and Symfony applications already using Guzzle, the integration is even cleaner:

use GuzzleHttp\Client;

$client = new Client(["base_uri" => "https://snapapi.pics"]);

$response = $client->post("/api/screenshot", [
    "headers" => [
        "X-API-Key" => config("services.snapapi.key"),
        "Content-Type" => "application/json",
    ],
    "json" => [
        "url" => "https://example.com",
        "full_page" => true,
        "format" => "png",
    ],
]);

$data = json_decode($response->getBody(), true);
$screenshotUrl = $data["url"];

Laravel Service Class Pattern

For production Laravel applications, wrapping SnapAPI in a dedicated service class gives you a clean interface for the rest of your application to consume:

<?php

namespace App\Services;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;

class SnapApiService
{
    private string $apiKey;
    private string $baseUrl = "https://snapapi.pics";

    public function __construct()
    {
        $this->apiKey = config("services.snapapi.key");
    }

    public function screenshot(string $url, array $options = []): string
    {
        $cacheKey = "screenshot:" . md5($url . serialize($options));

        return Cache::remember($cacheKey, now()->addMinutes(15), function () use ($url, $options) {
            $response = Http::withHeaders([
                "X-API-Key" => $this->apiKey,
            ])->post($this->baseUrl . "/api/screenshot", array_merge([
                "url" => $url,
                "full_page" => false,
                "format" => "png",
            ], $options));

            return $response->json("url");
        });
    }

    public function extract(string $url, array $fields): array
    {
        $response = Http::withHeaders([
            "X-API-Key" => $this->apiKey,
        ])->post($this->baseUrl . "/api/extract", [
            "url" => $url,
            "fields" => $fields,
        ]);

        return $response->json("data", []);
    }

    public function pdf(string $url): string
    {
        $response = Http::withHeaders([
            "X-API-Key" => $this->apiKey,
        ])->post($this->baseUrl . "/api/screenshot", [
            "url" => $url,
            "format" => "pdf",
            "full_page" => true,
        ]);

        return $response->json("url");
    }
}

Background Jobs with Laravel Queue

For high-volume screenshot workloads, synchronous API calls will block your web workers and degrade response times. Dispatch screenshot requests to a Laravel queue instead:

<?php

namespace App\Jobs;

use App\Services\SnapApiService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

class CaptureScreenshot implements ShouldQueue
{
    use Queueable;

    public function __construct(
        private string $url,
        private int $recordId,
        private string $recordType
    ) {}

    public function handle(SnapApiService $snapApi): void
    {
        $screenshotUrl = $snapApi->screenshot($this->url, ["full_page" => true]);

        // Store result against the record
        app($this->recordType)->find($this->recordId)
            ->update(["screenshot_url" => $screenshotUrl]);
    }
}

// Dispatch from your controller:
CaptureScreenshot::dispatch($url, $product->id, Product::class)->onQueue("screenshots");

PDF Generation for Reports

Generating PDF reports from web pages is a common requirement for SaaS dashboards, invoices, and compliance documentation. SnapAPI handles this natively:

// Generate a PDF from an invoice page
$snapApi = app(SnapApiService::class);
$pdfUrl = $snapApi->pdf("https://yourapp.com/invoices/" . $invoice->id);

// Return as a download response
return response()->stream(function () use ($pdfUrl) {
    $stream = fopen($pdfUrl, "r");
    fpassthru($stream);
    fclose($stream);
}, 200, [
    "Content-Type" => "application/pdf",
    "Content-Disposition" => "attachment; filename=invoice-" . $invoice->id . ".pdf",
]);

Adding SnapAPI Key to Laravel Config

Store your API key in .env and reference it via the services config file:

// .env
SNAPAPI_KEY=your_api_key_here

// config/services.php
"snapapi" => [
    "key" => env("SNAPAPI_KEY"),
],

Error Handling and Retries

Production integrations need graceful error handling. SnapAPI returns standard HTTP status codes: 200 for success, 400 for invalid parameters, 401 for invalid API keys, and 429 for rate limit hits. Handle these in your service class:

public function screenshot(string $url, array $options = []): ?string
{
    try {
        $response = Http::withHeaders(["X-API-Key" => $this->apiKey])
            ->retry(3, 1000)
            ->post($this->baseUrl . "/api/screenshot", array_merge([
                "url" => $url, "format" => "png"
            ], $options));

        if ($response->successful()) {
            return $response->json("url");
        }

        Log::error("SnapAPI error", [
            "status" => $response->status(),
            "body" => $response->body(),
        ]);
        return null;
    } catch (\Exception $e) {
        Log::error("SnapAPI exception: " . $e->getMessage());
        return null;
    }
}

Testing the Integration

Use Http::fake() in your Laravel tests to avoid real API calls during CI runs:

Http::fake([
    "snapapi.pics/*" => Http::response([
        "url" => "https://cdn.snapapi.pics/screenshots/test.png",
        "captured_at" => "2026-04-04T10:00:00Z",
    ], 200),
]);

$screenshotUrl = app(SnapApiService::class)->screenshot("https://example.com");
$this->assertEquals("https://cdn.snapapi.pics/screenshots/test.png", $screenshotUrl);

Getting Started

Create your free SnapAPI account at snapapi.pics/dashboard. The free plan provides 200 screenshots per month — more than enough to build and validate a complete PHP integration. For production workloads, the Starter plan ($19/month) provides 5,000 monthly captures, and the Growth plan ($79/month) scales to 50,000 with extract and scrape endpoints included.

The PHP SDK is also available on GitHub at github.com/Sleywill/snapapi-php and installable via Composer: composer require snapapi/snapapi-php. The SDK wraps all endpoints with typed method signatures, handles retries automatically, and includes full PHPDoc coverage for IDE autocompletion.

Advanced PHP Integration Patterns for SnapAPI

Beyond the core service class patterns, several advanced integration techniques can dramatically increase the efficiency and reliability of SnapAPI usage in PHP applications. These patterns apply to Laravel, Symfony, and plain PHP projects with minor adaptations.

Rate limiting is a common concern for high-volume screenshot pipelines. SnapAPI enforces per-key rate limits, and PHP applications firing many concurrent requests can hit these limits during traffic spikes. The recommended pattern is to implement a local token bucket or leaky bucket rate limiter using Redis, decrementing the token count before each SnapAPI call and queuing excess requests with a short exponential backoff delay. Laravel users can leverage the RateLimiter facade for a clean implementation that integrates with the application cache driver.

Webhook callbacks eliminate the need for polling in asynchronous workflows. Configure a webhook URL in your SnapAPI request payload, and SnapAPI will POST the completed screenshot URL to your endpoint when the render finishes. In Laravel, a dedicated webhook controller validates the callback signature, retrieves the associated job record by the identifier passed in the original request, and updates the record with the screenshot URL. This pattern is ideal for capture jobs triggered by user actions where immediate response is not required.

Multi-tenant PHP SaaS applications should track SnapAPI usage per tenant to enable usage-based billing, enforce plan limits, and generate per-tenant invoices. Store a snapapi_captures_count column on your tenants table, increment it inside the screenshot service after each successful API call, and add a middleware check that gates screenshot requests based on the tenant plan limits. Reset the counter monthly using a scheduled Artisan command that runs on the first day of each billing period.

Image optimization is worth considering for screenshot outputs that are displayed in your product UI. SnapAPI returns full-resolution PNG or JPEG images that may be larger than necessary for thumbnail or preview display contexts. Pass the returned image URL through your existing image optimization pipeline — Intervention Image for local processing, or Imgix or Cloudflare Image Resizing for CDN-side resizing — to serve appropriately sized images to each viewport without storing multiple resolution variants.

For WordPress plugin developers, SnapAPI integrates straightforwardly using wp_remote_post for the API call and the WordPress transients API for caching results. Store the transient with a key based on the URL hash and a 15-minute expiry. This gives WordPress-based tools the same caching behavior as the Laravel Cache::remember pattern without requiring Composer or external HTTP libraries. The SnapAPI PHP SDK is also compatible with WordPress environments when loaded via Composer with autoloading configured in the plugin bootstrap file.

Create a free SnapAPI account at snapapi.pics/dashboard. The PHP SDK is available at github.com/Sleywill/snapapi-php. Questions about high-volume pricing or enterprise SLAs can be directed to the SnapAPI team through the dashboard support channel.

Why Teams Choose SnapAPI Over Self-Hosted Browser Infrastructure

The alternative to using a screenshot API like SnapAPI is running your own headless browser infrastructure. At first glance this appears straightforward: install Playwright or Puppeteer, spin up a Node.js service, and capture pages on demand. In practice, self-hosted browser infrastructure introduces a category of operational burden that compounds rapidly as usage grows.

Memory management is the first challenge. Chromium instances accumulate memory over extended operation, particularly when rendering JavaScript-heavy pages with large DOM trees and active network connections. Without careful session management and process recycling, a self-hosted Playwright service will exhaust available RAM within hours of sustained use. SnapAPI handles this entirely at the infrastructure level, with automatic session cycling, memory monitoring, and process health checks that run continuously without any configuration from API consumers.

Concurrency is the second challenge. A single Chromium instance renders pages sequentially. To support concurrent screenshot requests, you need a pool of browser instances with a queue and work distribution layer in front of them. Building and maintaining this concurrency layer is a non-trivial engineering project. SnapAPI provides horizontal concurrency out of the box: every API request is routed to an available browser worker without any configuration on the caller side.

Maintenance overhead is the third and most persistent challenge. Chromium releases updates frequently. Playwright and Puppeteer bindings require corresponding version bumps. CSS rendering behavior changes between browser versions in ways that subtly break screenshot outputs. SnapAPI absorbs all of this version management work, ensuring that the browser rendering engine is always current and that output quality is consistent across all API versions.

For engineering teams focused on building product features rather than maintaining infrastructure, SnapAPI delivers the browser rendering capability they need at a fraction of the total cost of self-hosted alternatives when you account for engineering time, cloud compute costs, and incident response overhead. The free plan lets you validate this value proposition with zero commitment. Paid plans start at $19/month.