Language

PHP Screenshot API

Add screenshot capabilities to PHP applications using cURL or Guzzle HTTP. Perfect for WordPress plugins, Laravel apps, and REST-based web services. Covers authentication, error handling, saving to local storage or S3, and integrating with Laravel queues for background processing.

Quick summaryPHP capture via Guzzle or cURL. Common in Laravel, Symfony, and WordPress. Always run captures in a queued job (Laravel Queue, Symfony Messenger, Action Scheduler) — never inline in a controller.

Quick Start

1

Install dependencies

Ensure cURL extension is enabled in php.ini (enabled by default in most installations).

2

Get your API key

Sign up for Screenshotly and get your API key from the dashboard.

3

Copy the code example

Use our PHP code example as a starting point.

4

Customize and integrate

Modify the code to fit your specific use case and requirements.

Code Example

<?php
// PHP with cURL
function captureScreenshot($url, $outputPath) {
    $ch = curl_init('https://api.screenshotly.app/screenshot');
    
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $_ENV['SCREENSHOTLY_API_KEY'],
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'url' => $url,
            'device' => 'desktop',
            'format' => 'png',
        ]),
    ]);
    
    $response = curl_exec($ch);
    curl_close($ch);
    
    file_put_contents($outputPath, $response);
}

When to Use PHP with Screenshotly

Use the PHP integration for WordPress plugins, Laravel or Symfony applications, and traditional server-rendered web apps. PHP is the right choice when you need to generate screenshots from a CMS, embed capture functionality in a WordPress admin panel, or build a screenshot feature into an existing LAMP/LEMP stack.

PHP Best Practices

In Laravel, use Http::withToken()->post() for cleaner code than raw cURL. Pair with Laravel Queues (database or Redis) for background processing.

Cache screenshots with WordPress transients or Laravel Cache to avoid redundant API calls for the same URL.

Use curl_setopt($ch, CURLOPT_TIMEOUT, 30) to set a hard timeout — prevents your web server from hanging on slow captures.

For Guzzle users, enable the retry middleware with HandlerStack::create() and new RetryMiddleware() for automatic retry on 429/5xx.

PHP: Production Notes

In modern PHP (Laravel 10+, Symfony 6+), the clean pattern is Guzzle as the HTTP client and a queued job for the capture itself. Inline controller capture blocks the PHP-FPM worker for 2–5 seconds, which saturates worker pools under even modest traffic and produces 502s.

For WordPress, the appropriate hook is typically `save_post` or `publish_post`. Trigger the capture asynchronously via Action Scheduler (Woo bundles it) or defer via `wp_schedule_single_event()`. WP-Cron alone is unreliable on low-traffic sites because it only fires on page load.

Guzzle's `sink` option streams the response body directly to a file path — critical for large captures. Avoid `getBody()->getContents()` on multi-MB PDFs or full-page PNGs; PHP-FPM memory limits will starve the worker.

Error Handling Recipes

Concrete strategies for each failure mode. Do not silently swallow errors — surface them to your monitoring so the pipeline is observable.

HTTP 429

For Laravel queues, use $this->release($retryAfter) to requeue with delay rather than blocking.

if ($response->getStatusCode() === 429) {
    $retryAfter = (int) $response->getHeaderLine('Retry-After');
    $this->release($retryAfter);
    return;
}

GuzzleHttp\Exception\ServerException (5xx)

Use Guzzle retry middleware with exponential backoff — avoid hand-rolling retry logic in each call site.

Response too large for memory_limit

Use Guzzle's sink option: `new Client(['sink' => $path])`. Never `getBody()->getContents()` on large captures.

Production Hardening Checklist

The difference between dev code and prod code. Work through these before putting PHP captures on a critical path.

  • Capture runs in a queued job (Laravel Queue, Symfony Messenger, Action Scheduler) — never inline.
  • Guzzle client instantiated once in the service container and reused.
  • Response streamed to S3/filesystem via sink, not buffered.
  • Queue job has tries=3 and backoff configured.
  • API key stored in config/services (never .env in repo, never hardcoded).
  • Laravel Horizon or equivalent monitors queue throughput and failed jobs.

Rate-Limit Strategy

Cap captures per authenticated user at ~20/hour using Laravel's RateLimiter facade or Symfony's rate-limiter component (Redis-backed). At the worker level, configure Horizon/Messenger so parallel workers × requests/sec stays below your plan's QPS.

When PHP isn't the right fit

PHP works well for most capture workloads, but these patterns are legitimate reasons to pick a different stack:

  • Your WordPress install has no queue infrastructure (no Action Scheduler, no external cron). On low-traffic sites, WP-Cron fires unreliably — scheduled captures will be late or skipped.
  • You run a Laravel app on shared hosting without Supervisor or similar process manager. Queue workers need to stay alive, and that is not feasible without proper process supervision.
  • You need async HTTP with fiber-level concurrency. PHP is moving toward this with Swoole/OpenSwoole, but mainstream hosting is still request-per-process — scale concurrency via more workers, not per-worker fibers.

Want a step-by-step walkthrough?

Read the full PHP tutorial →

API Reference

EndpointPOST /api/screenshot
AuthenticationBearer token
Content-Typeapplication/json
View full API docs

Frequently Asked Questions

How do I use Screenshotly with Laravel?

Create a service class that wraps the API call using Laravel's HTTP client: Http::withToken(config('services.screenshotly.key'))->post('https://api.screenshotly.app/screenshot', [...]). Store screenshots in Laravel's filesystem using Storage::put().

How do I handle errors with the PHP cURL integration?

Check the HTTP status code with curl_getinfo($ch, CURLINFO_HTTP_CODE). Handle 401 (invalid key), 429 (rate limited), and 500 (server error) cases. Implement retry logic with exponential backoff for temporary failures.

Can I use Screenshotly in WordPress plugins?

Yes. Use WordPress's wp_remote_post() function instead of cURL for better compatibility. Store API keys in wp_options or constants. Cache screenshots using WordPress transients to avoid redundant API calls.

What's the recommended PHP version for the API integration?

PHP 7.4+ is recommended. The cURL extension is required. For modern projects, consider using Guzzle HTTP client for cleaner code and built-in retry/timeout handling.

Start building with PHP

Get your API key and start capturing screenshots in minutes.

Other Languages