返回博客

汇率 API 的数据从哪里来?(以及两个 API 为何不一致)

V
Vlado Grigirov
August 15, 2026
Currency API Exchange Rates FX Data API Integration Finexly Developer Guide

此刻向五个不同的服务查询 EUR/USD 汇率,你会得到五个略有差异的数字。差得不算离谱——但在小数点后第四位不同,有时是第三位。如果你的财务团队曾因为结账页显示 1.0847、而银行对账单写着 1.0821 而提过工单,你就明白这不是一个学术问题。

那么,汇率 API 的数据到底从哪里来? 诚实的答案是:没有任何一个 API "知道"汇率,因为根本不存在唯一一个汇率可供知晓。外汇是场外市场,没有中央交易所,也没有收盘钟——只有全球成千上万家机构互相报价。你能调用的每一个 API,都是一条对这个市场进行采样、清洗,然后交给你一个数字的流水线。本文逐层拆解这条流水线,解释两家提供商为何会出现分歧,并演示如何在把计费逻辑建立在某个数据源之上以前先对它做审计。

简短答案:市场与你的 JSON 之间有三层

每一个汇率 API——无论免费还是付费,包括我们自己的——都由相同的三层构成:

  1. 采集。 原始价格从上游来源拉取:机构级 FX 数据流、央行发布的数据,以及经纪商或零售报价。
  2. 归一化。 这些原始价格被校验,异常值被剔除,多个来源被融合,并为每个货币对推导出唯一的参考汇率。
  3. 交付。 推导出的汇率按固定节奏做快照、缓存,并带着时间戳通过 HTTP 提供出去。

这三层中任何一层出现差异,你的响应体里就会出现不同的数字。大多数开发者以为分歧来自第 1 层。实际上,第 2 层和第 3 层造成的分歧同样多。

第 1 层 —— 原始价格究竟来自哪里

银行间与机构级数据流

最接近"真实"汇率的是银行间市场:大型银行与流动性提供商彼此成交的价格。这些价格以持续的 bid 与 ask 报价流的形式,来自交易场所、主经纪商和市场数据供应商。

这类数据流是可获得的保真度最高的来源,同时也是最昂贵的——这正是免费 API 很少将其作为主要来源的原因。当某个提供商宣称提供"实时"或"亚分钟级"汇率时,几乎总是因为其流水线顶端接入了机构级数据流。

请注意,银行间数据流给你的是两个价格而非一个——一个 bid、一个 ask。你在 API 响应里看到的那个单一汇率,通常是两者的中点。如果这个区分对你来说是新的,我们关于货币兑换中的买卖价差的指南做了详细说明。

央行参考汇率

第二大来源是央行的官方发布。最著名的例子是欧洲央行,它在每个 TARGET 工作日约 16:00 CET,基于欧洲各国央行之间的协商程序,发布欧元外汇参考汇率。另有数十家央行为本国货币发布同类的每日汇率。

央行汇率有两个巨大优势:免费,且具有权威性。许多税务机关和会计准则明确接受它们用于申报。这正是免费 API 生态很大一部分建立其上的原因。Frankfurter 是该领域被广泛使用的开源项目,它追踪 84 家央行、覆盖 201 种货币的每日汇率,历史可回溯至 1948 年——全部是公开数据的再分发。

它们也有两个严重局限:

  • 它们是每日快照,不是实时价格。 16:00 CET 的参考汇率,无法告诉你 09:00 或 22:00 发生了什么。
  • 周末与节假日会停止。 如果你的 API 在周六不返回数据,或者重复周五的数字,通常的解释就是它源自欧洲央行。

零售与经纪商报价

第三类来源是面向终端客户的定价:银行、卡组织、支付处理商或汇款服务实际给客户的价格。这些汇率已经包含加价——在市场汇率之上嵌入价格里的利润。

这就是为什么你在消费者比价网站看到的汇率与银行对账单对不上。两边都没错,它们衡量的是不同的东西。面向消费者的网站通常展示中间市场汇率,而银行报给你的是中间市场汇率加上它自己的价差。对绝大多数软件场景而言,你要的是中间市场那个数字,并且应当显式地施加你自己的加价——放在你看得见、审得清的地方。

第 2 层 —— 提供商如何把数据流变成一个汇率

原始价格到位后,提供商必须决定发布哪个数字。这里会做四个决定,而每一个都是提供商彼此分道扬镳的地方。

融合(blending)。 大多数商业 API 并不依赖单一上游来源。例如 Open Exchange Rates 将其数据描述为从多家提供商收集、以算法方式融合。融合能抹平单个坏跳价,但融合权重是专有的——这恰恰是两个融合数据流永远不会完全一致的原因。

异常值剔除。 来自某个场所的错误报价可能偏离一个数量级。提供商会应用过滤器,剔除落在共识值容差带之外的价格。激进过滤意味着汇率稳定,但对真实波动反应更慢;宽松过滤意味着反应快,但偶尔有噪声。

中间价推导。 如果上游数据流是 bid/ask,提供商会发布一个中间价。简单中点 (bid + ask) / 2 是标准做法,但按成交量加权的方法会得出略有不同的结果。

交叉汇率三角推算。 没有任何提供商会直接获取全部 30,000 多个可能的货币对。绝大多数货币对是通过一个枢纽货币计算出来的——通常是 USD 或 EUR:

GBP/JPY = (USD/JPY) / (USD/GBP)

这意味着你拿到的冷门货币对汇率,继承了另外两个货币对的舍入与时点。以 USD 为枢纽的提供商与以 EUR 为枢纽的提供商,对同一个交叉盘会得出不同的数字。相关机制见交叉汇率详解

第 3 层 —— 汇率如何抵达你的代码

最后一层是开发者掌控最多、思考最少的一层。

更新频率是提供商之间、以及不同价格档位之间最大的单一差异点。免费套餐通常一天刷新一到两次;付费档位每小时、每十分钟或每 60 秒刷新一次。两个使用相同数据的 API 会产生分歧,仅仅因为一个在 14:00 拍了快照,另一个在 14:47。

缓存会放大这一点。多数 API 位于 CDN 之后,而构建良好的客户端又会在其之上做本地缓存。给 10 分钟的刷新周期叠加 15 分钟的边缘缓存,你的应用可能正在使用 25 分钟前的汇率。用来展示价格没问题,用来结算交易则不可接受——真正的实务问题永远是对这个具体操作而言,多旧算太旧。我们关于货币 API 的缓存与错误处理的指南讲解了如何设定这些窗口。

时间戳是你的防线。任何严肃的 API 都会返回汇率被采集的时刻。请读取它。不要假设你收到响应的时刻就是该汇率成立的时刻:

const MAX_AGE_SECONDS = 900; // 15 minutes

async function getRate(base, symbol) {
  const res = await fetch(
    `https://api.finexly.com/v1/latest?base=${base}&symbols=${symbol}`,
    { headers: { Authorization: `Bearer ${process.env.FINEXLY_API_KEY}` } }
  );

  const data = await res.json();
  const ageSeconds = Math.floor(Date.now() / 1000) - data.timestamp;

  if (ageSeconds > MAX_AGE_SECONDS) {
    throw new Error(`Rate is ${ageSeconds}s old — refusing to price on stale data`);
  }

  return { rate: data.rates[symbol], ageSeconds };
}

下面是底层请求与一个具有代表性的响应结构:

curl "https://api.finexly.com/v1/latest?base=USD&symbols=EUR,GBP,JPY" \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "success": true,
  "base": "USD",
  "timestamp": 1755244800,
  "rates": {
    "EUR": 0.9241,
    "GBP": 0.7863,
    "JPY": 147.2150
  }
}

完整的参数与端点说明见 Finexly API 文档

为什么两个 API 对同一货币对返回不同数字

把三层合起来看,分歧有六个成因,大致按危害程度排序:

  1. 快照时点不同。 迄今为止最常见的原因。两个数据流都没毛病;它们只是看的时刻不同。
  2. 来源组合不同。 源自央行的汇率与源自银行间市场的汇率,按定义衡量的就是两回事。
  3. 中间市场价 vs 含加价。 一家给你市场中点,另一家给你的是价差已经嵌进去的客户价。
  4. 交叉盘的枢纽货币不同。 以 USD 为枢纽和以 EUR 为枢纽的三角推算,对同一个非美元货币对会得出不同结果。
  5. 精度与舍入。 六位小数截断为四位,或者以反向货币对发布再被重新求倒数,两者都会引入漂移。
  6. 你忘掉的缓存层。 你的 CDN、框架的 HTTP 缓存和你自己的 Redis 层,每一个都在叠加数据的"年龄"。

一条实用经验法则:对主要货币对而言,两个可靠的中间市场来源之间相差几个基点(0.01% = 1 bp)是正常且预期之内的。相差 50 bp 或以上,说明其中一个要么陈旧、要么含加价、要么坏了——上线前你应该查清是哪一个。

在信任一个汇率 API 之前如何审计它

不要仅凭信任接受提供商关于准确度的说法。用一周时间,针对你财务团队认可的权威来源跑一下这个检查:

import os
import requests
from datetime import datetime, timezone

FINEXLY_URL = "https://api.finexly.com/v1/latest"
HEADERS = {"Authorization": f"Bearer {os.environ['FINEXLY_API_KEY']}"}


def get_rate(base: str, symbol: str) -> dict:
    r = requests.get(
        FINEXLY_URL,
        headers=HEADERS,
        params={"base": base, "symbols": symbol},
        timeout=5,
    )
    r.raise_for_status()
    data = r.json()
    return {
        "rate": data["rates"][symbol],
        "captured_at": datetime.fromtimestamp(data["timestamp"], tz=timezone.utc),
    }


def basis_points(a: float, b: float) -> float:
    """Difference between two rates, in basis points."""
    return abs(a - b) / ((a + b) / 2) * 10_000


primary = get_rate("EUR", "USD")
reference = 1.0839  # whatever your accounting source published

diff = basis_points(primary["rate"], reference)
print(f"Finexly: {primary['rate']}  captured {primary['captured_at']:%H:%M UTC}")
print(f"Reference: {reference}")
print(f"Delta: {diff:.1f} bp  ->  {'OK' if diff < 25 else 'INVESTIGATE'}")

在结果中重点看三件事:

  • 差值是稳定的还是在漂移? 恒定的偏移暗示存在系统性加价;随机的偏移暗示时点问题。
  • 差值是否在特定时段跳升? 这指向快照时点,通常出现在某家央行的发布窗口附近。
  • 周末会怎样? 如果你的来源周五下午冻结、周一才恢复,那它源自央行——请据此安排周一的对账。

你也可以验证三角推算:直接取一个交叉盘,再通过 USD 计算一遍,两者应当在一到两个基点内吻合。

按使用场景选择数据源

不存在放之四海皆"最好"的来源——只有适合你正在构建之物的来源。

使用场景你需要什么可接受的数据陈旧度
向购物者展示价格中间市场汇率,之上叠加你自己的加价小时级
SaaS 订阅计费中间市场价,每个计费批次一个快照,随发票一并保存小时级,但必须留痕
会计与税务申报特定日期的央行参考汇率按定义即每日
分析与仪表盘来自单一来源的一致历史时间序列每日
付款与汇款新鲜的中间市场价,并设定明确的容差带分钟级
交易与对冲来自机构级数据流的真实 bid/ask秒级
有两条实务规则贯穿以上所有场景。第一,永远保存你实际使用的那个汇率,连同其时间戳与来源一起存在交易记录旁——事后重建是不可能的,而审计师一定会问。第二,每个记录系统只用一个来源。在结账与总账之间混用提供商,等于保证会产生几分钱的漂移,而半年后没人解释得清。

如果你还在评估选项,我们对免费与付费货币 API的比较拆解了升级档位后有哪些变化,价格方案页面则展示了刷新频率与请求上限的位置。若只想对某个货币对做一次快速人工核对,货币转换器使用与 API 相同的底层数据流。

常见问题

免费货币 API 的数据从哪里来?

几乎总是来自央行发布,最常见的是欧洲央行的每日欧元参考汇率,有时会与少数其他公开来源融合。这正是免费档位通常一天只刷新一次、跳过周末,且覆盖的冷门货币比付费档位少的原因。

为什么我的 API 汇率和 Google 上的不一样?

Google 显示的是中间市场参考汇率,它是快照而非连续的实时价格,且未必与你调用 API 的时刻同步采样。小幅差异很正常;大幅差异通常意味着两者之一是含加价的零售汇率,而非中间市场汇率。

会计和税务申报应该使用哪个汇率?

使用相关央行针对交易日期发布的官方参考汇率——这是大多数税务机关的预期。请通过带明确日期的历史端点获取,而不是复用实时汇率,并把它与交易记录一并保存。

实时汇率 API 真的是实时的吗?

严格意义上很少是。"实时"通常意味着提供商以较短间隔刷新——最高档位常见 60 秒——而不是逐笔推送。请看响应中的时间戳和文档写明的刷新间隔,而不是营销文案。

我能直接爬取汇率而不用 API 吗?

可以,但你会继承所爬页面的全部失败模式:版面变更、速率限制、没有时间戳、没有历史回填,并且经常违反服务条款。完整权衡见货币 API 与网页爬取对比

建立在你能审计的数据流之上

清楚你的汇率数据从哪里来,决定了你面对的是一个一句话就能解释的货币 bug,还是一个吞掉一周工程时间的 bug。集成之前向任何提供商提三个问题:数据来源是什么、多久刷新一次、每个响应是否带采集时间戳。

准备好接入你真正能审计的汇率数据了吗?免费获取 Finexly API 密钥——无需信用卡。从每月 1,000 次免费请求起步,覆盖 170+ 种货币,从第一天起就提供带时间戳的响应与历史数据。

Vlado Grigirov

Senior Currency Markets Analyst & Financial Strategist

Vlado Grigirov is a senior currency markets analyst and financial strategist with over 14 years of experience in foreign exchange markets, cross-border finance, and currency risk management. He has wo...

View full profile →