Skip to main content
Tutorial

Mastra + fastCRW: TypeScript Agents, One Binary

Build Mastra TypeScript agents with live web data via fastCRW. Typed Zod tools, Firecrawl-compatible REST, and a single ~8 MB self-hostable binary.

fastcrw
By RecepJune 20, 20269 min readLast updated: June 2, 2026

By the fastCRW team · Benchmarks and footprint verified 2026-05-18 · fastCRW launch pricing expires 2026-06-01 · Verify independently before relying on any figure.

Disclosure: we build fastCRW. This is a vendor-authored tutorial, so weight it accordingly — but the latency tail and the missing endpoints below are stated plainly because a setup guide that hides them wastes your afternoon, not ours.

Mastra TypeScript agents and the web-data gap

Mastra is a TypeScript-native agent framework, and the mastra typescript fastcrw integration pattern exists because Mastra gives you the orchestration — agents, tools, workflows, memory — but no opinion on how to fetch the live web. The moment one of your agents needs the current state of a page rather than the model's training data, you need a tool that turns a URL into clean, LLM-ready markdown. This guide wires fastCRW in as that tool: a typed Zod tool over a Firecrawl-compatible REST API, backed by an engine that is a single ~8 MB binary you can self-host.

Where Mastra tools fit in the agent loop

A Mastra agent reasons, decides it needs information, calls a tool, and folds the result back into the next reasoning step. A web-fetch tool sits squarely on that path: the agent passes a URL (or a query), the tool returns markdown, and the agent reads it. The quality of that markdown is the ceiling on the agent's answer — garbage in, confidently-wrong out. So the two things that matter for a Mastra web tool are the cleanliness of the extracted content and the predictability of the call's latency and cost.

Why TypeScript teams want typed tool I/O

Mastra leans on Zod for tool input and output schemas, which means the agent runtime validates arguments before your tool runs and validates the shape your tool returns. That discipline is the whole reason teams pick a typed framework. A web tool should honor it: declare exactly what it accepts (a URL, an optional renderer) and exactly what it returns (markdown plus metadata), so a malformed tool call fails at the boundary instead of three steps downstream.

Define a fastCRW tool with a Zod schema

fastCRW exposes a Firecrawl-compatible REST surface — /v1/scrape, /v1/crawl, /v1/map, /v1/search — so from Node you are just making typed fetch calls and wrapping them in createTool.

createTool with Zod input/output

Define the tool once, with explicit schemas on both sides:

  • Input schema: { url: z.string().url(), renderer: z.enum(["auto","http","lightpanda","chrome"]).default("auto") }.
  • Output schema: { markdown: z.string(), title: z.string().optional(), sourceUrl: z.string() }.
  • execute: calls fastCRW and maps the response onto the output schema. Because Mastra validates the return against the schema, a partial or error response surfaces as a typed failure your agent can branch on.

Calling the Firecrawl-compatible REST from Node

Inside execute, POST to /v1/scrape with { url, formats: ["markdown"] } and an Authorization: Bearer header. Pull data.markdown off the response and return it. Keep the call abortable with an AbortController timeout — you will want that when we discuss the latency tail below.

Base-URL swap from Firecrawl to fastcrw.com

If you already have a Mastra tool built against Firecrawl's SDK, the migration is a base-URL change, not a rewrite. Point the client's API URL at https://fastcrw.com (or your self-hosted host) and keep everything else. The honest caveat from our own compatibility notes: a few response field names and the error-envelope shape diverge slightly, and cloud-only Firecrawl specialties have no fastCRW equivalent — validate that short list once before cutover. See the base-URL swap walkthrough for the exact diff.

A worked example: a research workflow

The canonical Mastra use case is a research agent: find sources, read them, synthesize. fastCRW covers both halves of that.

Search then scrape to markdown

Add a second tool over /v1/search so the agent can discover URLs before it reads them. The flow is: the agent calls the search tool with a query, gets back a ranked list of results, picks the ones worth reading, and calls the scrape tool on each. Search bills 1 credit per query and a scrape bills 1 credit per page on the http or lightpanda renderer.

Wiring the tool into a Mastra agent

Register both tools on the agent's tools map and write an instruction that tells the model when to reach for them: search when it lacks sources, scrape when it has a specific URL. Mastra handles the tool-call loop; your tools just return clean markdown, and the typed output schema keeps each result well-formed on the way back into the model context.

Structured JSON output for downstream steps

When a downstream workflow step needs fields rather than prose, switch the scrape call to formats: ["json"] with a jsonSchema. fastCRW runs an LLM extraction pass and returns structured data that maps onto a Mastra/Zod output schema. Note the cost: any request with formats: ["json"] bills 5 credits, and LLM extraction supports OpenAI and Anthropic providers only — plan for that if you standardize on another model.

One binary behind the whole stack

Mastra is deliberately infra-light, so the worst thing you can bolt onto it is a heavy cloud dependency you cannot run yourself. fastCRW's engine is the opposite of heavy.

Single ~8 MB AGPL-3.0 binary, 1 container

fastCRW is a single statically-linked Rust binary distributed as a roughly 8 MB Docker image running in 1 container (plus an optional sidecar). These are structural facts from the repo README, not benchmark claims. The default Docker Compose ships the lightweight lightpanda renderer; chrome is opt-in.

Self-host for $0 vs managed cloud

The engine is AGPL-3.0, so self-hosting it costs nothing beyond your own server — you can run the same engine your managed calls would hit, on a small VPS, and pay zero per-credit cost. For teams that would rather not run anything, the managed cloud at fastcrw.com meters the same operations at the credit costs above. The point for a Mastra app is that the choice is reversible: a config value, not a fork.

Footprint vs a multi-container cloud stack

For comparison, Firecrawl's self-host path is a multi-service stack reported in its own materials at roughly 2–3 GB across about 5 containers. That is the difference between "self-host is one docker run next to your Mastra service" and "self-host is a platform-team project." More on the single-binary tradeoff in our single-binary infrastructure write-up.

Performance honesty

The reason to put a number on this at all is that a web tool sits inline in the agent loop, so its latency is the user's latency.

Median latency beats Firecrawl

On the canonical three-way scrape benchmark — Firecrawl's own public 1,000-URL dataset, run via diagnose_3way.py on 2026-05-08 — fastCRW's p50 scrape latency was 1914 ms, which beats Firecrawl's 2305 ms and is effectively tied with Crawl4AI (1916 ms, two milliseconds apart). On accuracy, fastCRW posted the highest truth-recall of the three tools tested: 63.74% of 819 labeled URLs, versus Crawl4AI 59.95% and Firecrawl 56.04%, with 91.8% scrape-success of reachable URLs and 0 thrown errors across 3,000 requests. Higher recall on the first fetch is what keeps a Mastra agent from looping back to re-fetch.

The p90 in fast mode and timeout strategy

In fast mode, fastCRW's p90 is 4348 ms — the lowest of the three (Crawl4AI 4754 ms, Firecrawl 6937 ms). For a Mastra tool, that means you can set the AbortController timeout confidently from the fast-mode p90: let the agent fall back to a cached result or a re-plan on a timed-out edge instead of blocking, and reserve the chrome renderer for pages that actually need it. Verify the full split yourself at /benchmarks before quoting any figure internally.

Limitations to plan for

Two gaps will shape how you architect the Mastra side.

Stateless requests

fastCRW is stateless per request — there is no server-side session or cookie jar carried between calls. That is fine for Mastra, which already owns conversation state and memory: keep any cross-call context in the agent's state, not in the scraper. Just do not expect a login established in one scrape to persist into the next.

No /v1/agent autonomous endpoint

fastCRW has no /v1/agent (Spark-style) autonomous endpoint, no managed /v1/deep-research, and no multi-URL batch /v1/extract. The agentic loop lives in Mastra, which is exactly where a TypeScript team wants it. For many pages, iterate /v1/scrape concurrently or kick off a /v1/crawl job rather than reaching for a batch endpoint that does not exist. And there is no screenshot format — a request for formats: ["screenshot"] returns HTTP 422.

Where Firecrawl genuinely wins

An honest tutorial names the cases where you should not switch:

  • Cloud-only specialties. Firecrawl's heavy anti-bot fire-engine paths and its agentic/research endpoints are real strengths with no fastCRW equivalent. If your Mastra agent depends on those, use Firecrawl.
  • Ecosystem gravity. Firecrawl is the default name with more tutorials and examples. fastCRW wins on substitution, footprint, and the self-host floor — not on mindshare.
  • Non-fast-mode rendering. If you need to force the chrome renderer on every page rather than use fast-mode auto-selection, plan around the higher tail latency that produces.

Sources

  • fastCRW fact sheet and benchmark of record: github.com/us/crw (crw-opencore/README.md) · bench/server-runs/RESULT_3WAY_1000_FULL.md (diagnose_3way.py, 2026-05-08)
  • Pricing and credits: fastcrw.com/pricing (launch pricing verified 2026-05-18)
  • Mastra docs: mastra.ai/docs

Related: Node.js web scraping quickstart · Firecrawl SDK base-URL swap · Single-binary infrastructure

FAQ

Frequently asked questions

How do I add web scraping to a Mastra agent?
Define a Mastra tool with createTool and a Zod input/output schema, then have its execute function POST to fastCRW's Firecrawl-compatible /v1/scrape endpoint with { url, formats: ['markdown'] } and return data.markdown. Register the tool on your agent's tools map and instruct the model when to call it. Add a second tool over /v1/search if the agent needs to discover URLs first.
Can I define the fastCRW tool with a Zod schema?
Yes. fastCRW returns plain JSON over a REST API, so you declare a Zod input schema (e.g. a URL and an optional renderer) and a Zod output schema (markdown, title, source URL) on createTool. Mastra validates arguments before the tool runs and validates your return shape afterward, so malformed calls fail at the boundary instead of downstream.
Is fastCRW a drop-in Firecrawl replacement in TypeScript?
On the overlap surface (scrape, crawl, map, search) it is close to drop-in: point your Firecrawl SDK client's API URL at fastcrw.com or your self-hosted host and keep the rest. Validate a short known list first — a few response field names and the error-envelope shape diverge, and cloud-only Firecrawl specialties (heavy anti-bot, agentic/research endpoints) have no fastCRW equivalent.
Can I self-host fastCRW for my Mastra app?
Yes. The fastCRW engine is a single statically-linked Rust binary distributed as a roughly 8 MB Docker image in 1 container under AGPL-3.0, so you can run it next to your Mastra service for $0 beyond your own server cost. The default Docker Compose ships the lightweight lightpanda renderer; chrome is opt-in.
How does fastCRW scrape latency compare to Firecrawl?
On Firecrawl's own public 1,000-URL dataset (diagnose_3way.py, 2026-05-08), fastCRW's p50 scrape latency was 1914 ms versus Firecrawl's 2305 ms, while posting the highest truth-recall of the three tools tested (63.74% of 819 labeled URLs). In fast mode, fastCRW's p90 is 4348 ms — the lowest of the three — so you can set tool timeouts confidently. Reserve the chrome renderer only for pages that need full JS rendering.

Get Started

Try CRW Free

Self-host for free (AGPL) or use fastCRW cloud with 500 free credits — no credit card required.

Continue exploring

More tutorial posts

View category archive