Integrate Firecrawl with Mastra, the TypeScript framework for building AI agents and workflows.
Setup#
npm install @mastra/core firecrawl zodCreate .env file:
FIRECRAWL_API_KEY=your_firecrawl_keyOPENAI_API_KEY=your_openai_keyNote: If using Node < 20, install
dotenvand addimport 'dotenv/config'to your code.
Multi-Step Workflow#
This example demonstrates a complete workflow that searches, scrapes, and summarizes documentation using Firecrawl and Mastra.
import { createWorkflow, createStep } from "@mastra/core/workflows";import { z } from "zod";import { Firecrawl } from "firecrawl";import { Agent } from "@mastra/core/agent";const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY || "fc-YOUR_API_KEY"});const agent = new Agent({ name: "summarizer", instructions: "You are a helpful assistant that creates concise summaries of documentation.", model: "openai/gpt-5-nano",});// Step 1: Search with Firecrawl SDKconst searchStep = createStep({ id: "search", inputSchema: z.object({ query: z.string(), }), outputSchema: z.object({ url: z.string(), title: z.string(), }), execute: async ({ inputData }: { inputData: { query: string } }) => { console.log(`Searching: ${inputData.query}`); const searchResults = await firecrawl.search(inputData.query, { limit: 1 }); const webResults = (searchResults as any)?.web; if (!webResults || !Array.isArray(webResults) || webResults.length === 0) { throw new Error("No search results found"); } const firstResult = webResults[0]; console.log(`Found: ${firstResult.title}`); return { url: firstResult.url, title: firstResult.title, }; },});// Step 2: Scrape the URL with Firecrawl SDKconst scrapeStep = createStep({ id: "scrape", inputSchema: z.object({ url: z.string(), title: z.string(), }), outputSchema: z.object({ markdown: z.string(), title: z.string(), }), execute: async ({ inputData }: { inputData: { url: string; title: string } }) => { console.log(`Scraping: ${inputData.url}`); const scrapeResult = await firecrawl.scrape(inputData.url, { formats: ["markdown"], }); console.log(`Scraped: ${scrapeResult.markdown?.length || 0} characters`); return { markdown: scrapeResult.markdown || "", title: inputData.title, }; },});// Step 3: Summarize with Claudeconst summarizeStep = createStep({ id: "summarize", inputSchema: z.object({ markdown: z.string(), title: z.string(), }), outputSchema: z.object({ summary: z.string(), }), execute: async ({ inputData }: { inputData: { markdown: string; title: string } }) => { console.log(`Summarizing: ${inputData.title}`); const prompt = `Summarize the following documentation in 2-3 sentences:\n\nTitle: ${inputData.title}\n\n${inputData.markdown}`; const result = await agent.generate(prompt); console.log(`Summary generated`); return { summary: result.text }; },});// Create workflowexport const workflow = createWorkflow({ id: "firecrawl-workflow", inputSchema: z.object({ query: z.string(), }), outputSchema: z.object({ summary: z.string(), }), steps: [searchStep, scrapeStep, summarizeStep],}) .then(searchStep) .then(scrapeStep) .then(summarizeStep) .commit();async function testWorkflow() { const run = await workflow.createRunAsync(); const result = await run.start({ inputData: { query: "Firecrawl documentation" } }); if (result.status === "success") { const { summarize } = result.steps; if (summarize.status === "success") { console.log(`\n${summarize.output.summary}`); } } else { console.error("Workflow failed:", result.status); }}testWorkflow().catch(console.error);For more examples, check the Mastra documentation.

