DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

Laravel Screenshot API: Capture Screenshots and Generate PDFs in PHP

Laravel Screenshot API: Capture Screenshots and Generate PDFs in PHP

Laravel has 10+ million installations. And almost every Laravel app needs to do something that requires a screenshot or PDF at some point:

  • Generate invoice PDFs for orders
  • Create social preview images for shared links
  • Screenshot user-generated content for moderation
  • Generate certificates or badges
  • Create email header images with dynamic data

Your options have been limited:

  1. Use spatie/browsershot — which requires Chrome binary installed (pain on shared hosting)
  2. Use barryvdh/laravel-dompdf — which is fine for simple PDFs but limited on styling
  3. Build it yourself with Puppeteer — overkill and adds infrastructure burden

There's a simpler way: a screenshot API.

The Laravel Problem: Screenshots Without Infrastructure

Laravel is excellent at building web applications, but it doesn't come with built-in screenshot or PDF generation from HTML. Here's why each common solution falls short:

Option 1: Browsershot (Requires Chrome)

<?php
use Spatie\Browsershot\Browsershot;

// First, you need Chrome installed on your server
// On shared hosting? Good luck with permissions.
// On serverless? 500MB+ layer just for Chrome.

Browsershot::url('https://example.com')
    ->screenshot()
    ->save($pathToImage);
Enter fullscreen mode Exit fullscreen mode

Problems:

  • Requires system-level Chrome installation
  • Fails on shared hosting (no root access)
  • Bloated on serverless
  • Unmaintained since 2021

Option 2: DomPDF (Limited CSS)

<?php
use Barryvdh\DomPDF\Facade\Pdf;

$pdf = Pdf::loadView('invoice', ['data' => $data]);
return $pdf->download('invoice.pdf');

// Works fine for basic PDFs, but:
// - CSS Grid? No
// - Modern flexbox? Limited
// - SVG? Limited
// - Fonts? Complicated
Enter fullscreen mode Exit fullscreen mode

Problems:

  • Limited CSS support
  • Rendering bugs with modern styling
  • Not a real browser engine

Option 3: PageBolt API (Zero Dependencies)

<?php
use Illuminate\Support\Facades\Http;

$response = Http::post('https://api.pagebolt.dev/take_screenshot', [
    'Authorization' => 'Bearer ' . env('PAGEBOLT_API_KEY'),
], [
    'url' => 'https://example.com',
    'width' => 1200,
    'height' => 630
]);

$imageUrl = $response->json('imageUrl');
// Done. No Chrome. No infrastructure.
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Zero system dependencies
  • Works on any hosting
  • Modern Chromium rendering
  • Scales instantly

Build a Reusable Laravel Service

Create a service class to wrap PageBolt API calls:

<?php
namespace App\Services;

use Illuminate\Support\Facades\Http;

class PageBoltService
{
    protected $apiKey;
    protected $baseUrl = 'https://api.pagebolt.dev';

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

    /**
     * Take a screenshot of a URL
     */
    public function screenshot(string $url, array $options = []): ?string
    {
        $payload = array_merge(['url' => $url], $options);

        $response = Http::withHeaders([
            'Authorization' => "Bearer {$this->apiKey}",
            'Content-Type' => 'application/json'
        ])->post("{$this->baseUrl}/take_screenshot", $payload);

        if ($response->failed()) {
            return null;
        }

        return $response->json('imageUrl');
    }

    /**
     * Generate a PDF from a URL
     */
    public function pdfFromUrl(string $url, array $options = []): ?string
    {
        $payload = array_merge(['url' => $url], $options);

        $response = Http::withHeaders([
            'Authorization' => "Bearer {$this->apiKey}",
            'Content-Type' => 'application/json'
        ])->post("{$this->baseUrl}/generate_pdf", $payload);

        if ($response->failed()) {
            return null;
        }

        return $response->json('pdfUrl');
    }

    /**
     * Generate a PDF from raw HTML
     */
    public function pdfFromHtml(string $html, array $options = []): ?string
    {
        $payload = array_merge(['html' => $html], $options);

        $response = Http::withHeaders([
            'Authorization' => "Bearer {$this->apiKey}",
            'Content-Type' => 'application/json'
        ])->post("{$this->baseUrl}/generate_pdf", $payload);

        if ($response->failed()) {
            return null;
        }

        return $response->json('pdfUrl');
    }

    /**
     * Take a screenshot from raw HTML
     */
    public function screenshotFromHtml(string $html, array $options = []): ?string
    {
        $payload = array_merge(['html' => $html], $options);

        $response = Http::withHeaders([
            'Authorization' => "Bearer {$this->apiKey}",
            'Content-Type' => 'application/json'
        ])->post("{$this->baseUrl}/take_screenshot", $payload);

        if ($response->failed()) {
            return null;
        }

        return $response->json('imageUrl');
    }
}
Enter fullscreen mode Exit fullscreen mode

Real-World Laravel Examples

Example 1: Generate Invoice PDFs

<?php
namespace App\Http\Controllers;

use App\Models\Invoice;
use App\Services\PageBoltService;

class InvoiceController extends Controller
{
    public function __construct(protected PageBoltService $pagebolt) {}

    public function pdf(Invoice $invoice)
    {
        // Render the invoice view as HTML
        $html = view('invoices.template', ['invoice' => $invoice])->render();

        // Convert to PDF
        $pdfUrl = $this->pagebolt->pdfFromHtml($html, [
            'format' => 'A4',
            'margin' => '10mm'
        ]);

        if (!$pdfUrl) {
            return response()->json(['error' => 'PDF generation failed'], 500);
        }

        // Download the PDF
        return redirect($pdfUrl);
    }
}
Enter fullscreen mode Exit fullscreen mode

Example 2: Create Social Preview Images

<?php
namespace App\Http\Controllers;

use App\Models\BlogPost;
use App\Services\PageBoltService;

class BlogController extends Controller
{
    public function __construct(protected PageBoltService $pagebolt) {}

    public function generateOGImage(BlogPost $post)
    {
        $html = view('og-template', [
            'title' => $post->title,
            'author' => $post->author->name,
            'date' => $post->created_at->format('M d, Y')
        ])->render();

        $imageUrl = $this->pagebolt->screenshotFromHtml($html, [
            'width' => 1200,
            'height' => 630
        ]);

        // Store the URL in your database or cache
        $post->update(['og_image_url' => $imageUrl]);

        return response()->json(['image_url' => $imageUrl]);
    }
}
Enter fullscreen mode Exit fullscreen mode

Example 3: Screenshot Content for Moderation

<?php
namespace App\Jobs;

use App\Models\UserContent;
use App\Services\PageBoltService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

class CaptureContentScreenshot implements ShouldQueue
{
    use Queueable;

    public function __construct(protected UserContent $content) {}

    public function handle(PageBoltService $pagebolt)
    {
        // Capture a screenshot of user-generated content
        $screenshot = $pagebolt->screenshot(
            $this->content->url,
            ['width' => 1280, 'height' => 720]
        );

        if ($screenshot) {
            $this->content->update(['screenshot_url' => $screenshot]);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Configuration Setup

In your .env:

PAGEBOLT_API_KEY=your_api_key_here
Enter fullscreen mode Exit fullscreen mode

In config/services.php:

<?php
return [
    'pagebolt' => [
        'key' => env('PAGEBOLT_API_KEY'),
    ],
];
Enter fullscreen mode Exit fullscreen mode

Register the service in AppServiceProvider:

<?php
namespace App\Providers;

use App\Services\PageBoltService;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(PageBoltService::class, function ($app) {
            return new PageBoltService();
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Why PageBolt for Laravel

Aspect Browsershot DomPDF PageBolt API
Setup Install Chrome binary Composer dependency API key
Works on shared hosting ❌ No ✅ Yes ✅ Yes
Works on serverless ⚠️ Heavy layer ✅ Yes ✅ Yes
CSS support ✅ Full modern CSS ⚠️ Limited ✅ Full modern CSS
SVG/fonts ✅ Full support ⚠️ Limited ✅ Full support
Dependencies Chrome binary PHP-only None (HTTP)
Maintenance 🚨 Abandoned (2021) ⚠️ Community ✅ Active
Time to first PDF Hours (setup) Minutes Minutes

Getting Started

1. Get an API key (free tier: 100 requests/month)

# Visit pagebolt.dev, create account, copy API key
Enter fullscreen mode Exit fullscreen mode

2. Create the service class (code above)

3. Use in your controllers

// Inject and use
public function __construct(PageBoltService $pagebolt)
{
    $this->pagebolt = $pagebolt;
}

// Take a screenshot
$imageUrl = $this->pagebolt->screenshot('https://example.com');

// Generate a PDF
$pdfUrl = $this->pagebolt->pdfFromHtml($htmlString);
Enter fullscreen mode Exit fullscreen mode

4. Deploy and scale

No Chrome binary to manage. No serverless bloat. Just HTTP requests.

Next Steps

  • Try PageBolt free — 100 requests/month, no credit card. Perfect for testing.
  • Copy the service class above — drop it into your Laravel app.
  • Start with one use case — invoice PDFs, OG images, or screenshots. Pick one.
  • Scale seamlessly — when you need more requests, just upgrade. No infrastructure changes.

Stop fighting Chrome binaries. Start generating PDFs and screenshots in Laravel.


PageBolt: Screenshots and PDFs for Laravel, without the headache. Get started free →

Top comments (0)