PHP Integration Guide
Browserless provides multiple ways to integrate with PHP applications, from simple REST API calls using cURL to comprehensive Laravel SDK integration. This guide covers everything from basic usage to advanced automation workflows.
Quick Start: Basic cURL Usage
The simplest way to get started with Browserless in PHP is using cURL to call our REST APIs:
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
"url" => "https://example.com/",
"options" => [
"fullPage" => true,
"encoding" => "base64"
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
// Save the base64 encoded screenshot
file_put_contents('screenshot.png', base64_decode($response));
echo "Screenshot saved successfully!";
}
Laravel SDK (Community Supported)
The Laravel Browserless SDK is a community-supported package created and maintained by Christopher Miller. While not officially supported by Browserless, it provides an excellent Laravel-native way to integrate with our services.
Learn more about the SDK:
Installation
composer require millerphp/laravel-browserless
Laravel SDK Examples
The Laravel SDK provides a fluent, Laravel-native API for all Browserless features:
- PDF Generation
- Screenshots
- Content Scraping
- BrowserQL
use MillerPHP\LaravelBrowserless\Facades\Browserless;
// Generate a PDF from a URL
$pdf = Browserless::pdf()
->url('https://example.com/invoice/123')
->printBackground()
->format('A4')
->send()
->save('invoice-123.pdf');
// Capture a full-page screenshot
$screenshot = Browserless::screenshot()
->url('https://example.com')
->fullPage()
->send()
->save('homepage.png');
// Scrape structured data
$result = Browserless::scrape()
->url('https://example.com/products')
->element('.product', [
'text' => true,
'attributes' => ['id', 'href'],
])
->send();
$products = $result->results('.product');
// Run advanced browser automation with BQL
$result = Browserless::bql()
->query('
mutation VerifyUser {
goto(url: "https://example.com/protected") {
status
}
verify(type: cloudflare) {
found
solved
time
}
}
')
->stealth()
->humanLike()
->send();
Modern PHP Examples (PHP 8.x)
Using Guzzle HTTP Client
For more robust HTTP handling, consider using Guzzle:
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class BrowserlessClient
{
private Client $client;
private string $apiToken;
private string $baseUrl;
public function __construct(string $apiToken, string $region = 'sfo')
{
$this->apiToken = $apiToken;
$this->baseUrl = "https://production-{$region}.browserless.io";
$this->client = new Client([
'timeout' => 60,
'headers' => [
'Content-Type' => 'application/json',
'User-Agent' => 'PHP-Browserless-Client/1.0'
]
]);
}
public function screenshot(string $url, array $options = []): string
{
try {
$response = $this->client->post("{$this->baseUrl}/screenshot", [
'query' => ['token' => $this->apiToken],
'json' => [
'url' => $url,
'options' => array_merge([
'fullPage' => true,
'type' => 'png'
], $options)
]
]);
return $response->getBody()->getContents();
} catch (RequestException $e) {
throw new Exception("Screenshot failed: " . $e->getMessage());
}
}
public function generatePdf(string $url, array $options = []): string
{
try {
$response = $this->client->post("{$this->baseUrl}/pdf", [
'query' => ['token' => $this->apiToken],
'json' => [
'url' => $url,
'options' => array_merge([
'format' => 'A4',
'printBackground' => true
], $options)
]
]);
return $response->getBody()->getContents();
} catch (RequestException $e) {
throw new Exception("PDF generation failed: " . $e->getMessage());
}
}
public function getContent(string $url, int $waitFor = 0): array
{
try {
$response = $this->client->post("{$this->baseUrl}/content", [
'query' => ['token' => $this->apiToken],
'json' => [
'url' => $url,
'waitForTimeout' => $waitFor
]
]);
return json_decode($response->getBody()->getContents(), true);
} catch (RequestException $e) {
throw new Exception("Content retrieval failed: " . $e->getMessage());
}
}
}
Usage Example with Error Handling
<?php
try {
$browserless = new BrowserlessClient('YOUR_API_TOKEN_HERE');
// Generate a PDF report
$pdfData = $browserless->generatePdf('https://example.com/report', [
'format' => 'A4',
'margin' => [
'top' => '1cm',
'bottom' => '1cm',
'left' => '1cm',
'right' => '1cm'
]
]);
file_put_contents('report.pdf', $pdfData);
echo "PDF generated successfully!\n";
// Take a mobile screenshot
$screenshotData = $browserless->screenshot('https://example.com', [
'viewport' => [
'width' => 375,
'height' => 667,
'deviceScaleFactor' => 2
]
]);
file_put_contents('mobile-screenshot.png', $screenshotData);
echo "Mobile screenshot captured!\n";
} catch (Exception $e) {
error_log("Browserless operation failed: " . $e->getMessage());
}
Advanced Use Cases
Web Scraping with Content API
<?php
class WebScraper
{
private BrowserlessClient $client;
public function __construct(BrowserlessClient $client)
{
$this->client = $client;
}
public function scrapeProductPrices(string $url): array
{
// Wait for dynamic content to load
$content = $this->client->getContent($url, 3000);
$dom = new DOMDocument();
@$dom->loadHTML($content);
$xpath = new DOMXPath($dom);
$prices = [];
$priceNodes = $xpath->query('//span[@class="price"]');
foreach ($priceNodes as $node) {
$prices[] = trim($node->textContent);
}
return $prices;
}
public function scrapeBatch(array $urls): array
{
$results = [];
foreach ($urls as $url) {
try {
$results[$url] = $this->scrapeProductPrices($url);
// Add delay to be respectful
sleep(1);
} catch (Exception $e) {
$results[$url] = ['error' => $e->getMessage()];
}
}
return $results;
}
}
Using with Proxy Support
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://production-sfo.browserless.io/screenshot?token=YOUR_API_TOKEN_HERE&proxy=residential",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
"url" => "https://example.com/",
"options" => [
"fullPage" => true
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
curl_close($curl);
Function Execution for Custom Logic
<?php
function executeCustomBrowserFunction(string $url, string $jsCode): array
{
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://production-sfo.browserless.io/function?token=YOUR_API_TOKEN_HERE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
"code" => $jsCode,
"context" => [
"url" => $url
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
curl_close($curl);
return json_decode($response, true);
}
// Example: Get page performance metrics
$performanceCode = '
export default async ({ page, context }) => {
await page.goto(context.url);
const metrics = await page.evaluate(() => {
const navigation = performance.getEntriesByType("navigation")[0];
return {
loadTime: navigation.loadEventEnd - navigation.loadEventStart,
domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
firstPaint: performance.getEntriesByType("paint")[0]?.startTime || 0
};
});
return metrics;
};
';
$metrics = executeCustomBrowserFunction('https://example.com', $performanceCode);
echo "Page load time: " . $metrics['loadTime'] . "ms\n";
Framework Integration Examples
Symfony Integration
<?php
namespace App\Service;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class BrowserlessService
{
private HttpClientInterface $client;
private string $apiToken;
public function __construct(string $apiToken)
{
$this->apiToken = $apiToken;
$this->client = HttpClient::create([
'timeout' => 60,
'headers' => [
'Content-Type' => 'application/json'
]
]);
}
public function generateInvoicePdf(int $invoiceId): string
{
$response = $this->client->request('POST',
'https://production-sfo.browserless.io/pdf', [
'query' => ['token' => $this->apiToken],
'json' => [
'url' => "https://yourapp.com/invoice/{$invoiceId}",
'options' => [
'format' => 'A4',
'printBackground' => true
]
]
]);
return $response->getContent();
}
}
WordPress Plugin Integration
<?php
class WP_Browserless_Plugin
{
private string $apiToken;
public function __construct()
{
$this->apiToken = get_option('browserless_api_token');
add_action('init', [$this, 'init']);
}
public function init(): void
{
add_action('wp_ajax_generate_pdf', [$this, 'generatePdf']);
add_action('wp_ajax_nopriv_generate_pdf', [$this, 'generatePdf']);
}
public function generatePdf(): void
{
$post_id = intval($_POST['post_id']);
$post_url = get_permalink($post_id);
$response = wp_remote_post('https://production-sfo.browserless.io/pdf', [
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode([
'url' => $post_url,
'options' => [
'format' => 'A4',
'printBackground' => true
]
]),
'timeout' => 60
]);
if (is_wp_error($response)) {
wp_die('PDF generation failed');
}
$pdf_data = wp_remote_retrieve_body($response);
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="post-' . $post_id . '.pdf"');
echo $pdf_data;
exit;
}
}
new WP_Browserless_Plugin();
Best Practices
Error Handling and Retry Logic
<?php
class RobustBrowserlessClient
{
private const MAX_RETRIES = 3;
private const RETRY_DELAY = 1; // seconds
private function executeWithRetry(callable $operation, int $maxRetries = self::MAX_RETRIES): mixed
{
$lastException = null;
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
try {
return $operation();
} catch (Exception $e) {
$lastException = $e;
if ($attempt < $maxRetries) {
sleep(self::RETRY_DELAY * $attempt); // Exponential backoff
continue;
}
}
}
throw $lastException;
}
public function reliableScreenshot(string $url): string
{
return $this->executeWithRetry(function() use ($url) {
// Your screenshot logic here
return $this->client->screenshot($url);
});
}
}
Configuration Management
<?php
class BrowserlessConfig
{
public readonly string $apiToken;
public readonly string $baseUrl;
public readonly int $timeout;
public readonly array $defaultOptions;
public function __construct(
?string $apiToken = null,
string $region = 'sfo',
int $timeout = 60
) {
$this->apiToken = $apiToken ?? $_ENV['BROWSERLESS_API_TOKEN'] ?? throw new InvalidArgumentException('API token required');
$this->baseUrl = "https://production-{$region}.browserless.io";
$this->timeout = $timeout;
$this->defaultOptions = [
'stealth' => true,
'blockAds' => true
];
}
}
Performance Optimization
Concurrent Requests with ReactPHP
<?php
use React\Http\Browser;
use React\Promise\Promise;
function concurrentScreenshots(array $urls): Promise
{
$browser = new Browser();
$promises = [];
foreach ($urls as $url) {
$promises[] = $browser->post('https://production-sfo.browserless.io/screenshot', [
'Content-Type' => 'application/json'
], json_encode([
'url' => $url,
'options' => ['fullPage' => true]
]));
}
return \React\Promise\all($promises);
}
Security Considerations
- Never expose your API token in client-side code or public repositories
- Use environment variables to store sensitive configuration
- Implement rate limiting in your application to avoid hitting API limits
- Validate and sanitize URLs before sending them to Browserless
- Use HTTPS for all communications with the Browserless API
Troubleshooting Common Issues
Timeout Issues
If you're experiencing timeouts, increase the timeout values:
// Increase cURL timeout
curl_setopt($curl, CURLOPT_TIMEOUT, 120);
// Or add timeout parameter to URL
$url = "https://production-sfo.browserless.io/pdf?token=YOUR_TOKEN&timeout=120000";
Memory Issues with Large Responses
For large PDFs or screenshots, consider streaming:
$handle = fopen('large-file.pdf', 'w');
curl_setopt($curl, CURLOPT_FILE, $handle);
curl_exec($curl);
fclose($handle);
Next Steps
- Explore our REST APIs documentation for complete API reference
- Learn about BrowserQL for advanced browser automation
- Check out session management for persistent browser sessions
- Review best practices for production deployments
Be sure to let us know if you have questions or issues.