PHP
Browserless works with PHP through cURL, Guzzle, or community frameworks like Laravel and Symfony.
Basic Usage
The quickest way to call Browserless from PHP is with cURL:
<?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)
Community Package
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
}
solve(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.