On this page本页目录
The shortest correct three-statement workflow最短的正确三表工作流
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.
FMP Key 保存在服务端。统一 symbol + fiscalYear + period,保留报表日期与申报日期,不擅自把空值变成 0,校验币种和单位,再计算比率或交给 Agent。
Developers building fundamental research, screeners, valuation models, portfolio analytics or financial-research agents.
开发基本面研究、股票筛选器、估值模型、组合分析或财务研究 Agent 的团队。
An FMP API key, a trusted server runtime, a defined annual/quarter/TTM policy, and explicit null, currency and revision handling.
FMP API Key、可信服务端、明确的年度/季度/TTM 策略,以及空值、币种和修订处理规则。
Choose standardized, TTM or as-reported data选择标准化、TTM 或原始披露数据
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.
FMP 提供多类财务报表端点,是因为跨公司比较、最新经营表现和申报级审计是不同任务。先确定应用需要回答的问题。
| Need需求 | Stable endpoint | Use it when适用场景 | Main caution主要注意点 |
|---|---|---|---|
| Profitability盈利能力 | /income-statement | Revenue, margins, operating income, net income and EPS.收入、利润率、营业利润、净利润和 EPS。 | Fiscal period and diluted/basic share basis.财务期间与基本/稀释股数口径。 |
| Financial position财务状况 | /balance-sheet-statement | Cash, working capital, debt, assets, liabilities and equity.现金、营运资本、债务、资产、负债与权益。 | It is a point-in-time statement.它是时点报表,而非期间流量。 |
| Cash generation现金创造 | /cash-flow-statement | Operating, 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.不要把 TTM 与年度或季度数据相加。 |
| 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-reported | You need original filing taxonomy or your own mapping layer.需要原始申报分类或自行建立映射层。 | Fields vary by filer and taxonomy.字段会随公司与分类标准变化。 |
Do not describe one as universally “more accurate.” They answer different questions. Keep the selected representation in dataset metadata.
不能笼统说其中一种“更准确”。两者解决的问题不同,应在数据集元数据中保存所选口径。
Annual, quarterly and TTM are different contracts年度、季度与 TTM 是不同的数据契约
Best for audited multi-year trends and full fiscal-year models. Companies can have non-calendar fiscal years.
适合经审计的多年趋势和完整财年模型。公司的财年不一定等于自然年。
Best for recent operating changes. A fiscal Q1 can fall in a different calendar year than expected.
适合观察近期经营变化。财务 Q1 所在自然年可能与你直觉不同。
A rolling latest-twelve-month view. Useful for current ratios, but not an additional period to sum into a history.
滚动最近十二个月视图,适合当前比率,但不是可以再加进历史序列的新期间。
Preserves issuer taxonomy and filing detail. Mapping consistency becomes your responsibility.
保留发行人的申报分类与细节,但字段映射一致性需要由你负责。
Start with symbol + fiscalYear + period. Confirm date, calendarYear, filingDate and acceptedDate when present.
以 symbol + fiscalYear + period 为起点,再核验 date、calendarYear、filingDate 和可用时的 acceptedDate。
One endpoint can contain a missing, duplicate or revised period. Build maps by key and report unmatched rows.
某个端点可能缺期、重复或出现修订版本。应按键建立映射,并报告无法匹配的记录。
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.
收入与现金流覆盖一段期间;现金、债务和权益是在某个日期的存量。应使用结束于同一财务期间的资产负债表。
Fetch the three FMP statements safely安全获取 FMP 三张财务报表
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.
FMP 支持通过请求头或查询参数鉴权。服务端请求头可避免密钥进入 URL、浏览器历史和常见访问日志。
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"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)
]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);
}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 group字段组 | Recommended fields建议字段 | Rule规则 |
|---|---|---|
| Instrument标的身份 | symbol, cik | Resolve symbol changes and exchange variants outside the statement row.在报表记录之外处理代码变更与交易所变体。 |
| Fiscal identity财务期间 | fiscalYear, period, date | Use as the join identity; never rely on array order.作为连接身份,不能依赖数组顺序。 |
| Filing provenance申报来源 | filingDate, acceptedDate, finalLink | Retain to explain when and where the value entered the dataset.用于解释数据何时、从哪里进入数据集。 |
| Measurement计量口径 | reportedCurrency, unit, scale | Normalize currency and magnitude before comparison.比较前统一币种与数量级。 |
| Representation数据形态 | standardized or as_reported | Do not blend the two without a documented mapping.没有明确映射时不要混用。 |
| Revision修订信息 | retrievedAt, payload hash, version | Support restatement detection and reproducible research.支持修订检测和可复现研究。 |
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.
字段缺失可能表示不适用、未披露、未映射或暂时不可用。在计算比率或排名前,应保留空值并附上质量原因。
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.
校验应输出标记和容差,而不是静默修改源数据。舍入、汇率影响、分类映射或发行人列报方式都可能造成合理差异。
| Check检查项 | Test测试方式 | Failure meaning失败含义 |
|---|---|---|
| Balance sheet equation资产负债表恒等式 | assets ≈ liabilities + equity | Mapping, units, rounding or incomplete statement.映射、单位、舍入或报表不完整。 |
| Period consistency期间一致性 | Same symbol, fiscal year, period and compatible dates.相同代码、财年、期间和兼容日期。 | Mixed quarter/year, missing statement or restatement.混入季度/年度、缺报表或发生修订。 |
| Currency and scale币种与数量级 | Same reported currency and documented unit.相同报告币种且单位明确。 | Cross-listing, conversion or million/unit mismatch.跨市场上市、换算或百万/个位数混用。 |
| Cash movement现金变化 | Opening to closing cash agrees with net change, allowing explicit FX effects.期初到期末现金与净变化一致,并显式考虑汇率影响。 | Sign convention, FX, acquisition classification or missing row.符号、汇率、收购分类或记录缺失。 |
| Net income bridge净利润衔接 | Cash-flow starting income is compatible with the income statement.现金流起始利润与利润表口径兼容。 | Continuing-operations, minority interest or mapping difference.持续经营、少数股东损益或映射差异。 |
| Duplicate identity重复期间身份 | At most one active record per representation and fiscal key.每种数据形态与财务期间键最多一个有效版本。 | Restatement or ingestion duplication; select by documented rule.发生修订或重复采集,应按规则选择版本。 |
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.身份、币种或期间检查失败时,阻止下游比率计算。
Calculate only after the source periods pass源报表通过校验后再计算指标
Use income-statement numerator and denominator from the same period and representation.
分子与分母必须来自同一期间、同一数据形态的利润表。
Document whether debt is total debt, net debt or interest-bearing debt, and whether EBITDA is annual or TTM.
明确债务是总债务、净债务还是有息债务,并说明 EBITDA 是年度还是 TTM。
Prefer a documented provider field or normalize the capex sign before calculating operating cash flow less capex.
优先使用定义明确的供应商字段,或在用经营现金流减资本开支前统一资本开支符号。
A flow numerator usually needs an average opening/closing balance denominator, not only the closing balance.
期间流量作为分子时,分母通常应使用期初与期末余额平均值,而不是只用期末值。
Production controls for statement pipelines财务报表管线的生产控制
Statements change far less often than prices. Cache aggressively, then refresh around expected filings and provider updates.
财务报表的变化远少于价格。可以较长缓存,并在预计申报和供应商更新窗口刷新。
Retry 429 and 5xx with exponential backoff and jitter. Do not retry invalid symbols, bad keys or unsupported plan access.
对 429 和 5xx 使用指数退避与随机抖动;无效代码、错误密钥和套餐不支持不要重试。
Keep provider, endpoint, parameters, retrieval time and payload fingerprint so a research result can be reproduced.
保存供应商、端点、参数、获取时间和响应指纹,确保研究结果可复现。
Track empty results, missing-period rate, reconciliation failures, revision rate and time from filing to usable data.
监控空结果、缺期率、勾稽失败率、修订率,以及从申报到数据可用的时间。
Common financial statement failure modes常见财务报表失败模式
| Symptom现象 | Likely cause可能原因 | What to do处理方式 |
|---|---|---|
| HTTP 200, empty arrayHTTP 200,但数组为空 | Unsupported symbol, period or coverage.代码、期间或覆盖不受支持。 | Treat as not found; verify symbol and statement coverage.按未找到处理,并核验代码与报表覆盖。 |
| 403 | Missing/invalid key or plan access.密钥缺失、无效或套餐权限不足。 | Stop retrying; inspect secret injection and entitlement.停止重试,检查密钥注入和权限。 |
| 429 | Rate limit exceeded.超过速率限制。 | Back off, reduce concurrency and cache repeated history.退避、降低并发,并缓存重复历史请求。 |
| Three arrays have different lengths三张报表数组长度不同 | Missing, duplicate or revised fiscal period.财务期间缺失、重复或被修订。 | Join by fiscal key and surface unmatched records.按财务期间键连接,并暴露未匹配记录。 |
| Ratio jumps by 1,000×比率突然放大 1,000 倍 | Unit, currency or magnitude mismatch.单位、币种或数量级不一致。 | Inspect reported currency and scaling before recalculation.重新计算前检查报告币种与缩放。 |
| Quarterly totals do not match annual季度合计与年度不一致 | Restatement, fiscal calendar, rounding or discrete-quarter/YTD semantics.修订、财年、舍入或单季度/年初至今口径差异。 | Inspect period semantics and filing versions; never force equality.检查期间语义与申报版本,不要强行对齐。 |
| Previously stored value changed历史已存数值发生变化 | Restatement or provider normalization update.公司修订或供应商标准化更新。 | Create a new version, diff it and retain prior provenance.生成新版本、记录差异并保留旧版本来源。 |
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.
在该模式下,FMP 仍然是数据供应商。Agent 需要发现报表能力、检查当前 Schema、统一调用、保存证据或切换供应商时,QVeris 才发挥作用。固定的 FMP 集成可以继续直接调用。
Search by statement type, market coverage, period and required provenance.
根据报表类型、市场覆盖、期间和来源要求搜索能力。
Read the live input/output contract before calling; confirm supported identifiers and periods.
调用前读取实时输入输出契约,确认支持的标识符和期间。
Invoke with validated arguments, then apply the same fiscal alignment and reconciliation gates.
使用校验后的参数执行,再应用同样的财务期间对齐与勾稽检查。
FMP financial statements API questionsFMP 财务报表 API 常见问题
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.
在 Stable 基础 URL 下使用 income-statement、balance-sheet-statement 和 cash-flow-statement,三次请求保持代码、期间和条数一致。
Should I use annual, quarterly or TTM data?应该使用年度、季度还是 TTM 数据?
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.
多年经审计比较使用年度;近期变化使用季度;滚动最近一年使用 TTM。没有显式转换时不要混用。
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?缺失值可以替换成 0 吗?
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?可以在前端代码中暴露 FMP Key 吗?
No. Store it in a server-side environment variable or secret manager and request FMP from a trusted backend.
不可以。应保存到服务端环境变量或密钥管理器,由可信后端请求 FMP。
When should I use QVeris with FMP?什么时候应该将 QVeris 与 FMP 一起使用?
Use QVeris for capability discovery, live schema inspection, consistent invocation or provider fallback. A fixed single-provider integration can call FMP directly.
需要能力发现、实时 Schema 检查、统一调用或供应商降级时使用 QVeris;固定的单供应商集成可以直接调用 FMP。
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.
先正确对齐 FMP 财务报表;当工作流需要能力发现、Schema 检查或供应商感知路由时,再接入 QVeris。
