Laravel Screenshot API Integration

Capture website screenshots in Laravel with ScreenshotAPI. No Browsershot or Node.js needed. PHP code examples with queues and storage.

Last updated: 2026-03-25

Try ScreenshotAPI free

5 free credits. No credit card required.

Start for free

Capture Website Screenshots in Laravel with ScreenshotAPI

Laravel's ecosystem offers Spatie's Browsershot package for screenshot generation, but it depends on a local Chromium binary and Node.js. That adds complexity to Docker images, CI pipelines, and shared hosting environments. Many teams spend hours debugging Puppeteer crashes, missing fonts, and ARM compatibility issues.

A Laravel screenshot API integration with ScreenshotAPI removes those pain points. Your Laravel app makes one HTTP request and gets back a pixel-perfect PNG, JPEG, or WebP. No Chromium, no Node.js, no system-level dependencies.

Quick Start

  1. Sign up for ScreenshotAPI and get your API key. You receive 5 free credits on signup.
  2. Add your API key to .env.
  3. Create a service class and controller.

Installation

ScreenshotAPI works with Laravel's built-in HTTP client. No additional packages are required.

Add the API key to your .env file:

bash
SCREENSHOTAPI_KEY=sk_live_xxxxx

Register it in your config:

php
// config/services.php return [ // ... other services 'screenshotapi' => [ 'key' => env('SCREENSHOTAPI_KEY'), ], ];

Basic Example

A controller that returns a screenshot image:

php
<?php // app/Http/Controllers/ScreenshotController.php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; class ScreenshotController extends Controller { public function show(Request $request) { $request->validate([ 'url' => 'required|url', 'width' => 'integer|min:320|max:3840', 'height' => 'integer|min:200|max:2160', 'type' => 'in:png,jpeg,webp', ]); $response = Http::withHeaders([ 'x-api-key' => config('services.screenshotapi.key'), ])->timeout(30)->get('https://screenshotapi.to/api/v1/screenshot', [ 'url' => $request->input('url'), 'width' => $request->input('width', 1440), 'height' => $request->input('height', 900), 'type' => $request->input('type', 'png'), ]); if ($response->failed()) { return response()->json(['error' => 'Screenshot capture failed'], 502); } $type = $request->input('type', 'png'); return response($response->body()) ->header('Content-Type', "image/{$type}") ->header('Cache-Control', 'public, max-age=86400'); } }

Register the route:

php
// routes/api.php use App\Http\Controllers\ScreenshotController; Route::get('/screenshot', [ScreenshotController::class, 'show']);

Laravel Screenshot Service

Extract the logic into a service class for reuse:

php
<?php // app/Services/ScreenshotService.php namespace App\Services; use Illuminate\Support\Facades\Http; use RuntimeException; class ScreenshotService { private const API_BASE = 'https://screenshotapi.to/api/v1/screenshot'; private const MAX_RETRIES = 2; public function capture( string $url, int $width = 1440, int $height = 900, string $type = 'png', ?int $quality = null, bool $fullPage = false, ?string $colorScheme = null, string $waitUntil = 'networkidle', ): string { $params = [ 'url' => $url, 'width' => $width, 'height' => $height, 'type' => $type, 'waitUntil' => $waitUntil, ]; if ($quality !== null) { $params['quality'] = $quality; } if ($fullPage) { $params['fullPage'] = 'true'; } if ($colorScheme) { $params['colorScheme'] = $colorScheme; } $lastError = null; for ($attempt = 0; $attempt <= self::MAX_RETRIES; $attempt++) { try { $response = Http::withHeaders([ 'x-api-key' => config('services.screenshotapi.key'), ])->timeout(30)->get(self::API_BASE, $params); if ($response->successful()) { return $response->body(); } $lastError = "API returned {$response->status()}"; } catch (\Exception $e) { $lastError = $e->getMessage(); } if ($attempt < self::MAX_RETRIES) { sleep($attempt + 1); } } throw new RuntimeException("Screenshot failed: {$lastError}"); } }

Register it as a singleton:

php
// app/Providers/AppServiceProvider.php use App\Services\ScreenshotService; public function register(): void { $this->app->singleton(ScreenshotService::class); }

Saving to Laravel Storage

Store screenshots on any filesystem disk:

php
use App\Services\ScreenshotService; use Illuminate\Support\Facades\Storage; $service = app(ScreenshotService::class); $image = $service->capture( url: 'https://example.com', type: 'webp', quality: 80, ); $path = 'screenshots/' . md5('https://example.com') . '.webp'; Storage::disk('s3')->put($path, $image, 'public'); $publicUrl = Storage::disk('s3')->url($path);

Eloquent Model Integration

Attach screenshots to your models:

php
<?php // app/Models/Site.php namespace App\Models; use App\Services\ScreenshotService; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Storage; class Site extends Model { protected $fillable = ['url', 'name', 'screenshot_path']; public function captureScreenshot(): void { $service = app(ScreenshotService::class); $image = $service->capture(url: $this->url, type: 'webp', quality: 80); $path = "screenshots/{$this->id}.webp"; Storage::disk('public')->put($path, $image); $this->update(['screenshot_path' => $path]); } public function getScreenshotUrlAttribute(): ?string { if (! $this->screenshot_path) { return null; } return Storage::disk('public')->url($this->screenshot_path); } }

Queued Screenshot Jobs

Process screenshots in the background with Laravel Queues:

php
<?php // app/Jobs/CaptureScreenshot.php namespace App\Jobs; use App\Models\Site; use App\Services\ScreenshotService; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; class CaptureScreenshot implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public int $tries = 3; public array $backoff = [10, 30, 60]; public function __construct( private readonly int $siteId, ) {} public function handle(ScreenshotService $service): void { $site = Site::findOrFail($this->siteId); $site->captureScreenshot(); } }

Dispatch from a controller:

php
use App\Jobs\CaptureScreenshot; public function store(Request $request) { $site = Site::create($request->validated()); CaptureScreenshot::dispatch($site->id); return response()->json($site, 201); }

Batch Processing with Bus

Capture screenshots for multiple sites at once:

php
use Illuminate\Support\Facades\Bus; use App\Jobs\CaptureScreenshot; $sites = Site::whereNull('screenshot_path')->get(); $jobs = $sites->map(fn (Site $site) => new CaptureScreenshot($site->id)); Bus::batch($jobs) ->name('Bulk screenshot capture') ->allowFailures() ->dispatch();

Production Tips

Caching

Use Laravel's cache to avoid redundant API calls:

php
use Illuminate\Support\Facades\Cache; function cachedScreenshot(string $url, string $type = 'webp'): string { $key = 'screenshot:' . md5($url . $type); return Cache::remember($key, now()->addHour(), function () use ($url, $type) { return app(ScreenshotService::class)->capture(url: $url, type: $type, quality: 80); }); }

Rate Limiting

Protect your endpoint with Laravel's throttle middleware:

php
Route::middleware('throttle:30,1')->group(function () { Route::get('/screenshot', [ScreenshotController::class, 'show']); });

Deployment

ScreenshotAPI works on any Laravel hosting environment: Forge, Vapor, shared hosting, Docker. No Chromium binary means smaller images and faster deployments. Visit the pricing page to select the right credit tier.

Further Reading

Frequently asked questions

Does ScreenshotAPI replace Spatie Browsershot?

Yes. Browsershot requires a local Chromium and Node.js installation. ScreenshotAPI offloads rendering to its cloud infrastructure, so you only need a simple HTTP call from PHP.

Can I save screenshots to Laravel's filesystem?

Absolutely. Download the image from the API and use Storage::put to write it to any configured disk, including S3, GCS, or local storage.

How do I capture screenshots in a queued job?

Dispatch a job that calls ScreenshotAPI, saves the resulting image, and updates your database record. Laravel's retry mechanisms handle transient failures automatically.

Does it work on shared hosting?

Yes. ScreenshotAPI only requires outbound HTTP access. No Chromium binary, no Node.js, no special server configuration.

Related resources

Start capturing screenshots today

Create a free account and get 5 credits to try the API. No credit card required. Pay only for what you use.