Finance & Market Data
Financial Statements API for AI Agents
Compare financial statements APIs for AI agents: provider coverage, normalized schemas, Python code, accounting validation, provenance and production controls.
Published: April 12, 2026 · By the QVeris Team

On this page
TL;DR: choose the statement contract before the provider
Financial Datasets is a strong fit for normalized US statements with filing provenance. FMP is useful when one global API must expose standardized, TTM, growth, and as-reported statements. SEC EDGAR is the authoritative US XBRL evidence layer when your team can own normalization. Alpha Vantage is simple for prototypes; EODHD and Finnhub are candidates when global issuer coverage matters. The production decision depends on period semantics, restatements, identifiers, provenance, licensing, and tested field completeness.
One-call income, balance sheet, and cash flow responses with report-period filters and filing metadata.
Separate standardized, TTM, growth, bulk, and as-reported endpoints in one larger financial API.
Use Company Facts and filing submissions when authority matters more than a ready-made statement table.
Evaluate them when exchange-qualified global identity and non-US statement history are required.
What “financial statements API” searchers actually need
This query is not asking for a definition of an income statement. The user usually needs to select a source, fetch the three statements, make periods comparable, handle revisions, and produce calculations that can be traced back to a filing. A page that only lists fields or promotes one routing layer leaves the implementation problem unsolved.
This page covers provider-neutral selection and architecture. The separate FMP financial statements guide remains the brand-specific endpoint and validation tutorial.
Who this guide is for and what to prepare
This guide is for engineers building equity-research agents, screeners, valuation systems, covenant monitors, portfolio reviews, and source-backed financial copilots. It assumes the workflow needs more than a demo response: it must survive odd fiscal calendars, provider corrections, missing facts, and audit questions.
- Create a test universe containing a normal issuer, bank, insurer, ADR, non-calendar fiscal year, loss-making company, and recently restated filer.
- Define annual, quarterly, YTD, and TTM as separate contracts before fetching data.
- Store API credentials server-side and record plan entitlements separately from code.
- Decide which source is authoritative when standardized values disagree with a filing.
Annual, quarterly, TTM, and as-reported are different contracts
| Contract | Meaning | Common mistake |
|---|---|---|
| Annual | A reported fiscal year, which may not end in December. | Labelling it by calendar year only. |
| Quarterly | A discrete quarter or a provider-derived quarter. | Mixing a discrete quarter with a year-to-date filing value. |
| TTM | A rolling sum or latest point assembled from recent periods. | Treating TTM as a filed statement. |
| As-reported | Issuer labels and values close to the original filing taxonomy. | Assuming fields are cross-company comparable. |
| Standardized | Provider mappings into a common schema. | Discarding the raw tag and source that explain the mapping. |
Evaluation criteria for an agent-ready statements API
- Coverage: issuers, exchanges, active and delisted securities, history depth, banks, insurers, and foreign filers.
- Period semantics: fiscal year, fiscal quarter, start/end dates, filing date, accepted date, and discrete versus YTD values.
- Statement modes: standardized, as-reported, annual, quarterly, TTM, bulk, segments, and restatement history.
- Evidence: accession number, filing URL, source tag, unit, currency, retrieval time, and revision identity.
- Operations: rate limits, bulk behavior, correction policy, cache and redistribution rights, status reporting, and support.
Financial statements API providers compared
| Option | Best fit | Documented strengths | Validate before launch |
|---|---|---|---|
| Financial Datasets | Normalized US company statements with filing traceability | One-call three-statement endpoint; annual, quarterly, and TTM; report-period filters; accession and filing fields | US-only boundary, plan limits, special-industry completeness, revision behavior |
| FMP Stable | Broad financial stack and multiple statement modes | Income, balance, cash flow, TTM, growth, bulk, financial-report dates, and as-reported endpoints | Plan entitlements, regional depth, field null rates, point-in-time behavior |
| SEC EDGAR | Primary US filing and XBRL evidence | Submissions, Company Facts, Company Concept, Frames, real-time updates, and nightly bulk archives | Custom tags, units, contexts, fiscal calendars, fair-access operations, no browser CORS |
| Alpha Vantage | Simple prototypes and normalized annual/quarterly reports | Income, balance sheet, and cash flow functions with GAAP/IFRS taxonomy mappings | Quota, universe, field mapping, null handling, and refresh timing |
| EODHD | Exchange-qualified global fundamentals | Versioned Fundamentals endpoint, exchange suffixes, partial-field filters, global financial statement sections | Exact exchange coverage, plan rights, payload size, missing fields by issuer |
| Finnhub | Global fundamentals joined with estimates and market data | Long statement history for global companies plus estimates, transcripts, revenue segments, and KPIs | Endpoint entitlements, normalized-versus-reported semantics, coverage by region |
Choose by workflow, not by feature count
Cross-company screening needs predictable names, units, periods, and coverage statistics. Preserve raw links for drill-down.
Material conclusions need accession numbers, filed facts, source tags, and a reproducible retrieval path.
Ticker alone is unsafe across exchanges. Map issuer, security, listing, exchange, currency, and provider symbol.
Backtests and historical agent answers need what was known then, not today's corrected value for an old period.
A provider-neutral architecture for financial statements
Map the issuer, security, listing, exchange, CIK/LEI/ISIN where available, and provider-specific symbol.
Write the response, request parameters, headers safe for logging, retrieval time, status, and content hash before transformation.
Map identity, period, currency, unit scale, statement type, canonical field, original field, value, and provenance.
Join only compatible periods, run accounting and semantic checks, then label the bundle valid, warning, or rejected.
Calculate ratios only from accepted inputs, and attach citations, formulas, units, and validation results to the agent answer.
Fetch all three statements and preserve the evidence envelope
The example uses Financial Datasets because its documented endpoint returns all three statements in one call. The important part is the envelope: use the same pattern for every provider adapter.
import hashlib
import json
import os
from datetime import datetime, timezone
import requests
def fetch_statement_bundle(ticker: str, period: str = "annual", limit: int = 8):
endpoint = "https://api.financialdatasets.ai/financials"
params = {"ticker": ticker, "period": period, "limit": limit}
response = requests.get(
endpoint,
params=params,
headers={"X-API-KEY": os.environ["FINANCIAL_DATASETS_API_KEY"]},
timeout=30,
)
response.raise_for_status()
raw = response.json()
stable = json.dumps(raw, sort_keys=True, separators=(",", ":"))
return {
"provider": "financial_datasets",
"endpoint": endpoint,
"request": params,
"retrievedAt": datetime.now(timezone.utc).isoformat(),
"rawSha256": hashlib.sha256(stable.encode()).hexdigest(),
"raw": raw,
"statements": raw.get("financials", raw),
}
bundle = fetch_statement_bundle("AAPL", period="annual")
print(bundle["rawSha256"], bundle["statements"].keys())Never log API keys. Retry only bounded transient failures, honor rate-limit headers, and write the raw snapshot before normalization so a mapping change cannot erase the original evidence.
Use a long-form fact schema before rebuilding statements
A wide provider response is convenient for display but brittle for multi-provider normalization. Convert each value into a fact record first; pivot into a statement only after identity and period validation.
| Field group | Required fields | Why it matters |
|---|---|---|
| Identity | issuerId, securityId, listingId, providerSymbol | Prevents ADR, dual-listing, and ticker-reuse errors. |
| Period | periodType, startDate, endDate, fiscalYear, fiscalQuarter | Separates annual, discrete quarter, YTD, instant, and TTM facts. |
| Value | statement, canonicalField, rawField, value, currency, unitScale | Makes transformations explicit and reversible. |
| Evidence | provider, filingDate, accession, sourceUrl, retrievedAt, rawSha256 | Reproduces the answer and identifies later corrections. |
Align identity and periods before joining the three statements
Do not join on ticker and year. Use issuer identity plus period type, period end, fiscal year, fiscal quarter, currency, and filing version. Income and cash flow statements are duration facts; many balance-sheet values are instant facts. A calendar-quarter label is not enough to prove they belong together.
Store raw labels and dates before mapping them into your fiscal model.
Record component quarters, formula, missing-quarter policy, and derivation time.
The financial period ended before the filing was submitted and accepted.
Zero is a reported value; null means absent, not applicable, unmapped, or unavailable.
Turn accounting relationships into validation tests
def close_enough(left, right, abs_tol=1_000, rel_tol=0.002):
scale = max(abs(left), abs(right), 1)
return abs(left - right) <= max(abs_tol, rel_tol * scale)
def validate_period(bundle):
errors, warnings = [], []
bs, cf = bundle["balance"], bundle["cashFlow"]
if not close_enough(
bs["totalAssets"],
bs["totalLiabilities"] + bs["totalEquity"],
):
errors.append("balance_sheet_equation_failed")
if all(cf.get(k) is not None for k in
["beginningCash", "changeInCash", "endingCash"]):
if not close_enough(
cf["beginningCash"] + cf["changeInCash"],
cf["endingCash"],
):
warnings.append("cash_rollforward_failed")
if len({bundle[x]["currency"] for x in
["income", "balance", "cashFlow"]}) != 1:
errors.append("currency_mismatch")
if len({bundle[x]["periodEnd"] for x in
["income", "balance", "cashFlow"]}) != 1:
errors.append("period_end_mismatch")
return {"valid": not errors, "errors": errors, "warnings": warnings}- Use absolute and relative tolerances; rounding and provider presentation can create small differences.
- Check sign conventions before reconciling capex, debt issuance, buybacks, and dividends.
- Recompute subtotals only when component definitions and scopes match.
- Track check status per period; one failed quarter should not silently contaminate a multi-year trend.
Restatements and provider corrections need versioned history
The same report period can appear in an original filing, an amendment, and later comparative columns. A provider can also improve its taxonomy mapping after your first retrieval. Overwriting the old row destroys point-in-time reproducibility. Use an append-only key such as issuer + period + statement mode + filing/accession + provider revision + retrieval snapshot.
For a current answer, prefer the latest accepted version and disclose that a restatement occurred. For a historical backtest, use the version available at the simulated decision time.
Security, cost, latency, and licensing controls
A specific filing snapshot can be long-lived; latest-statement aliases require revalidation and correction detection.
Do not fan out thousands of per-symbol calls when a documented bulk or nightly archive exists.
Secrets prove identity; plan records determine which endpoints, history, and uses are allowed.
Internal analysis, end-user display, derived data, caching, and resale can have different contractual rights.
Common financial statement failure modes
| Symptom | Likely cause | Fix |
|---|---|---|
| Revenue is four times too large | YTD value treated as a discrete quarter or quarterly value annualized twice | Store duration start/end and derivation method; never infer from the Q label alone |
| Assets do not equal liabilities plus equity | Mixed periods, units, currencies, scopes, or incomplete mappings | Reject the joined bundle and inspect raw fields before calculating |
| Historical answer changes silently | Restatement or provider correction overwrote a row | Use append-only versions, hashes, effective time, and observed time |
| Bank or insurer ratios look absurd | Industrial-company schema applied to a specialized balance sheet | Route by industry schema and require industry-specific validation |
| Missing value becomes a valid zero | Null coercion during parsing or model prompting | Use typed nullable fields and explicit missing-reason codes |
QVeris pattern: Discover → Inspect → Call
QVeris belongs above provider endpoints as a capability-discovery and routing layer. It does not become the financial source. The agent discovers a capability such as “annual standardized statements with filing URLs,” inspects schema, coverage, limits, and permissions, then calls the selected tool with bounded inputs. The normalized response must retain the underlying provider and filing evidence.
Search by statement mode, geography, fiscal period, provenance, and latency.
Check identifiers, field definitions, null behavior, permissions, rate limits, and evidence fields.
Invoke the smallest scope, preserve the raw response, validate the normalized bundle, and cite the source.
Financial statements API questions
What is the best financial statements API for AI agents?
There is no universal winner. Financial Datasets is strong for normalized US statements and provenance, FMP for broad standardized and as-reported endpoints, SEC EDGAR for primary US XBRL evidence, and EODHD or Finnhub for broader global coverage.
Should an agent use standardized or as-reported statements?
Use standardized statements for screening and cross-company comparison. Retain as-reported facts and filing references for audit, unusual line items, and material conclusions.
Can annual, quarterly, and TTM statements be mixed?
No. They are different period contracts. Store period type, start date, end date, fiscal year, fiscal quarter, and filing date, then compare only compatible periods.
How should an agent validate a balance sheet?
Check that assets approximately equal liabilities plus equity using a documented tolerance, while preserving the original currency, units, source, and reported values.
Why do financial statement values change after publication?
Companies amend filings, providers correct mappings, and later filings can restate comparative periods. Keep append-only snapshots and distinguish report period, filing date, and retrieval time.
Is SEC EDGAR enough for a production financial agent?
It can be the authoritative US evidence layer, but most teams still need identifier mapping, XBRL normalization, caching, taxonomy handling, and derived statement assembly.
What provenance should every normalized value retain?
Retain provider, endpoint, issuer and security identifiers, report period, filing date, accession or source URL, currency, unit scale, raw field name, retrieval time, and a raw-payload hash.
Where does QVeris fit in a financial statement workflow?
QVeris helps agents discover, inspect, and call provider tools. The provider response and underlying filing remain the sources of record.
Official sources and related implementation guides
Coverage, schemas, quotas, and commercial rights change. Verify current documentation and run a representative issuer test set before committing to a provider.
Make every financial conclusion reproducible
Use QVeris to discover and inspect statement capabilities, then preserve provider identity, fiscal semantics, raw evidence, validation status, and filing citations throughout the workflow.
Start with QVerisA verified profile link does not guarantee entitlement, field availability, price or current execution success; inspect the current contract before calling.
