本页目录
最短的正确实现路径
/stable/quote。
API Key 必须保存在服务端;设置超时;拒绝空数组;验证响应时间戳;按交易时段缓存;只对 429 或 5xx 重试。图表应使用历史端点,不要靠反复调用 quote 拼接。
使用 FMP 价格数据开发行情面板、预警、研究管线、AI 工具或投资组合功能的开发者。
FMP API Key、服务端运行环境、明确的新鲜度目标,以及复权或不复权的历史数据策略。
根据任务选端点,不要只看名字
“股票价格”可能指最新快照、关注列表的轻量刷新、日线或分钟线。它们的字段、调用成本和时效要求都不同。
| 需求 | Stable endpoint | 适用场景 | 注意事项 |
|---|---|---|---|
| 完整最新报价 | /quote?symbol=AAPL | 需要价格、涨跌、区间和成交量。 | 返回字段较多。 |
| 轻量最新报价 | /quote-short?symbol=AAPL | 只需要代码、价格和成交量。 | 可用于校验的上下文较少。 |
| 多个股票代码 | /batch-quote/batch-quote-short | 关注列表或组合定时刷新。 | 套餐权限与响应体大小。 |
| 日线历史 | /historical-price-eod/full | 图表、收益计算与回测。 | 复权策略。 |
| 盘中 K 线 | /historical-chart/1min/5min · /15min · /30min · /1hour | 盘中图表与技术指标。 | 套餐、历史深度与时区。 |
| 盘前盘后 | /aftermarket-quote/aftermarket-trade | 明确标注扩展交易时段数据。 | 不要静默混入正常收盘价。 |
“实时”并不是一个统一承诺
交易所授权、端点、套餐和交易时段共同决定数据是实时、延迟、盘后还是日终。界面不能因为接口叫 “quote” 就假设它一定实时。
市场事件实际发生的时间。读取响应时间戳并统一转换为 UTC。
你的服务收到数据的时间。单独记录,才能衡量延迟与缓存年龄。
周六看到周五收盘价是正常的。“旧”不一定是“失效”,要结合交易所日历判断。
合并股票或计算收益前,明确币种、交易场所和公司行动复权规则。
安全调用 FMP Quote API
从环境变量或密钥管理器读取 FMP_API_KEY。FMP 支持 apikey 请求头和查询参数;请求头更不容易把密钥泄露到 URL 与日志中。
设置超时、编码股票代码、限制并发,并记录端点延迟。
HTTP 200 并不代表业务成功。空数组、缺失代码、非有限价格或不可接受的时间戳都应拒绝。
curl --fail --silent --show-error \
--get "https://financialmodelingprep.com/stable/quote" \
--data-urlencode "symbol=AAPL" \
--header "apikey: $FMP_API_KEY"import os, time, requests
def get_quote(symbol: str) -> dict:
response = requests.get(
"https://financialmodelingprep.com/stable/quote",
params={"symbol": symbol.upper()},
headers={"apikey": os.environ["FMP_API_KEY"]},
timeout=8,
)
response.raise_for_status()
rows = response.json()
if not isinstance(rows, list) or not rows:
raise ValueError(f"No FMP quote returned for {symbol}")
quote = rows[0]
price = float(quote["price"])
event_time = int(quote["timestamp"])
if price <= 0 or time.time() - event_time > 20 * 60:
raise ValueError("Invalid or unexpectedly stale quote")
return {
"symbol": quote["symbol"],
"price": price,
"currency": quote.get("currency"),
"volume": quote.get("volume"),
"eventTime": event_time,
"retrievedAt": int(time.time()),
"source": "fmp",
}type Price = {
symbol: string; price: number; volume?: number;
eventTime: number; retrievedAt: number; source: "fmp";
};
export async function getQuote(symbol: string): Promise<Price> {
const key = process.env.FMP_API_KEY;
if (!key) throw new Error("FMP_API_KEY is missing");
const url = new URL("https://financialmodelingprep.com/stable/quote");
url.searchParams.set("symbol", symbol.toUpperCase());
const response = await fetch(url, {
headers: { apikey: key },
signal: AbortSignal.timeout(8_000),
});
if (!response.ok) throw new Error(\`FMP returned \${response.status}\`);
const rows: unknown = await response.json();
if (!Array.isArray(rows) || !rows.length) {
throw new Error(\`No quote returned for \${symbol}\`);
}
const q = rows[0] as Record<string, unknown>;
if (!Number.isFinite(Number(q.price)) || !Number.isFinite(Number(q.timestamp))) {
throw new Error("Invalid quote payload");
}
return {
symbol: String(q.symbol),
price: Number(q.price),
volume: q.volume == null ? undefined : Number(q.volume),
eventTime: Number(q.timestamp),
retrievedAt: Math.floor(Date.now() / 1000),
source: "fmp",
};
}把价格标准化为可验证证据
不要让供应商的原始响应贯穿整个应用。建立一个小而稳定的内部契约,并在政策允许时保留原始响应以便排错或审计。
| 字段 | 为什么需要 | 校验 |
|---|---|---|
symbol + exchange | 仅有股票代码可能存在歧义。 | 匹配请求的标的与市场。 |
price + currency | 没有币种的数字无法安全使用。 | 有限、正数、币种符合预期。 |
eventTime | 用于判断新鲜度与排序。 | 有效时间戳,并结合交易日历判断。 |
retrievedAt | 衡量传输与缓存年龄。 | 由你的服务写入,而非供应商。 |
adjustment | 防止混用不同复权口径。 | 显式枚举:原始、拆股复权或股息复权。 |
source | 为 Agent 与审计保留来源。 | 供应商、端点,以及可用时的请求 ID。 |
计算之前先确定复权口径
FMP 提供完整与轻量日终历史,同时有不做拆股复权和股息复权的版本。应根据分析问题选择序列。
优先使用口径一致的复权序列,避免拆股或分红在图上表现为无缘无故的暴跌。
使用当时真实成交价格,并单独建模拆股、股息、费用和执行情况。
每个落库数据集都应保存复权方式、周期、时区与请求日期范围。
真正重要的生产控制
开盘中的关注列表使用短 TTL;闭市后延长;已完成的历史 K 线可按不可变数据缓存。
批量接口可减少调用次数,但响应更大、失败影响更广。超大标的池应分成有上限的批次。
对 429 和 5xx 使用指数退避、随机抖动与最大次数;无效代码或鉴权失败不要重试。
监控延迟、状态码、空结果率、报价年龄、缓存命中率和供应商成本。告警不能只看网络错误。
上线前验证清单
- API Key 未出现在浏览器包、URL、分析系统或应用日志中。
- 有效代码返回非空数组,且代码与请求一致。
- 无效代码、过期密钥、超时与 429 都有经过测试的错误路径。
- 新鲜度阈值考虑周末、节假日与盘前盘后。
- 历史测试覆盖一个已知拆股或分红事件,并记录复权行为。
- 展示或再分发数据符合适用的 FMP 许可条款。
常见失败模式
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| HTTP 200,但数组为空 | 代码无效或不受支持。 | 按未找到处理,并核验代码与交易所。 |
| 401 / 403 | 密钥缺失、无效或套餐权限不足。 | 停止重试;检查密钥注入与套餐。 |
| 429 | 触发速率或配额限制。 | 按提示退避,并使用批量与缓存。 |
| 报价看起来过旧 | 闭市、延迟授权或缓存数据。 | 将事件时间与交易时段、获取时间比较。 |
| 图表突然断崖式下跌 | 拆股、股息或混用复权口径。 | 检查公司行动,并重建口径一致的序列。 |
| 日线日期发生偏移 | UTC 与本地时区转换问题。 | 同时保存 UTC 与交易所时区,只在展示层格式化。 |
当工作流需要“选择能力”时,再引入能力层
当供应商和数据结构固定时,直接调用 FMP 很合适。当 Agent 需要发现可用能力、检查当前输入契约、统一调用、保存证据或跨供应商降级时,QVeris 才更有价值。
根据任务意图、市场与新鲜度要求查找股票报价或历史价格能力。
调用前读取实时 Schema,确认代码格式、必填字段与输出来源。
使用校验后的参数执行调用,再应用本文同样的标准化与新鲜度规则。
根据频率与历史深度选择套餐
FMP 定价会变化。截至 2026 年 7 月 30 日,公开套餐为:免费版每天 250 次;Starter 按年付折算每月 22 美元、每分钟 300 次;Premium 每月 59 美元、每分钟 750 次;Ultimate 每月 149 美元、每分钟 3,000 次。盘中历史深度、全球覆盖、批量权限和带宽也不同。
FMP 定价页说明,展示或再分发数据需要适用的数据展示与许可协议。购买或上线前请核验最新套餐和条款。
FMP 股票价格 API 常见问题
FMP 股票价格 API 是实时的吗?
这取决于端点、交易所覆盖、数据授权和套餐。应把实时、延迟、盘后和日终视为不同数据契约,并验证响应时间戳。
哪个端点返回最新股票价格?
完整快照使用 /stable/quote;只需要代码、价格和成交量时使用 /stable/quote-short。
如何一次请求多只股票?
完整快照使用 batch-quote,轻量结果使用 batch-quote-short。先确认套餐权限,并将超大标的池分批。
FMP 是否提供历史股票价格?
提供。FMP 文档包含完整、轻量、不做拆股复权和股息复权的日终端点,以及 1 分钟至 1 小时的盘中周期。
应该使用复权还是不复权价格?
收益计算和连续图表使用口径一致的复权序列;还原真实交易时使用不复权成交价,并单独处理公司行动。
可以把 API Key 放到浏览器代码里吗?
不可以。把密钥保存在服务端环境变量或密钥管理器中,并通过后端代理请求。
如何处理 FMP 限流?
缓存相同请求、适当批量化、限制并发,并只对瞬时 429 和 5xx 使用指数退避与随机抖动重试。
什么时候应该在 FMP 前使用 QVeris?
需要能力发现、Schema 检查、统一调用或供应商降级时使用 QVeris;若永久绑定单一 FMP 端点,直接服务端调用可能更简单。
以最新官方文档为准
让每一个价格都可解释
先选择正确的 FMP 端点;当工作流需要能力发现、Schema 检查或供应商感知路由时,再接入 QVeris。

