# Firecrawl v1: the Python client against SpecStream

Date: 2026-11-10. Client: firecrawl-py (V1FirecrawlApp) 4.55.0.

## The one line a user changes

Before:

```
V1FirecrawlApp(api_key="fc-...")
```

After:

```
V1FirecrawlApp(api_key=KEY, api_url="https://api.specstream.ai/firecrawl")
```

The v1 client keeps the address's path and calls `/firecrawl/v1/scrape`, `/search`, `/map`, `/crawl` and `/crawl/{id}`. A scrape is one page read, a search one web search, a map one page read when the site gives a link of its own (the start address is always its first link), a crawl one page read for each page with text.

## Request parameters

Honoured means the check in the Measured column passed on the reply. Accepted, not applied means status 200 and the reply has the form of the same call without the parameter. Refused means the vendor's error body and status.

Counts: 27 honoured, 18 accepted, not applied, 2 refused.

| Parameter | Sent | Verdict | Measured |
|---|---|---|---|
| `scrape url` | `https://en.wikipedia.org/wiki/Web_scraping` | honoured | status 200; markdown of 28045 characters |
| `scrape formats` | `markdown`, `links`, `rawHtml`, `html` | honoured | status 200; links, rawHtml and html present |
| `scrape formats` | `links` only | honoured | status 200; links and no markdown |
| `scrape url (a page the site answers 404)` | a missing page | honoured | status 200; metadata.statusCode 404 |
| `scrape onlyMainContent` | `false` | accepted, not applied | status 200; the baseline's form |
| `scrape includeTags` | `p` | accepted, not applied | status 200; the baseline's form |
| `scrape excludeTags` | `nav` | accepted, not applied | status 200; the baseline's form |
| `scrape waitFor` | `1000` | accepted, not applied | status 200; the baseline's form |
| `scrape timeout` | `30000` | accepted, not applied | status 200; the baseline's form |
| `scrape mobile` | `true` | accepted, not applied | status 200; the baseline's form |
| `scrape blockAds` | `false` | accepted, not applied | status 200; the baseline's form |
| `scrape proxy` | `stealth` | accepted, not applied | status 200; the baseline's form |
| `scrape maxAge` | `0` | accepted, not applied | status 200; the baseline's form |
| `scrape headers` | one header | accepted, not applied | status 200; the baseline's form |
| `search query` | `web scraping history` | honoured | status 200; 5 results (the client's default limit 5) |
| `search limit` | `3` | honoured | status 200; 3 results |
| `search scrapeOptions.formats` | `markdown` | honoured | status 200; every result has markdown |
| `search query operator` | `site:wikipedia.org` | honoured | status 200; every result on the site |
| `search tbs` | `qdr:w` | honoured | status 200; results within the window |
| `search lang, country` | `de`, `de` | honoured | status 200; results from German sites |
| `search location` | `Germany` | accepted, not applied | status 200; the baseline's form |
| `search timeout` | `30000` | accepted, not applied | status 200; the baseline's form |
| `search filter` | `x` | accepted, not applied | status 200; the baseline's form |
| `map url` | `https://quotes.toscrape.com/` | honoured | status 200; 47 links, each a string |
| `map url (a page with no links)` | `https://example.com` | honoured | status 200; 1 link, the start address |
| `map limit` | `3` | honoured | status 200; 3 links |
| `map search` | `einstein` | honoured | status 200; first https://quotes.toscrape.com/author/Albert-Einstein |
| `map sitemapOnly` | `true` | honoured | status 200; 1020 links from the sitemaps |
| `map ignoreSitemap` | `true` | honoured | status 200; 37 links, 1036 with the sitemaps |
| `map includeSubdomains` | `false` | honoured | status 200; every link on the start host |
| `map ignoreQueryParameters (default true)` | not sent | honoured | status 200; 20 links, none with a query |
| `map timeout` | `30000` | accepted, not applied | status 200; the baseline's form |
| `crawl url` | `https://quotes.toscrape.com/` | honoured | status 200; status completed, 3 pages |
| `crawl limit` | `3` | honoured | status 200; 3 pages |
| `crawl maxDepth` | `1` | honoured | status 200; 2 pages, none deeper than one path part |
| `crawl includePaths` | `^/author/.*` | honoured | status 200; 3 pages |
| `crawl maxDiscoveryDepth` | `0` | honoured | status 200; 1 pages |
| `crawl scrapeOptions.formats` | `markdown`, `links` | honoured | status 200; the first page has both |
| `crawl allowExternalLinks` | `true` | accepted, not applied | status 200; status completed, 2 pages |
| `crawl deduplicateSimilarURLs` | `false` | accepted, not applied | status 200; status completed, 2 pages |
| `crawl delay` | `1` | accepted, not applied | status 200; status completed, 2 pages |
| `crawl webhook` | a URL | accepted, not applied | status 200; status completed, 2 pages |
| `async_crawl_url` | limit 12 | honoured | status 200; an id |
| `cancel_crawl (DELETE)` | a running job | honoured | status 200; {'success': True, 'status': 'cancelled'} |
| `check_crawl_status` | the cancelled job | honoured | status 200; status cancelled |
| `check_crawl_status` | an unknown id | refused | status 404; error raised |
| `api key` | a key that is not one | refused | status 401; error raised |

## Response fields

Present means the field holds a value in the replies to the calls above. Empty means it is there with no value. Absent means it is not in the reply.

Counts: 27 present, 0 empty, 3 absent.

| Field | State | Measured |
|---|---|---|
| `scrape data.markdown` | present | every scrape |
| `scrape data.html` | present | every scrape |
| `scrape data.rawHtml` | present | every scrape |
| `scrape data.links` | present | every scrape |
| `scrape data.screenshot` | absent | every scrape |
| `scrape data.metadata` | present | every scrape |
| `scrape data.metadata.title` | present | every scrape |
| `scrape data.metadata.description` | absent | every scrape |
| `scrape data.metadata.language` | absent | every scrape |
| `scrape data.metadata.sourceURL` | present | every scrape |
| `scrape data.metadata.statusCode` | present | every scrape |
| `scrape data.metadata.error` | present | every scrape |
| `search success` | present | every search |
| `search data[].url` | present | every result |
| `search data[].title` | present | every result |
| `search data[].description` | present | every result |
| `search data[].markdown` | present | every result |
| `search data[].metadata` | present | every result |
| `map links` | present | every map |
| `crawl status success` | present | every status read |
| `crawl status status` | present | every status read |
| `crawl status total` | present | every status read |
| `crawl status completed` | present | every status read |
| `crawl status creditsUsed` | present | every status read |
| `crawl status expiresAt` | present | every status read |
| `crawl status next` | present | every status read |
| `crawl status data` | present | every status read |
| `crawl status data[].markdown` | present | every page |
| `crawl status data[].links` | present | every page |
| `crawl status data[].metadata` | present | every page |
