Firecrawl turns web pages into something a language model can use. Give it a URL and it returns clean Markdown, HTML, structured JSON or a screenshot, with JavaScript rendered and boilerplate stripped; give it a site and it crawls or maps every page; give it a query and it searches the web and returns the full content of the results. It can click, scroll and type before extracting, parses PDFs and DOCX files, and has an agent endpoint that gathers data from a plain-language request.
It is the most-starred project in its category, at about 185,000 stars, built by the company behind Mendable and offered two ways. The hosted API at firecrawl.dev handles proxies and bot walls and adds features the open-source build lacks; this repository is the same API, AGPL-3.0, that you can run yourself with Docker Compose. SDKs for Python, Node, Go, Java, Rust and PHP are MIT-licensed, and an MCP server and CLI plug it into coding agents.
- Repository: github.com/firecrawl/firecrawl
- Licence: AGPL-3.0 (GNU Affero General Public License v3.0)
- Language: TypeScript. Stars: 185.2K. Forks: 9,935. Last push: Sep 27, 2026.
- Scan: safe, Sep 25, 2026, commit f75a8d4
Who it is for
Developers building RAG pipelines, research agents or monitoring tools that need web content as clean text, and teams that want the same API self-hosted for cost or data-control reasons.
Getting started
1. Python SDK, for the hosted API (get a key at firecrawl.dev) or your own instance
pip install firecrawl-py2. Or the Node SDK
npm install firecrawl3. Self-host: clone and start the Docker Compose stack
git clone https://github.com/firecrawl/firecrawl.git && cd firecrawl && docker compose up -d4. Scrape a page through your own instance
curl -X POST http://localhost:3002/v2/scrape -H 'Content-Type: application/json' -d '{"url": "firecrawl.dev"}'The self-hosted API needs no key because it has no authentication by default. The project's own SELF_HOST.md says to keep it on a trusted network until you add authentication, TLS and network policy, and notes the Compose file defines no persistent volumes.
Safety scan
We cloned firecrawl/firecrawl at commit f75a8d4 on Sep 25, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.
- One secret hit: a private key in apps/api/src/__tests__/snips/v2/scrape-skip-tls.test.ts, named SELF_SIGNED_KEY. The test starts an HTTPS server with a deliberately bad certificate to check the skipTlsVerification option; the key protects nothing. The bare-IP URLs are also tests, including phishing-style examples for the URL canonicalizer.
- Three pattern hits. apps/api/Dockerfile installs Rust with rustup's official curl | sh at image build time, on the build machine. The other two are in tests and examples: a Java SDK test that uses webhook.site as a placeholder, and a blog notebook quoting Heroku's installer.
- Install hooks matter only if you build from source: apps/api/native's install script runs pnpm build to compile a Rust module (with its build.rs), and apps/api's prepare script sets up husky git hooks for contributors. One committed binary, the Java SDK's Gradle wrapper jar.
- 48 known advisories and none critical. The API itself (apps/api/pnpm-lock.yaml, 930 packages) has 1 high and 1 moderate. Most of the rest sit in an example project (9 high), the Rust SDK's Cargo.lock (1 high, rustls-webpki) and unrated entries in the Go HTML-to-Markdown service. Dependabot is configured.
- 31 workflows, none using pull_request_target, and 33 of 41 third-party actions pinned to a commit, one of the best ratios in this series. Licence and contributing guide present; no security policy file or CodeQL.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | 1 candidate found and read; see the notes above. |
| Suspicious code | 3 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 2 npm lifecycle scripts. 1 Cargo build script. 1 installer script |
| Committed binaries | 1 executable or compiled object committed; listed under the raw findings. |
| CI workflows | 31 workflows. None use pull_request_target. 8 of 41 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often firecrawl.dev, docs.firecrawl.dev, api.firecrawl.dev, cloud.google.com. 9 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | 48 advisories across 2,382 pinned packages: 0 critical, 11 high, 22 moderate, 4 low, 11 unrated. apps/api/pnpm-lock.yaml: 930 packages, 2 advisories; apps/api/sharedLibs/go-html-to-md/go.mod: 7 packages, 10 advisories; apps/go-html-to-md-service/go.mod: 12 packages, 11 advisories; apps/js-sdk/firecrawl/pnpm-lock.yaml: 453 packages, 0 advisories; apps/js-sdk/pnpm-lock.yaml: 83 packages, 0 advisories; apps/playwright-service-ts/pnpm-lock.yaml: 128 packages, 0 advisories; apps/rust-sdk/Cargo.lock: 228 packages, 10 advisories; apps/test-site/pnpm-lock.yaml: 430 packages, 0 advisories; apps/test-suite/pnpm-lock.yaml: 438 packages, 1 advisories; apps/ui/ingestion-ui/pnpm-lock.yaml: 325 packages, 0 advisories; examples/deep-research-apartment-finder/requirements.txt: 3 packages, 1 advisories; examples/deepseek-v3-crawler/requirements.txt: 2 packages, 1 advisories; examples/gemini-2.5-web-extractor/requirements.txt: 4 packages, 2 advisories; examples/gpt-4.1-company-researcher/requirements.txt: 3 packages, 2 advisories; examples/gpt-4.1-web-crawler/requirements.txt: 3 packages, 1 advisories; examples/o4-mini-web-crawler/requirements.txt: 3 packages, 1 advisories; examples/scrape_and_analyze_airbnb_data_e2b/package-lock.json: 96 packages, 21 advisories. |
| Project hygiene | Has automated dependency updates, licence file, contributing guide. Missing security policy, CodeQL. |
| OpenSSF Scorecard | Not scored: the project is not in Scorecard's weekly index. |
The raw findings
Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.
Secret candidates (1, redacted)
| Where | Rule | Match |
|---|---|---|
| apps/api/src/__tests__/snips/v2/scrape-skip-tls.test.ts:11 | private-key | -----B…--- (27 chars) |
Pattern hits (3)
| Where | Rule | Match |
|---|---|---|
| apps/api/Dockerfile:44 | download-piped-to-shell | RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path \ |
| apps/java-sdk/src/test/java/com/firecrawl/CrawlTest.java:245 | exfil-host (test/example) | .url("https://webhook.site/test") |
| examples/blog-articles/deploying_web_scrapers/notebook.ipynb:846 | download-piped-to-shell (test/example) | "$ curl https://cli-assets.heroku.com/install.sh | sh # Linux\n", |
URLs to bare IP addresses (9)
| Where | Rule | Match |
|---|---|---|
| apps/api/src/lib/__tests__/url-utils.test.ts:89 | ip-literal-url | expect(hasReachableHost("https://8.8.8.8/")).toBe(true); |
| apps/api/src/lib/__tests__/url-utils.test.ts:90 | ip-literal-url | expect(hasReachableHost("https://93.184.216.34/")).toBe(true); |
| apps/api/src/lib/__tests__/url-utils.test.ts:92 | ip-literal-url | expect(hasReachableHost("https://1.1.1.10:8080/p")).toBe(true); |
| apps/api/src/lib/threat-protection/providers/web-risk/canonicalize.test.ts:19 | ip-literal-url | "http://168.188.99.26/.secure/www.ebay.com/", |
| apps/api/src/lib/threat-protection/providers/web-risk/canonicalize.test.ts:22 | ip-literal-url | "http://195.127.0.11/uploads/%20%20%20%20/.verify/.eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/", |
| apps/api/src/lib/threat-protection/providers/web-risk/canonicalize.test.ts:23 | ip-literal-url | "http://195.127.0.11/uploads/%20%20%20%20/.verify/.eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/", |
| apps/api/src/lib/threat-protection/providers/web-risk/canonicalize.test.ts:29 | ip-literal-url | ["http://3279880203/blah", "http://195.127.0.11/blah"], |
| apps/api/src/lib/threat-protection/providers/web-risk/canonicalize.test.ts:138 | ip-literal-url | expect(generateUrlExpressions("http://1.2.3.4/1/")).toEqual([ |
| apps/api/src/lib/threat-protection/verdict.test.ts:422 | ip-literal-url | "http://195.127.0.11/", |
npm lifecycle scripts (2)
apps/api/native/package.jsoninstall:pnpm buildapps/api/package.jsonprepare:cd ../.. && husky ./apps/api/.husky
Installer scripts (1)
- apps/redis/start-redis-server.sh, 31 lines
Committed binaries (1)
apps/java-sdk/gradle/wrapper/gradle-wrapper.jar: JAR, 44 KB
Worst known vulnerabilities (24 of 48)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-mwp4-54f8-5fhr | high | ip-address@6.4.0 | ip-address: Address4 decodes leading-zero octets as decimal while resolvers decode them as octal, allowing SSRF and trus… |
| GHSA-82j2-j2ch-gfr8 | high | rustls-webpki@0.103.4 | rustls-webpki: Denial of service via panic on malformed CRL BIT STRING |
| GHSA-35jp-ww65-95wh | high | axios@1.15.2 | axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `config.proxy` |
| GHSA-777c-7fjr-54vf | high | axios@1.15.2 | Allocation of Resources Without Limits or Throttling in Axios |
| GHSA-gcfj-64vw-6mp9 | high | axios@1.15.2 | Axios Node HTTP adapter can use an inherited proxy after interceptor config cloning |
| GHSA-hfxv-24rg-xrqf | high | axios@1.15.2 | Axios: Regular Expression Denial of Service (ReDoS) via Cookie Name Injection |
| GHSA-j5f8-grm9-p9fc | high | axios@1.15.2 | Axios: Proxy-Authorization header leaks to redirect target when proxy is re-evaluated to direct connection |
| GHSA-p92q-9vqr-4j8v | high | axios@1.15.2 | Axios: Proxy-Authorization Credential Leak to Origin Server Across HTTP-to-HTTPS Redirect in Axios Node.js HTTP Adapter |
| GHSA-pjwm-pj3p-43mv | high | axios@1.15.2 | axios's shouldBypassProxy does not recognize IPv4-mapped IPv6 addresses, allowing NO_PROXY bypass (incomplete fix for CV… |
| GHSA-hmw2-7cc7-3qxx | high | form-data@4.0.5 | form-data: CRLF injection in form-data via unescaped multipart field names and filenames |
| GHSA-96hv-2xvq-fx4p | high | ws@8.18.3 | ws: Memory exhaustion DoS from tiny fragments and data chunks |
| GHSA-v2v4-37r5-5v8g | moderate | ip-address@6.4.0 | ip-address has XSS in Address6 HTML-emitting methods |
| GHSA-5cv4-jp36-h3mw | moderate | golang.org/x/net@0.41.0 | Go Net HTML parser is vulnerable to denial of service |
| GHSA-434x-w66g-qw3r | moderate | bytes@1.10.1 | bytes has integer overflow in BytesMut::reserve |
| GHSA-2mjx-qc3c-rqvc | moderate | rustls@0.23.31 | TLS 1.3 handshake messages incorrectly accepted across encryption level boundaries |
| GHSA-pwjx-qhcg-rvj4 | moderate | rustls-webpki@0.103.4 | webpki: CRLs not considered authoritative by Distribution Point due to faulty matching logic |
| GHSA-7gcf-g7xr-8hxj | moderate | serde_with@3.14.0 | serde_with: KeyValueMap serialization panics on empty sequence or map entries |
| GHSA-r6v5-fh4h-64xc | moderate | time@0.3.41 | time vulnerable to stack exhaustion Denial of Service attack |
| GHSA-8cw4-87c7-c6xx | moderate | csv-parse@4.16.3 | node-csv: Prototype replacement still reachable via columns path |
| GHSA-mf9w-mj56-hr94 | moderate | python-dotenv@1.0.0 | python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback |
| GHSA-mf9w-mj56-hr94 | moderate | python-dotenv@1.0.1 | python-dotenv: Symlink following in set_key allows arbitrary file overwrite via cross-device rename fallback |
| GHSA-gc5v-m9x4-r6x2 | moderate | requests@2.32.4 | Requests has Insecure Temp File Reuse in its extract_zipped_paths() utility function |
| GHSA-42h9-826w-cgv3 | moderate | axios@1.15.2 | Axios: Excessive recursion in formDataToJSON can cause denial of service |
| GHSA-7q8q-rj6j-mhjq | moderate | axios@1.15.2 | Axios: Nested axios option objects can consume polluted prototype values |
By the numbers
| Stars | 185.2K |
|---|---|
| Forks | 9,935 |
| Contributors | 175 |
| Commits | 6,389 |
| Open issues | 87 |
| Open pull requests | 565 |
| Releases | 35 |
| Latest release | v2.11.0 |
| Licence | AGPL-3.0 |
| Main language | TypeScript |
| Project age | 2 years |
| Last push | Sep 27, 2026 |
| Tracked files | 1,956 |
| Lines of code | 399.7K |
| Checkout size | 53 MB |
Lines by language: TypeScript 263.3K, Python 49.3K, Markdown 15.7K, JSON 14.3K, Rust 12.1K, Jupyter 9,099.
Questions
Is Firecrawl free?
The code is. The server is AGPL-3.0 and free to self-host, and the SDKs are MIT. The hosted API at firecrawl.dev is paid by credits, with a free allowance to start, and includes extras the open-source build lacks, such as its own scraping engine and proxy handling. If you modify the server and offer it to others, the AGPL requires sharing your changes.
Firecrawl or Crawl4AI?
Firecrawl is an API service first: you call endpoints from any language, and self-hosting means running a multi-container stack with Redis, RabbitMQ and PostgreSQL. Crawl4AI is a Python library first that runs a browser inside your own process, with a Docker server as an option. For a hosted API with SDKs, Firecrawl; for crawling inside a Python program, Crawl4AI.
Does Firecrawl respect robots.txt?
By default, yes. The crawler reads each site's robots.txt and skips disallowed URLs, and the code offers an ignoreRobotsTxt option for crawls that override it. The README is explicit that respecting each site's policies and terms is the user's responsibility, which applies equally to a self-hosted instance.
This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.
