How to export HTML content to PDF in Laravel
If you’ve ever needed to convert HTML content or a full web page into a PDF from your Laravel application, you’re in luck. In this post we’ll walk through how to integrate PageSnap.co’s API to generate PDFs from HTML (or URLs) in bulk, and how to implement it cleanly in a Laravel app.
Why and when you’d want this
Sometimes you need to produce PDF documents automatically: for reports, invoices, receipts, or snapshots of web content. Rather than embedding a heavy PDF library or managing headless-browser infrastructure yourself, you can delegate that to a service (like PageSnap.co) and focus on your Laravel logic.
With PageSnap you can:
- Send either URLs or raw HTML snippets.
- Handle batch jobs (many contents at once) via webhook callback.
- Fine-tune PDF options (margins, orientation, header/footer etc).
- Use your own AWS S3 bucket to receive PDFs.
Since you’re a Laravel developer, you’ll appreciate how you can wrap the API call into a service class, queue the job, handle webhook callbacks, and store result links in your database.
Prerequisites
Before you begin:
- You have a Laravel app (e.g., Laravel 10).
- You have signed up for PageSnap.co and obtained your API key (via the dashboard).
- You have your authentication credentials (Basic Auth) set up in the .env file.
- You know how to dispatch jobs or handle controllers in Laravel.
Step 1: Store API credentials in Laravel
In your .env file, add something like:
PAGESNAP_API_USERNAME=your_username
PAGESNAP_API_KEY=your_api_key
PAGESNAP_API_ENDPOINT=https://api.pagesnap.co/snap
Then in config/services.php, add an entry:
'pagesnap' => [
'username' => env('PAGESNAP_API_USERNAME'),
'key' => env('PAGESNAP_API_KEY'),
'endpoint' => env('PAGESNAP_API_ENDPOINT'),
],
Step 2: Create a Laravel service class to call the API
In app/Services/PageSnapService.php you might write:
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class PageSnapService
{
protected $username;
protected $key;
protected $endpoint;
public function __construct()
{
$this->username = config('services.pagesnap.username');
$this->key = config('services.pagesnap.key');
$this->endpoint = config('services.pagesnap.endpoint');
}
/**
* Submit a single HTML content or URL for PDF generation.
*
* @param array $contents e.g. ['htmls'=>[...]] or ['urls'=>[...]]
* @param array $options Optional settings for PDF generation
* @param bool $sandbox Whether to use sandbox (watermarked) mode
* @param string|null $webhookUrl If batch mode, specify webhook
*
* @return array The JSON response from PageSnap
*/
public function submit(array $contents, array $options = [], bool $sandbox = true, string $webhookUrl = null)
{
$payload = [
'sandbox' => $sandbox,
'contents' => $contents,
];
if (! empty($options)) {
$payload['options'] = $options;
}
if ($webhookUrl) {
$payload['webhook_url'] = $webhookUrl;
}
$response = Http::withBasicAuth($this->username, $this->key)
->post($this->endpoint, $payload);
if ($response->failed()) {
Log::error('PageSnap API error', [
'status' => $response->status(),
'response' => $response->body(),
]);
throw new \Exception('Failed to submit PageSnap request: ' . $response->body());
}
return $response->json();
}
}
This service class handles the API call, sends Basic Auth headers as required by PageSnap.
Step 3: Use it in a controller
Suppose you want a controller endpoint where you submit some HTML content and return the PDF link. In app/Http/Controllers/PdfExportController.php:
namespace App\Http\Controllers;
use App\Services\PageSnapService;
use Illuminate\Http\Request;
class PdfExportController extends Controller
{
protected $pageSnap;
public function __construct(PageSnapService $pageSnap)
{
$this->pageSnap = $pageSnap;
}
public function exportHtml(Request $request)
{
$htmlContent = $request->input('html');
$contents = [
'htmls' => [
$htmlContent,
],
];
$options = [
'format' => 'A4',
'print_background' => true,
];
$response = $this->pageSnap->submit($contents, $options, true /* sandbox */);
if (isset($response['pdf'])) {
return response()->json([
'success' => true,
'pdf_url' => $response['pdf'],
]);
}
return response()->json([
'success' => false,
'message' => $response['message'] ?? 'Unknown error',
], 500);
}
}
You’ll notice when sending a single request (no webhook_url), the response includes a pdf field with the download link.
Step 4: Handling batch (bulk) export
If you want to handle multiple URLs or HTML snippets in one request, you can use the batch mode. For example:
$contents = [
'urls' => [
'https://example.com/report1',
'https://example.com/report2',
],
'htmls' => [
'<h1>Custom HTML 1</h1>',
'<h1>Custom HTML 2</h1>',
],
];
$webhookUrl = route('pagesnap.webhook'); // e.g., your webhook endpoint
$response = $this->pageSnap->submit($contents, [], false /* sandbox false for production */, $webhookUrl);
// response will include request_id but no immediate pdf link
When using webhook_url, the API will queue the job and send you a POST callback once completed with the download links
Webhook handler
Add a route and controller to handle the callback:
Route::post('/pagesnap/webhook', [WebhookController::class, 'handle'])->name('pagesnap.webhook');
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class WebhookController extends Controller
{
public function handle(Request $request)
{
$payload = $request->all();
// e.g. $payload['results'] contains array of { source, pdf, expired_at, error }
Log::info('PageSnap webhook received', $payload);
// Save the result in your DB, notify user, etc.
return response()->json(['received' => true]);
}
}
Step 5: Additional options & fine-tuning
The PageSnap API supports many options for controlling the PDF output. Some you might find useful:
set_java_script_enabled: Disable JS execution if you’re rendering a simple static page.disable_images: Skip images to reduce size/performance.add_style/add_script: Inject CSS or JS before generation.emulate_media_type: e.g., “print” versus “screen” for styling differences.margin_top,margin_bottom,margin_left,margin_right: Fine-tune white space.format: Choose paper size like A4, Letter.landscape: Switch orientation.
You can combine these options in the $options array when calling the service.
Step 6: Store or forward the resulting PDF
Once you have the PDF URL, you might:
- Download it into your own Laravel storage (e.g., using
Storage::put()), - Or simply store the link in your database and allow users to download via redirect.
- If using
s3_path_urlparameter, you can direct PageSnap to store the resulting PDFs in your AWS S3 bucket — this is useful if you want full control of PDF storage.
Common pitfalls & tips
- Sandbox mode: During development, always set
sandbox: trueso you don’t expend credits and PDFs are watermarked. - Authentication: Ensure Basic Auth headers are correct; otherwise you’ll get 401/403.
- Large PDFs: If your content is huge, you might get status 413 (Generated PDF too large).
- Webhook reliability: Ensure your webhook endpoint is publicly accessible and fast. If it fails, you might need to re-poll via dashboard.
- Batch size: A single request can include up to 200 “contents” (urls/htmls/templates) for batch mode.
- Template mode: If you use templates (via your PageSnap dashboard), you can pass structured data; handy for invoices etc.
Example use-case: Generating weekly invoice PDFs for customers
Let’s imagine you run a subscription service and each week you want to generate a PDF invoice for all paying customers. In your Laravel Console\Command you could:
- Fetch all invoices due this week from DB.
- Build an array of HTML snippets or URLs (maybe a view rendered as HTML).
- Submit a batch request to PageSnap with
'htmls' => [...]. - Provide your webhook URL so when conversion is done you get notified with all PDF download links.
- On webhook callback, update each customer record with their PDF link, then email them.
- This is scalable, you don’t need to manage headless browser servers, and you can handle hundreds of invoices in one batch.
Summary
Converting HTML or URLs to PDF in Laravel is straightforward when using a purpose-built API like PageSnap.co. By wrapping the API call in a service class, and optionally using Laravel queues/cron jobs for bulk exports, you can build a robust PDF export workflow with minimal overhead.
Whether you’re generating a one-off PDF for a user or processing thousands of documents overnight in batch, PageSnap gives you the flexibility (and fine-tuning options) you need.
Ready to try it? Sign up for PageSnap.co, drop your API credentials into Laravel, and you’ll be generating PDFs in minutes.
If you enjoyed this tutorial and want more like “customizing headers and footers”, “monitoring conversion status”, or “using templates for invoices”, let me know and I’ll follow up.
Happy coding! 🚀
