# Data flow — firecrawl/firecrawl

> Canonical page: https://codevetter.com/unpack/firecrawl/data-flow

# Data flow — firecrawl/firecrawl

Scanned commit 556c12c9d293bc7392feb008946944d54068d04c (2026-09-21). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

Inbound JSON is validated by Zod schemas in controllers/v2/types.ts, then the request is stamped with team/org identity (ACUC), credits are checked against Autumn, and the job is either executed inline (sync scrape) or persisted as a NuQ row. The scrapeURL pipeline rewrites/permutes the URL, consults the blocklist, robots.txt, safe-mode and threat-protection policy, then walks a quality-ordered engine waterfall; each engine returns raw HTML which is normalized (removeUnwantedElements), converted to markdown (Go native lib or HTTP service), and passed through transformers (LLM extract, JSON, screenshots, change tracking) and postprocessors. Results are persisted to Bigtable job state, indexed to GCS+Postgres, billed per-feature, logged to PubSub/ClickHouse, and returned synchronously or delivered via webhook.
- Request validation is schema-first: scrapeRequestSchema.parse(req.body) runs inside an 'api.scrape.validate' OTel span, and ZodError is mapped centrally to a 400 with a strict 'Unrecognized key' message for v2. — [apps/api/src/controllers/v2/scrape.ts#L94-99](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/controllers/v2/scrape.ts#L94-L99) [apps/api/src/index.ts#L225-254](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/index.ts#L225-L254) [apps/api/src/controllers/v2/types.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/controllers/v2/types.ts)
- authenticateUser resolves the API key to an ACUC (auth credit usage chunk) via Redis-cached DB RPCs (authCreditUsageChunk), supports PREVIEW_TOKEN bypass, USE_DB_AUTHENTICATION mode, OAuth token introspection and MCP delegated credentials; the chunk carries team_id, org_id and feature flags used everywhere downstream. — [apps/api/src/controllers/auth.ts#L57-95](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/controllers/auth.ts#L57-L95) [apps/api/src/controllers/auth.ts#L635-748](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/controllers/auth.ts#L635-L748) [apps/api/src/db/rpc.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/db/rpc.ts)
- Credits are enforced by Autumn: checkCreditsMiddleware calls autumnService.checkCredits, fails open when Autumn is unreachable, clamps crawl/batch limits to remaining balance, and returns 402 with pricing links when denied; scrape-worker bills actual usage post-hoc via billScrapeJob → billTeam. — [apps/api/src/routes/shared.ts#L157-268](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/routes/shared.ts#L157-L268) [apps/api/src/services/worker/scrape-worker.ts#L143-161](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/worker/scrape-worker.ts#L143-L161) [apps/api/src/services/autumn/autumn.service.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/autumn/autumn.service.ts)
- Sync scrapes run a NuQJob in-process: scrapeController builds a NuQJob<ScrapeJobData> with skipNuq:true and calls processJobInternal directly inside teamConcurrencySemaphore.withSemaphore, so the queue row is skipped but the same worker code path executes. — [apps/api/src/controllers/v2/scrape.ts#L390-441](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/controllers/v2/scrape.ts#L390-L441) [apps/api/src/services/worker/team-semaphore.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/worker/team-semaphore.ts)
- Async jobs persist as rows in nuq.queue_scrape via scrapeQueue.addJob/addJobs (queue-jobs.ts), with routing through nuq-router/scrapeQueueFdb when the FDB backend is selected; workers consume via runNuqWorker → processJobInternal. — [apps/api/src/services/queue-jobs.ts#L154-226](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/queue-jobs.ts#L154-L226) [apps/api/src/services/worker/nuq-worker-runner.ts#L52-173](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/worker/nuq-worker-runner.ts#L52-L173) [apps/api/src/services/worker/nuq-router.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/worker/nuq-router.ts)
- Inside scrapeURL, URL handling precedes engines: safe-mode resolution strips credentials/login actions, threat-protection checkUrl runs before any fetch with a per-scrape dedup map, robots.txt is consulted via shouldCheckRobots/fetchRobotsTxt, and urlSpecificParams/rewriteUrl normalize the target. — [apps/api/src/scraper/scrapeURL/index.ts#L1237-1363](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/index.ts#L1237-L1363) [apps/api/src/scraper/scrapeURL/shouldCheckRobots.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/shouldCheckRobots.ts) [apps/api/src/lib/robots-txt.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/lib/robots-txt.ts)
- buildFallbackList computes the engine waterfall: exchange/index short-circuits first, then engines are filtered by required FeatureFlags and sorted by quality (index=1000, wikipedia=500, fire-engine chrome-cdp=50, playwright=20, tlsclient=10, fetch=5, file engines=-20); scrapeURLLoopIter runs each and deems success on markdown length + status code + absence of page error. — [apps/api/src/scraper/scrapeURL/engines/index.ts#L241-608](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/engines/index.ts#L241-L608) [apps/api/src/scraper/scrapeURL/engines/index.ts#L637-734](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/engines/index.ts#L637-L734) [apps/api/src/scraper/scrapeURL/index.ts#L662-803](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/index.ts#L662-L803)
- Engine output flows through htmlTransform (removeUnwantedElements), parseMarkdown, executeTransformers (llmExtract, deterministicJson, diff/changeTracking, audio/video, menu, product, query, sendToSearchIndex) and postprocessors (youtube) before becoming the Document returned to callers. — [apps/api/src/scraper/scrapeURL/index.ts#L30-31](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/index.ts#L30-L31) [apps/api/src/scraper/scrapeURL/index.ts#L720-743](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/index.ts#L720-L743) [apps/api/src/scraper/scrapeURL/transformers/index.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/transformers/index.ts) [apps/api/src/scraper/scrapeURL/postprocessors/index.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/scraper/scrapeURL/postprocessors/index.ts)
- Crawl state lives in Redis: StoredCrawl, per-URL locks (lockURL/lockURLsIndividually), job membership (addCrawlJob/addCrawlJobDone), robots.txt cache and crawl errors are managed by lib/crawl-redis.ts; completion fans out through crawlFinishedQueue to finishCrawlSuper. — [apps/api/src/lib/crawl-redis.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/lib/crawl-redis.ts) [apps/api/src/controllers/v2/crawl.ts#L324-380](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/controllers/v2/crawl.ts#L324-L380) [apps/api/src/services/worker/crawl-logic.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/worker/crawl-logic.ts)
- Terminal job state is written to Bigtable (job-state-store.ts, salted uuidv7 row keys) so scrape-status can read results after Redis TTLs expire; a SCRAPE_STATE_BARRIER_MS=2000 wait in scrape-worker lets the write land before sync responses return. — [apps/api/src/lib/job-state-store.ts#L113-204](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/lib/job-state-store.ts#L113-L204) [apps/api/src/services/worker/scrape-worker.ts#L116-120](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/worker/scrape-worker.ts#L116-L120)
- The URL index is a two-tier cache: metadata/verdicts in Postgres via db/rpc.ts functions (queryIndexAtSplitLevel, queryEngpickerVerdict) plus document blobs in GCS (getIndexFromGCS), with a Redis index-cache layer and screenshot URL re-signing. — [apps/api/src/services/index.ts#L1-60](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/index.ts#L1-L60) [apps/api/src/services/index-cache.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/index-cache.ts) [apps/api/src/db/rpc.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/db/rpc.ts)
- Webhooks are queued and delivered asynchronously: createWebhookSender builds per-crawl senders, delivery.ts filters events and persists deliveries, and shutdownWebhookQueue is part of the graceful-exit chain in index.ts. — [apps/api/src/services/webhook/delivery.ts#L32-49](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/webhook/delivery.ts#L32-L49) [apps/api/src/services/webhook/index.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/webhook/index.ts) [apps/api/src/index.ts#L176-188](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/index.ts#L176-L188)
- Request/audit logging is pipelined: logRequest/logScrape in services/logging/log_job.ts emit to PubSub/GCS, cclog-worker handles ClickHouse logs, and SIEM events go through lib/siem-logging to per-team transports (e.g. Azure Sentinel) via a RabbitMQ-delivered worker. — [apps/api/src/services/logging/log_job.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/logging/log_job.ts) [apps/api/src/services/cclog-worker.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/services/cclog-worker.ts) [apps/api/src/lib/siem-logging/index.ts](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/lib/siem-logging/index.ts) [apps/api/src/index.ts#L108-110](https://github.com/firecrawl/firecrawl/blob/556c12c9d293bc7392feb008946944d54068d04c/apps/api/src/index.ts#L108-L110)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
