QVeris

Finance & Market Data

FMP Financial Statements API: Examples & Validation

Use the FMP financial statements API for income, balance sheet and cash flow data with code examples, period alignment, validation and error controls.

Published: April 13, 2026 · By the QVeris Team

Financial reports, calculations and market evidence reviewed together
Financial reports, calculations and market evidence reviewed together
TL;DR

The shortest correct three-statement workflow

Request all three statements with the same symbol, period and limit—then join by fiscal identity, not array position.

Keep the FMP key on your server. Normalize symbol + fiscalYear + period, retain statement and filing dates, preserve nulls, verify currency and units, and run reconciliation checks before calculating ratios or giving the data to an agent.

Who this is for

Developers building fundamental research, screeners, valuation models, portfolio analytics or financial-research agents.

Prerequisites

An FMP API key, a trusted server runtime, a defined annual/quarter/TTM policy, and explicit null, currency and revision handling.

Endpoint selector

Choose standardized, TTM or as-reported data

FMP exposes several statement families because comparison, latest-performance analysis and filing-level audit are different jobs. Start with the question your application must answer.

NeedStable endpointUse it whenMain caution
Profitability/income-statementRevenue, margins, operating income, net income and EPS.Fiscal period and diluted/basic share basis.
Financial position/balance-sheet-statementCash, working capital, debt, assets, liabilities and equity.It is a point-in-time statement.
Cash generation/cash-flow-statementOperating, investing and financing cash flow; capex and free cash flow.Capex sign convention and FX effects.
Rolling latest year/income-statement-ttm
/balance-sheet-statement-ttm
/cash-flow-statement-ttm
Latest-year trend or valuation inputs without waiting for fiscal year-end.Do not add TTM to annual or quarterly rows.
Comparable metrics/key-metrics · /ratios
/key-metrics-ttm · /ratios-ttm
Margins, returns, liquidity, leverage and valuation screens.Know the formula and denominator period.
Filing-level audit/financial-statement-full-as-reportedYou need original filing taxonomy or your own mapping layer.Fields vary by filer and taxonomy.
Standardized is for comparability; as reported is for provenance.

Do not describe one as universally “more accurate.” They answer different questions. Keep the selected representation in dataset metadata.

Period alignment

Annual, quarterly and TTM are different contracts

Annual

Best for audited multi-year trends and full fiscal-year models. Companies can have non-calendar fiscal years.

Quarterly

Best for recent operating changes. A fiscal Q1 can fall in a different calendar year than expected.

TTM

A rolling latest-twelve-month view. Useful for current ratios, but not an additional period to sum into a history.

As reported

Preserves issuer taxonomy and filing detail. Mapping consistency becomes your responsibility.

Use fiscal identity as the primary key

Start with symbol + fiscalYear + period. Confirm date, calendarYear, filingDate and acceptedDate when present.

Never zip arrays by index

One endpoint can contain a missing, duplicate or revised period. Build maps by key and report unmatched rows.

Separate flow and stock concepts

Revenue and cash flow cover a period; cash, debt and equity are measured at a date. Use the balance-sheet date that closes the same fiscal period.

Step-by-step implementation

Fetch the three FMP statements safely

FMP supports API-key authorization in a header or query parameter. A server-side header keeps the key out of URLs, browser history and common access logs.

cURL · annual income statement
curl --fail --silent --show-error \
  --get "https://financialmodelingprep.com/stable/income-statement" \
  --data-urlencode "symbol=AAPL" \
  --data-urlencode "period=annual" \
  --data-urlencode "limit=5" \
  --header "apikey: $FMP_API_KEY"
Python · parallel fetch + fiscal-key join
import os
from concurrent.futures import ThreadPoolExecutor
import requests

BASE = "https://financialmodelingprep.com/stable"
ENDPOINTS = {
    "income": "income-statement",
    "balance": "balance-sheet-statement",
    "cashflow": "cash-flow-statement",
}

def fetch(name: str, symbol: str, period="annual", limit=5):
    response = requests.get(
        f"{BASE}/{ENDPOINTS[name]}",
        params={"symbol": symbol.upper(), "period": period, "limit": limit},
        headers={"apikey": os.environ["FMP_API_KEY"]},
        timeout=10,
    )
    response.raise_for_status()
    rows = response.json()
    if not isinstance(rows, list) or not rows:
        raise ValueError(f"No {name} rows for {symbol}")
    return rows

def fiscal_key(row: dict):
    year = row.get("fiscalYear") or row.get("calendarYear")
    return row.get("symbol"), str(year), row.get("period")

def get_three_statements(symbol: str):
    with ThreadPoolExecutor(max_workers=3) as pool:
        futures = {name: pool.submit(fetch, name, symbol) for name in ENDPOINTS}
        data = {name: future.result() for name, future in futures.items()}

    indexed = {
        name: {fiscal_key(row): row for row in rows}
        for name, rows in data.items()
    }
    common = set.intersection(*(set(rows) for rows in indexed.values()))
    if not common:
        raise ValueError("The three statements share no fiscal periods")

    return [
        {"key": key, **{name: indexed[name][key] for name in ENDPOINTS}}
        for key in sorted(common, reverse=True)
    ]
TypeScript · bounded Promise.all
const paths = {
  income: "income-statement",
  balance: "balance-sheet-statement",
  cashflow: "cash-flow-statement",
} as const;

async function getStatements(symbol: string, period = "annual") {
  const key = process.env.FMP_API_KEY;
  if (!key) throw new Error("FMP_API_KEY is missing");

  const entries = await Promise.all(
    Object.entries(paths).map(async ([name, path]) => {
      const url = new URL(\`https://financialmodelingprep.com/stable/\${path}\`);
      url.searchParams.set("symbol", symbol.toUpperCase());
      url.searchParams.set("period", period);
      url.searchParams.set("limit", "5");

      const response = await fetch(url, {
        headers: { apikey: key },
        signal: AbortSignal.timeout(10_000),
      });
      if (!response.ok) throw new Error(\`\${name}: HTTP \${response.status}\`);
      const rows: unknown = await response.json();
      if (!Array.isArray(rows) || rows.length === 0) {
        throw new Error(\`\${name}: empty statement response\`);
      }
      return [name, rows] as const;
    }),
  );
  return Object.fromEntries(entries);
}
Three-statement data contract

Normalize identity before financial values

Provider payloads can evolve. Keep a stable internal envelope around the fields your product actually uses, and retain the raw payload or a content hash where your policy allows.

Field groupRecommended fieldsRule
Instrumentsymbol, cikResolve symbol changes and exchange variants outside the statement row.
Fiscal identityfiscalYear, period, dateUse as the join identity; never rely on array order.
Filing provenancefilingDate, acceptedDate, finalLinkRetain to explain when and where the value entered the dataset.
MeasurementreportedCurrency, unit, scaleNormalize currency and magnitude before comparison.
Representationstandardized or as_reportedDo not blend the two without a documented mapping.
RevisionretrievedAt, payload hash, versionSupport restatement detection and reproducible research.
Null is not zero.

A missing field can mean not applicable, not disclosed, not mapped or temporarily unavailable. Preserve null and attach a quality reason before ratios or rankings.

Validation layer

Turn accounting relationships into tests

Validation should produce flags and tolerances, not silently rewrite source data. Some differences are legitimate because of rounding, foreign-exchange effects, taxonomy mapping or issuer presentation.

CheckTestFailure meaning
Balance sheet equationassets ≈ liabilities + equityMapping, units, rounding or incomplete statement.
Period consistencySame symbol, fiscal year, period and compatible dates.Mixed quarter/year, missing statement or restatement.
Currency and scaleSame reported currency and documented unit.Cross-listing, conversion or million/unit mismatch.
Cash movementOpening to closing cash agrees with net change, allowing explicit FX effects.Sign convention, FX, acquisition classification or missing row.
Net income bridgeCash-flow starting income is compatible with the income statement.Continuing-operations, minority interest or mapping difference.
Duplicate identityAt most one active record per representation and fiscal key.Restatement or ingestion duplication; select by documented rule.
Python · tolerance-based balance check
def close_enough(left, right, rel_tol=0.001, abs_tol=1.0):
    if left is None or right is None:
        return None  # unknown, not pass
    return abs(left - right) <= max(abs_tol, rel_tol * max(abs(left), abs(right)))

def validate_balance_sheet(row):
    assets = row.get("totalAssets")
    liabilities = row.get("totalLiabilities")
    equity = row.get("totalStockholdersEquity")
    return {
        "balanceEquation": close_enough(assets, liabilities + equity)
        if None not in (assets, liabilities, equity) else None,
        "currencyPresent": bool(row.get("reportedCurrency")),
        "periodPresent": bool(row.get("period")),
    }
  • Test one known annual period and one known quarter before loading full history.
  • Confirm the provider's capex sign before deriving free cash flow.
  • Compare a material value with the linked filing during acceptance testing.
  • Keep null, zero and not-applicable as distinct states.
  • Store retrieval time and a payload fingerprint for revision detection.
  • Block downstream ratios when identity, currency or period checks fail.
Derived metrics

Calculate only after the source periods pass

Margins

Use income-statement numerator and denominator from the same period and representation.

Leverage

Document whether debt is total debt, net debt or interest-bearing debt, and whether EBITDA is annual or TTM.

Free cash flow

Prefer a documented provider field or normalize the capex sign before calculating operating cash flow less capex.

Return ratios

A flow numerator usually needs an average opening/closing balance denominator, not only the closing balance.

Security, cost and lineage

Production controls for statement pipelines

Cache by filing lifecycle

Statements change far less often than prices. Cache aggressively, then refresh around expected filings and provider updates.

Retry narrowly

Retry 429 and 5xx with exponential backoff and jitter. Do not retry invalid symbols, bad keys or unsupported plan access.

Version every refresh

Keep provider, endpoint, parameters, retrieval time and payload fingerprint so a research result can be reproduced.

Measure data quality

Track empty results, missing-period rate, reconciliation failures, revision rate and time from filing to usable data.

Troubleshooting

Common financial statement failure modes

SymptomLikely causeWhat to do
HTTP 200, empty arrayUnsupported symbol, period or coverage.Treat as not found; verify symbol and statement coverage.
403Missing/invalid key or plan access.Stop retrying; inspect secret injection and entitlement.
429Rate limit exceeded.Back off, reduce concurrency and cache repeated history.
Three arrays have different lengthsMissing, duplicate or revised fiscal period.Join by fiscal key and surface unmatched records.
Ratio jumps by 1,000×Unit, currency or magnitude mismatch.Inspect reported currency and scaling before recalculation.
Quarterly totals do not match annualRestatement, fiscal calendar, rounding or discrete-quarter/YTD semantics.Inspect period semantics and filing versions; never force equality.
Previously stored value changedRestatement or provider normalization update.Create a new version, diff it and retain prior provenance.
QVeris implementation pattern

Use a capability layer when the workflow must choose

FMP remains the data provider in this pattern. QVeris is useful when an agent needs to discover a statement capability, inspect the current schema, invoke it consistently, preserve evidence or route to another provider. A fixed FMP integration can remain direct.

Discover

Search by statement type, market coverage, period and required provenance.

Inspect

Read the live input/output contract before calling; confirm supported identifiers and periods.

Call

Invoke with validated arguments, then apply the same fiscal alignment and reconciliation gates.

Read the QVeris documentation →

FAQ

FMP financial statements API questions

Which endpoints return the three financial statements?

Use income-statement, balance-sheet-statement and cash-flow-statement under the stable base URL. Use the same symbol, period and limit across the three requests.

Should I use annual, quarterly or TTM data?

Use annual for audited multi-year comparisons, quarterly for recent change, and TTM for a rolling latest-year view. Do not mix period types without explicit conversion.

What is standardized versus as reported?

Standardized statements improve common-field comparability. As-reported endpoints preserve original filing taxonomy for audit and custom mapping.

How should I join the three statements?

Join on symbol, fiscal year and fiscal period, then confirm statement date and filing metadata. Filing date alone and array position are unsafe keys.

How do I validate statement data?

Check period and currency alignment, the balance-sheet equation, cash movement, duplicated periods, missing values, filing timestamps and revision behavior.

Can missing values be replaced with zero?

Not automatically. Null can mean not reported, not applicable, unavailable or not mapped. Preserve it and attach a quality reason before deriving ratios.

Can I expose the FMP key in frontend code?

No. Store it in a server-side environment variable or secret manager and request FMP from a trusted backend.

When should I use QVeris with FMP?

Use QVeris for capability discovery, live schema inspection, consistent invocation or provider fallback. A fixed single-provider integration can call FMP directly.

Official sources

Verify endpoints and fields against live docs

Make every financial conclusion traceable

Start with correctly aligned FMP statements. Add QVeris when the workflow needs capability discovery, schema inspection or provider-aware routing.

A verified profile link does not guarantee entitlement, field availability, price or current execution success; inspect the current contract before calling.