Introducing our most accurate /search yet. Read the announcement →

Build a Finance Research Agent with Live Web Search + Scraping Using Firecrawl

placeholderHiba Fathima
May 25, 2026

Ask an LLM about a company's most recent earnings. It won't refuse. It will answer confidently, cite specific numbers, and mostly be wrong.

Not hallucinating wrong. Stale wrong. LLM training data has a cutoff. Financial data has a quarterly clock. The two don't align.

A finance research agent fixes this by removing the LLM from the loop on facts. It scrapes live sources first, then hands the data to the model for analysis. The model reasons. It doesn't recall.

In this guide, you'll build a finance research agent that scrapes live data from SEC EDGAR, company IR pages, and the web, then passes it to an LLM for analysis.

TL;DR

ProblemSolution
LLM training data goes stale quarterlyAgent scrapes live sources every run; no stale data
SEC filings are raw HTML/XBRLFirecrawl's Scrape returns clean markdown; no parser needed
Hard to find the right source across the webFirecrawl Search discovers relevant URLs dynamically
Extracting structured metrics (revenue, EPS) from long documentsFirecrawl scrape with Zod schema returns typed JSON

What is a finance research agent?

A finance research agent is a system that combines live web retrieval with an LLM to answer financial questions using current, sourced information. The key word is live. The agent doesn't recall financial data from training. It fetches it from the web each time it runs, extracts what's relevant, and passes it as context to the model.

This is fundamentally different from a finance chatbot. A chatbot draws on training data. A finance research agent draws on the real web at query time: SEC filings, company IR pages, financial news.

Firecrawl's web scraping service handles retrieval and extraction. Point it at a financial page and it returns clean markdown or structured JSON: both ready to pass directly to an LLM.

Why financial data is hard to get right in LLM applications

The training cutoff problem

LLMs have a knowledge cutoff. For financial use cases, this is a hard constraint. A model trained on data through early 2025 has no knowledge of what happened in Q4 2025 earnings season. When you ask about a company's most recent quarter, the model either declines or, more dangerously, answers with data from a prior period. The distinction between training data vs live web data matters more in finance than almost any other domain.

Nicolas Bustamante on X: two years building AI agents for financial services — normalizing financial data is 80% of the work, and the data layer is what makes agents actually work

@nicbstme on building AI agents for financial services.

A 2025 arXiv study evaluating 197,000+ financial questions found that LLMs display what researchers call "retrograde knowledge bias": they answer revenue questions correctly for 54% of companies when asked about 2017 data, but only 6% when asked about 1995 data. Accuracy degrades unpredictably across time periods, even for public data that has been available for decades.

The FailSafeQA benchmark found a 41% hallucination rate for financial queries under adversarial inputs. These are questions that are slightly ambiguous or incomplete, exactly the kind a real user would ask.

The cost barrier for developers

Institutional financial data is expensive.

Brett Caughran on X explaining why finance is one of the hardest verticals for LLM deployment: fiduciary precision demands, low-quality training signal, multi-modal data, and Bloomberg's closed ecosystem

@FundamentEdge on why finance is one of the hardest LLM verticals.

Bloomberg Terminal runs approximately $28,320–$31,980 per seat per year, according to third-party transaction records (as of May 2026). Bloomberg doesn't publish official pricing. FactSet starts around $4,000/user and scales to $30,000/user depending on modules. LSEG Workspace (formerly Refinitiv Eikon) runs $10,000–$42,000/user.

For developers building AI applications, this is a non-starter. As one developer put it on Hacker News, the major vendors are "predatory and will change the goalposts to charge you as much as you can bear."

r/algotrading thread asking where to find affordable financial data for backtesting

r/algotrading: where do developers get financial data for backtesting?

The yfinance trap

yfinance is an open-source Python library that pulls financial data from Yahoo Finance. It's widely used for fetching historical prices, fundamentals, and earnings data with a simple API. The obvious workaround for developers, but it's not a real API. It scrapes Yahoo Finance's HTML. Yahoo tightened its rate limits around early 2024, and developers now report hitting YFRateLimitError after 4–5 requests per day. A tool that breaks at 5 requests/day isn't usable in any agent workflow.

Why public web data fills the gap

SEC EDGAR is a public, government-operated database. Every 10-K, 10-Q, 8-K, and earnings release is available at no cost. Company IR pages are public. Financial news is public. The data is there. The challenge is extracting it reliably from pages that use JavaScript rendering or XBRL markup. That's a smart web search and scraping problem, not a data access problem.

That's exactly what Firecrawl solves.

Key data sources for a finance research agent

Data sourceExampleContent typeBest Firecrawl primitive
SEC EDGARedgar.sec.gov10-K, 10-Q, 8-K filingsScrape + JSON extraction
Company IR pagesinvestor.apple.comEarnings releases, press releasesScrape
Financial newsreuters.com/financeAnalysis, price action, macro contextSearch + Scrape
Federal Reserve / Treasuryfederalreserve.govRate decisions, economic dataScrape
Earnings call transcriptsfool.com, seekingalpha.comManagement commentaryScrape
Market aggregatorsfinance.yahoo.comPrices, summary dataScrape + JSON extraction
Alternative datajob boards, review sites, web trafficHiring signals, sentiment, product tractionSearch + Scrape

The agent should use Search before scraping. Company IR page structures change frequently. SEC EDGAR filing URLs for specific filings are not guessable. A search step dynamically discovers the right current URL instead of relying on hardcoded paths that break.

r/algotrading post about scraping 12 years of SEC EDGAR financial data for 3000+ stocks

r/algotrading: manually scraping 12 years of SEC EDGAR data.

Among these sources, company IR pages deserve special attention. They're often the fastest source for earnings data. A company publishes its earnings release on its IR page the moment it goes out, before any aggregator picks it up and days before the corresponding 10-Q lands on EDGAR. For time-sensitive analysis, this gap matters.

Apple's investor relations page showing FY 26 Second Quarter Results, a key data source for a finance research agent

Apple's IR page, updated minutes after each earnings release. The SEC filing typically lands on EDGAR days later.

Why Firecrawl handles financial data well

Most financial data sources are not friendly to standard scrapers. Here's what makes them hard, and how Firecrawl handles each issue.

JS-heavy pages and dynamic rendering

The SEC EDGAR filing viewer uses dynamic rendering. Many company IR pages are React SPAs. A standard fetch + HTML parser returns empty or incomplete content.

SEC.gov blocks requests from undeclared automated tools, returning an error page instead of filing content

What you get when you hit SEC.gov without JavaScript rendering. Firecrawl renders the page and returns the filing content.

Firecrawl renders JavaScript server-side. The same API call works on static HTML and JS-heavy SPAs. No configuration change needed.

Clean markdown by default

Firecrawl strips navigation, footers, cookie banners, and ad chrome by default. What comes back is the document content, not the page wrapper. For a 10-K filing, this means the financial statements and MD&A text, not the 30% EDGAR navigation chrome surrounding them.

Every token of noise is a token wasted in the LLM context window. Clean input makes the analysis better and the cost lower.

Structured extraction without selectors

Traditional scrapers require CSS selectors or XPaths. These break every time a site redesigns. Firecrawl's JSON extraction mode accepts a Zod schema. You describe the fields you want; Firecrawl locates them using LLM-assisted extraction. This works across different company IR page layouts without custom selectors per company.

Try it for yourself at firecrawl.dev/playground: paste any earnings release URL and see the clean markdown output.

Parsing local files

Not all financial documents live on the web. Downloaded 10-K PDFs, emailed research reports, and exported spreadsheets are common inputs for finance workflows.

Firecrawl's /parse endpoint handles these directly. Upload a PDF, DOCX, or spreadsheet and get back the same clean markdown or structured JSON that /scrape returns for web pages. Tables come back with reading order preserved. You can request structured JSON extraction in the same call.

This means one consistent pipeline handles both live web retrieval and local file processing. No separate AI PDF parser needed.

How the agent pipeline works

The finance research agent follows four steps: Search, Scrape, Extract, Analyze. Each maps to a Firecrawl primitive or the LLM layer.

Step 1: Search for relevant sources

The Search API takes a natural-language query and returns URLs with page content. For a finance agent, the query might be:

"Apple FY26 Q2 earnings release investor relations"

The response includes the URL, title, and optionally the full page content in markdown. For simple queries, the search result already contains enough context. No second scrape needed.

Searching first means the agent always finds the current, correct source, even if company IR page URLs change between quarters. For a broader comparison of deep research APIs suited to agentic research workflows, that guide covers the tradeoffs. For a comparison of all leading search tools for AI agents including Brave, Exa, Tavily, and Serper, see the full guide.

Step 2: Scrape the page into clean markdown

The Scrape API takes a URL and returns clean markdown. onlyMainContent: true strips headers, footers, and nav. What remains is the document body.

For a 10-K filing, this is hundreds of pages. The agent should scrape the filing, then chunk by section headers before passing to the LLM. Firecrawl's markdown preserves heading structure, which makes section-based chunking straightforward.

Step 3: Extract structured metrics as JSON

When you need typed values (revenue, EPS, guidance), define a Zod schema and pass it to the scrape endpoint as a JSON format object. Firecrawl extracts those values and returns structured JSON. No selectors. No regex. The schema works across different IR page layouts.

Step 4: Analyze with an LLM

The LLM receives the scraped markdown and extracted JSON as context. It's not being asked to recall financial data from training. It's being asked to reason over the data you just retrieved. This is the difference between asking a model to know something and asking it to think about something.

Always include the source URL and a retrieval timestamp in the prompt. The model can then cite its sources in the output.

Full TypeScript code: a finance research agent

Install dependencies:

npm install firecrawl ai @ai-sdk/anthropic zod

Set environment variables:

FIRECRAWL_API_KEY=fc-...
ANTHROPIC_API_KEY=sk-ant-...

The agent is split into two files. tools.ts defines three Firecrawl-powered agent tools. agent.ts wires them into a generateText loop with a finance-specific system prompt.

Part 1: Define the tools (tools.ts)

import { Firecrawl, type SearchResultWeb } from "firecrawl";
import { tool, zodSchema } from "ai";
import { z } from "zod";
 
const firecrawl = new Firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY });
 
const FinancialMetricsSchema = z.object({
  company_name: z.string(),
  reporting_period: z.string(),
  revenue_millions: z.number().optional(),
  net_income_millions: z.number().optional(),
  earnings_per_share: z.number().optional(),
  revenue_guidance_next_quarter: z.number().optional(),
  key_risks: z.array(z.string()).optional(),
});
 
export const searchFinancialSources = tool({
  description: "Search the web for financial news, SEC filings, and earnings releases",
  inputSchema: z.object({
    query: z.string().describe('Search query, e.g. "Apple FY26 Q2 earnings investor relations"'),
    limit: z.number().optional().default(5),
  }),
  execute: async ({ query, limit }) => {
    const results = await firecrawl.search(query, { limit });
    const items = (results.web ?? []) as SearchResultWeb[];
    return items.map((r) => ({
      url: r.url ?? "",
      title: r.title ?? "",
      content: r.description ?? "",
    }));
  },
});
 
export const scrapeFinancialPage = tool({
  description:
    "Scrape a financial page (SEC filing, earnings release, IR page) and return clean markdown",
  inputSchema: z.object({
    url: z.string().url().describe("URL to scrape"),
  }),
  execute: async ({ url }) => {
    const result = await firecrawl.scrape(url, {
      formats: ["markdown"],
      onlyMainContent: true,
    });
    return { content: result.markdown?.slice(0, 10000) ?? "" };
  },
});
 
export const extractFinancialMetrics = tool({
  description:
    "Extract structured financial metrics (revenue, EPS, guidance) as typed JSON from a page",
  inputSchema: z.object({
    url: z.string().url().describe("Earnings release or IR page URL"),
  }),
  execute: async ({ url }) => {
    const result = await firecrawl.scrape(url, {
      formats: [
        {
          type: "json",
          schema: zodSchema(FinancialMetricsSchema).jsonSchema as Record<string, unknown>,
        },
      ],
    });
    return result.json ?? {};
  },
});

Part 2: The agent (agent.ts)