Skip to content
Firecrawl Docsv2
Firecrawl Docs
Official

PHP

Firecrawl PHP SDK is a wrapper around the Firecrawl API to help you easily turn websites into markdown.

Installation

The official PHP SDK is maintained in the Firecrawl monorepo at apps/php-sdk.

To install the Firecrawl PHP SDK, add the dependency via Composer:

composer require firecrawl/firecrawl-sdk
Requires PHP 8.1 or later.

Laravel Integration

The SDK includes first-class Laravel support with auto-discovery. After installing the package, publish the configuration file:

php artisan vendor:publish --provider="Firecrawl\Laravel\FirecrawlServiceProvider"

Then add your API key to your .env file:

FIRECRAWL_API_KEY=fc-your-api-key

The following environment variables are supported:

VariableDefaultDescription
FIRECRAWL_API_KEYYour Firecrawl API key (required)
FIRECRAWL_API_URLhttps://api.firecrawl.devAPI base URL
FIRECRAWL_TIMEOUT300HTTP request timeout in seconds
FIRECRAWL_MAX_RETRIES3Automatic retries for transient failures
FIRECRAWL_BACKOFF_FACTOR0.5Exponential backoff factor in seconds

Usage

  1. Get an API key from firecrawl.dev
  2. Set the API key as an environment variable named FIRECRAWL_API_KEY, or pass it with FirecrawlClient::create(apiKey: ...)

Here is a quick example using the current SDK API surface:

use Firecrawl\Client\FirecrawlClient;
use Firecrawl\Models\CrawlOptions;
use Firecrawl\Models\ScrapeOptions;

$client = FirecrawlClient::fromEnv();

$doc = $client->scrape(
    'https://firecrawl.dev',
    ScrapeOptions::with(formats: ['markdown'])
);

$crawl = $client->crawl(
    'https://firecrawl.dev',
    CrawlOptions::with(limit: 5)
);

echo $doc->getMarkdown();
echo 'Crawled pages: ' . count($crawl->getData());

Using the Laravel Facade

In a Laravel application you can use the Firecrawl facade or dependency injection:

use Firecrawl\Client\FirecrawlClient;
use Firecrawl\Laravel\Facades\Firecrawl;

// Via Facade
$doc = Firecrawl::scrape('https://example.com');

// Via Dependency Injection
class ScrapeController
{
    public function __construct(
        private readonly FirecrawlClient $firecrawl,
    ) {}

    public function index()
    {
        $doc = $this->firecrawl->scrape('https://example.com');
        return response()->json(['markdown' => $doc->getMarkdown()]);
    }
}

Scraping a URL

To scrape a single URL, use the scrape method.

use Firecrawl\Models\Document;
use Firecrawl\Models\ScrapeOptions;

$doc = $client->scrape(
    'https://firecrawl.dev',
    ScrapeOptions::with(
        formats: ['markdown', 'html'],
        onlyMainContent: true,
        waitFor: 5000,
    )
);

echo $doc->getMarkdown();
echo $doc->getMetadata()['title'] ?? '';

JSON Extraction

Extract structured JSON with JsonFormat via the scrape endpoint:

use Firecrawl\Models\JsonFormat;
use Firecrawl\Models\ScrapeOptions;

$jsonFmt = JsonFormat::with(
    prompt: 'Extract the product name and price',
    schema: [
        'type' => 'object',
        'properties' => [
            'name' => ['type' => 'string'],
            'price' => ['type' => 'number'],
        ],
    ],
);

$doc = $client->scrape(
    'https://example.com/product',
    ScrapeOptions::with(formats: [$jsonFmt])
);

print_r($doc->getJson());

Crawling a Website

To crawl a website and wait for completion, use crawl.

use Firecrawl\Models\CrawlOptions;
use Firecrawl\Models\ScrapeOptions;

$job = $client->crawl(
    'https://firecrawl.dev',
    CrawlOptions::with(
        limit: 50,
        maxDiscoveryDepth: 3,
        scrapeOptions: ScrapeOptions::with(formats: ['markdown']),
    )
);

echo 'Status: ' . $job->getStatus();
echo 'Progress: ' . $job->getCompleted() . '/' . $job->getTotal();

foreach ($job->getData() as $page) {
    echo $page->getMetadata()['sourceURL'] ?? '';
}

Start a Crawl

Start a job without waiting using startCrawl.

use Firecrawl\Models\CrawlOptions;

$start = $client->startCrawl(
    'https://firecrawl.dev',
    CrawlOptions::with(limit: 100)
);

echo 'Job ID: ' . $start->getId();

Checking Crawl Status

Check crawl progress with getCrawlStatus.

$status = $client->getCrawlStatus($start->getId());
echo 'Status: ' . $status->getStatus();
echo 'Progress: ' . $status->getCompleted() . '/' . $status->getTotal();

Cancelling a Crawl

Cancel a running crawl with cancelCrawl.

$result = $client->cancelCrawl($start->getId());
print_r($result);

Crawl Errors

Fetch crawl-level errors (if any) with getCrawlErrors.

$errors = $client->getCrawlErrors($start->getId());
print_r($errors);

Mapping a Website

Discover links on a site using map.

use Firecrawl\Models\MapOptions;

$data = $client->map(
    'https://firecrawl.dev',
    MapOptions::with(
        limit: 100,
        search: 'blog',
    )
);

foreach ($data->getLinks() as $link) {
    echo ($link['url'] ?? '') . ' - ' . ($link['title'] ?? '');
}

Searching the Web

Search with optional search settings using search.

use Firecrawl\Models\SearchOptions;

$results = $client->search(
    'firecrawl web scraping',
    SearchOptions::with(limit: 10)
);

foreach ($results->getWeb() as $result) {
    echo ($result['title'] ?? '') . ' - ' . ($result['url'] ?? '');
}

Batch Scraping

Scrape multiple URLs in parallel using batchScrape.

use Firecrawl\Models\BatchScrapeOptions;
use Firecrawl\Models\ScrapeOptions;

$job = $client->batchScrape(
    ['https://firecrawl.dev', 'https://firecrawl.dev/blog'],
    BatchScrapeOptions::with(
        options: ScrapeOptions::with(formats: ['markdown']),
    )
);

foreach ($job->getData() as $doc) {
    echo $doc->getMarkdown();
}

For manual async control, use startBatchScrape, getBatchScrapeStatus, and cancelBatchScrape:

use Firecrawl\Models\BatchScrapeOptions;
use Firecrawl\Models\ScrapeOptions;

$start = $client->startBatchScrape(
    ['https://firecrawl.dev', 'https://firecrawl.dev/blog'],
    BatchScrapeOptions::with(
        options: ScrapeOptions::with(formats: ['markdown']),
    )
);

$status = $client->getBatchScrapeStatus($start->getId());
echo 'Batch status: ' . $status->getStatus();

$cancel = $client->cancelBatchScrape($start->getId());
print_r($cancel);

Agent

Run an AI-powered agent with agent.

use Firecrawl\Models\AgentOptions;

$result = $client->agent(
    AgentOptions::with(
        prompt: 'Find the pricing plans for Firecrawl and compare them',
    )
);

print_r($result->getData());

With a JSON schema for structured output:

use Firecrawl\Models\AgentOptions;

$result = $client->agent(
    AgentOptions::with(
        prompt: 'Extract pricing plan details',
        urls: ['https://firecrawl.dev'],
        schema: [
            'type' => 'object',
            'properties' => [
                'plans' => [
                    'type' => 'array',
                    'items' => [
                        'type' => 'object',
                        'properties' => [
                            'name' => ['type' => 'string'],
                            'price' => ['type' => 'string'],
                        ],
                    ],
                ],
            ],
        ],
    )
);

print_r($result->getData());

For manual async control, use startAgent, getAgentStatus, and cancelAgent:

use Firecrawl\Models\AgentOptions;

$start = $client->startAgent(
    AgentOptions::with(
        prompt: 'Summarize what Firecrawl does in one sentence',
        urls: ['https://firecrawl.dev'],
    )
);

$status = $client->getAgentStatus($start->getId());
echo 'Agent status: ' . $status->getStatus();

$cancel = $client->cancelAgent($start->getId());
print_r($cancel);

Usage & Metrics

Check concurrency and remaining credits:

use Firecrawl\Models\ConcurrencyCheck;
use Firecrawl\Models\CreditUsage;

$concurrency = $client->getConcurrency();
echo 'Concurrency: ' . $concurrency->getConcurrency() . '/' . $concurrency->getMaxConcurrency();

$credits = $client->getCreditUsage();
echo 'Remaining credits: ' . $credits->getRemainingCredits();

Laravel AI SDK Tools

The SDK ships native tool classes for the Laravel AI SDK (laravel/ai), so agents can scrape, search, map, and crawl the web without an MCP server or manual HTTP calls.

composer require laravel/ai
Requires firecrawl/firecrawl-sdk 1.9.0 or later, plus laravel/ai 0.9 or later (PHP 8.3+, Laravel 12+). The tool classes only load when laravel/ai is installed.

The tools resolve the FirecrawlClient from the container, so your existing config/firecrawl.php and FIRECRAWL_API_KEY setup is reused as is:

use Firecrawl\Laravel\Tools\FirecrawlScrape;
use Firecrawl\Laravel\Tools\FirecrawlSearch;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Promptable;
use Stringable;

class ResearchAssistant implements Agent, HasTools
{
    use Promptable;

    public function instructions(): Stringable|string
    {
        return 'You are a research assistant. Use the Firecrawl tools to find and read web content.';
    }

    public function tools(): iterable
    {
        return [
            new FirecrawlScrape,
            new FirecrawlSearch,
        ];
    }
}

$response = ResearchAssistant::make()->prompt('What does firecrawl.dev do?');

Available Tools

ClassTool nameWhat it does
FirecrawlScrapefirecrawl_scrapeScrape one URL and return clean markdown
FirecrawlSearchfirecrawl_searchSearch the web, returns JSON results
FirecrawlMapfirecrawl_mapDiscover the URLs on a website
FirecrawlCrawlfirecrawl_crawlCrawl multiple pages into markdown

The tool names match the Firecrawl MCP server, so agents see the same vocabulary across surfaces. Register all four at once with the spread helper:

use Firecrawl\Laravel\Tools\FirecrawlTools;

public function tools(): iterable
{
    return [...FirecrawlTools::all()];
}

Every tool also accepts an explicit client, for one off credentials or use outside the container. FirecrawlTools::all() passes one to all four tools:

use Firecrawl\Client\FirecrawlClient;

$client = FirecrawlClient::create(apiKey: 'fc-other-key');

new FirecrawlScrape($client);
// or
FirecrawlTools::all($client);

Tool Parameters

Each tool exposes a small, model-facing schema. These are the parameters the agent can pass:

ToolParameterDescription
firecrawl_scrapeurl (required)Absolute URL of the page to scrape, including the scheme
firecrawl_searchquery (required)The search query
limitMaximum results to return, 1–20. Defaults to 5
firecrawl_mapurl (required)Base URL of the website to map
searchOptional term to filter discovered URLs by relevance
limitMaximum URLs to return, 1–500. Defaults to 100
firecrawl_crawlurl (required)URL to start crawling from
limitMaximum pages to crawl, 1–25. Defaults to 5

Out-of-range limit values are clamped to the nearest bound rather than rejected, so a model that asks for 99 search results gets 20 instead of an error.

Tool Behavior

Tool failures such as rate limits, timeouts, and invalid URLs are returned to the model as readable error strings rather than thrown, so agent runs degrade gracefully. Outputs are capped to stay within model context: scrape results truncate at 80,000 characters, crawl pages at 15,000 characters each under a 100,000 character whole result budget, and search and map results drop tail items with an explicit omitted marker.

firecrawl_search and firecrawl_map return JSON arrays of results. firecrawl_scrape returns the page as markdown.

Crawl Results

firecrawl_crawl waits up to 55 seconds for the crawl to finish, then returns a JSON object that makes the outcome explicit. Failed, cancelled, or partial crawls stay visible to the model through the status field rather than being silently truncated:

{
  "status": "completed",
  "completed": 5,
  "total": 5,
  "pages": [
    { "url": "https://example.com/docs", "markdown": "..." }
  ]
}

Two optional fields appear when results don’t fit: omittedPages counts pages dropped to stay inside the output budget, and note tells the model that more pages exist on the server and that it should use a smaller limit or scrape specific pages with firecrawl_scrape. The tool reports pagination instead of following it, so agents that need every page of a large crawl should use FirecrawlClient directly.

If the crawl is still running when the wait expires, the tool says so and reminds the model the crawl may still complete server-side. Crawl starts carry a UUID idempotency key, so an HTTP-level retry never creates a duplicate crawl.

If your agent runs inside a queued job, keep the crawl limit small or raise the worker’s job timeout. The wait, poll cadence, and per-page cap are protected properties, so extend the class to tune them:

use Firecrawl\Laravel\Tools\FirecrawlCrawl;

class PatientCrawl extends FirecrawlCrawl
{
    protected int $timeoutSeconds = 120;
    protected int $pollIntervalSeconds = 5;
    protected int $pageCharacterLimit = 30000;
}

Browser

The PHP SDK includes Browser Sandbox helpers.

Create a Session

use Firecrawl\Models\BrowserCreateResponse;

$session = $client->browser(ttl: 120, activityTtl: 60, streamWebView: true);
echo $session->getId();
echo $session->getCdpUrl();
echo $session->getLiveViewUrl();

Execute Code

use Firecrawl\Models\BrowserExecuteResponse;

$run = $client->browserExecute(
    sessionId: $session->getId(),
    code: 'await page.goto("https://example.com"); console.log(await page.title());',
    language: 'node',
    timeout: 60,
);

echo $run->getStdout();
echo $run->getExitCode();

Scrape-Bound Interactive Session

Use a scrape job ID to run follow-up browser code in the same replayed context:

  • interact(...) runs code in the scrape-bound browser session (and initializes it on first use).
  • stopInteractiveBrowser(...) explicitly stops the interactive session when you are done.
use Firecrawl\Models\BrowserExecuteResponse;
use Firecrawl\Models\BrowserDeleteResponse;
use Firecrawl\Models\ScrapeOptions;

$doc = $client->scrape(
    'https://example.com',
    ScrapeOptions::with(formats: ['markdown'])
);

$scrapeJobId = $doc->getMetadata()['scrapeId'] ?? null;
if ($scrapeJobId === null) {
    throw new RuntimeException('scrapeId not found in metadata');
}

$scrapeRun = $client->interact(
    jobId: $scrapeJobId,
    code: 'console.log(page.url());',
    language: 'node',
    timeout: 60,
);

echo $scrapeRun->getStdout();

$deleted = $client->stopInteractiveBrowser($scrapeJobId);
echo 'Deleted: ' . ($deleted->isSuccess() ? 'true' : 'false');

List & Close Sessions

use Firecrawl\Models\BrowserListResponse;
use Firecrawl\Models\BrowserSession;

$active = $client->listBrowsers('active');
foreach ($active->getSessions() as $s) {
    echo $s->getId() . ' - ' . $s->getStatus();
}

$closed = $client->deleteBrowser($session->getId());
echo 'Closed: ' . ($closed->isSuccess() ? 'true' : 'false');

Configuration

FirecrawlClient::create() supports the following options:

OptionTypeDefaultDescription
apiKeystringFIRECRAWL_API_KEY env varYour Firecrawl API key
apiUrlstringhttps://api.firecrawl.dev (or FIRECRAWL_API_URL)API base URL
timeoutSecondsfloat300HTTP request timeout in seconds
maxRetriesint3Automatic retries for transient failures
backoffFactorfloat0.5Exponential backoff factor in seconds
httpClientGuzzleHttp\ClientInterfaceBuilt from timeoutCustom Guzzle-compatible HTTP client
use Firecrawl\Client\FirecrawlClient;

$client = FirecrawlClient::create(
    apiKey: 'fc-your-api-key',
    apiUrl: 'https://api.firecrawl.dev',
    timeoutSeconds: 300,
    maxRetries: 3,
    backoffFactor: 0.5,
);

Custom HTTP Client

You can pass a pre-configured GuzzleHttp\ClientInterface implementation to control connection pooling, middleware, proxy settings, and other HTTP features. When provided, the timeoutSeconds setting is ignored in favor of the client’s own configuration.

use Firecrawl\Client\FirecrawlClient;
use GuzzleHttp\Client as GuzzleClient;

$guzzle = new GuzzleClient([
    'proxy' => 'http://proxy.example.com:8080',
    'timeout' => 60,
    'connect_timeout' => 10,
]);

$client = FirecrawlClient::create(
    apiKey: 'fc-your-api-key',
    httpClient: $guzzle,
);

Error Handling

The SDK throws runtime exceptions under Firecrawl\Exceptions.

use Firecrawl\Exceptions\AuthenticationException;
use Firecrawl\Exceptions\FirecrawlException;
use Firecrawl\Exceptions\JobTimeoutException;
use Firecrawl\Exceptions\RateLimitException;

try {
    $doc = $client->scrape('https://example.com');
} catch (AuthenticationException $e) {
    echo 'Auth failed: ' . $e->getMessage();
} catch (RateLimitException $e) {
    echo 'Rate limited: ' . $e->getMessage();
} catch (JobTimeoutException $e) {
    echo 'Job ' . $e->getJobId() . ' timed out after ' . $e->getTimeoutSeconds() . 's';
} catch (FirecrawlException $e) {
    echo 'Error ' . $e->getStatusCode() . ': ' . $e->getMessage();
}

Are you an AI agent that needs a Firecrawl API key? See firecrawl.dev/agent-onboarding/SKILL.md for automated onboarding instructions.

Was this page helpful?Suggest editsRaise issue