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.
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
- Sign up for ScreenshotAPI and get your API key. You receive 5 free credits on signup.
- Add your API key to
.env. - 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:
bashSCREENSHOTAPI_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:
phpuse 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:
phpuse 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:
phpuse 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:
phpuse 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:
phpRoute::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
- The PHP SDK documentation covers the full parameter reference.
- See the WordPress integration if you need screenshot support in a WordPress environment.
- Read about OG image generation for dynamic social sharing previews.
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.