综合

A 股免费 API 最危险的地方:不是不能用,而是错了你不知道

作者: TickDB Research · 发布: 2026/6/3 · 阅读: 2

标签: B 类, 今日头条, A 股

摘要

免费行情 API 最危险的坑,不是接口挂了,而是它一直能返回数据——HTTP 200、JSON 正常、DataFrame 有数据——但数据不完整、字段单位不对、复权口径变了。本文基于开源项目公开 issue 和官方文档,整理出四种静默失败模式,并给出从“应急接一个免费源”到“建一个长期可用的数据接入层”的完整思考路径。

!image.png

一、它没报错,但数据少了一大截

用免费行情接口做全市场扫描时,有一种常见的排查场景:接口调通了,数据能拉到,本地数据库跑了一段时间后,偶然发现股票数量跟交易所公布的上市数量对不上。

排查一圈,最后发现不是代码 bug,不是网络问题——是上游数据源的分页规则变了。 之前调用一次能拿回全量数据,上游改版后默认只返回前 200 条。接口没报错,JSON 结构完全正常,只是数据不完整。

免费 API 最危险的不是报错——报错你至少会停下来查。最危险的是返回了 HTTP 200、JSON 格式正常、DataFrame 里有数据,但数据是残缺的,而你浑然不知。

这个现象在开源社区里有公开佐证。2025 年 3 月,AKShare 的 GitHub issue #5810 记录了完全一致的情况:东方财富接口改版后,默认分页变成强制 200 条,必须手动补分页参数才能拿到全量数据。发帖人还提醒:自己只测了几个接口,其他接口可能也有类似问题。

核心问题:免费行情接口常依赖上游网页或非正式接口。上游改版时,最危险的不是接口挂掉,而是行为悄悄变化,下游不校验就发现不了。

这件事揭示了一个更根本的问题:把“能拉到数据”当成了“数据接入完成了”。但实际上,从“应急接一个免费源”到“建一个长期可用的数据接入层”,中间缺了一整套校验和抽象机制——校验帮你发现数据哪里不对,抽象帮你换数据源时不用重写整个管道。

!image.png

二、四种静默失败模式

以下四种模式的共同特征是:接口没挂,数据返回了,但你不能信。

失败模式看起来实际上你怎么提前发现
数据不完整HTTP 200,DataFrame 有数据分页规则变了,只拿到部分股票校验返回数量与交易所公布数量
字段语义漂移字段名叫 volume,能用可能是手、股、金额或万元读文档注释,标注单位
隐性限流/断连单次调用正常定时轮询后触发验证码、连接断开开盘时段做负载测试
停更与迁移成本现在能用就行迁移是整个数据管道重做检查更新时间和 issue 回复

模式一:数据不完整——返回了,但少了一大截

用免费接口拉全市场股票列表时,你以为拿到了全部,但实际上可能只拿到了一部分。原因通常是两个:

上游分页规则变化。除了 issue #5810,AKShare 在 2025 年 3 月的 issue #5999 也报告了类似问题:Eastmoney 接口变动导致返回列表不全,老版本只返回约 300 项。

上游接口改版导致覆盖范围缩小。接口没报错,但某些品种的数据就是拿不到。

你要做的:调用全市场接口后,校验返回数量是否与交易所公布数量在同一量级。差距明显时,排查分页参数和接口版本。

这个校验逻辑,在工程上最好的落地方式不是在业务代码里写一堆 if-else,而是在数据接入层统一做。一个好的数据接入层,应该在拿到接口返回后自动完成数量校验、字段完整性检查和异常标记,而不是让每个使用数据的下游模块各自重复这些工作。

模式二:字段语义漂移——字段名没变,但意思变了

API 返回的 JSON 里,字段名叫 volume,你觉得这肯定是成交量。但它的单位是什么?

AKShare 官方文档写得很清楚:A 股实时行情接口的成交量单位是“手”,成交额单位是“元”。但同一文档的美股、港股历史行情示例里,成交量单位又变成了“股”。同样叫“成交量”,跨了市场,单位就变了。

另一个开源项目 easyquotation 的 README 里,turnover 被注释为“交易股数”,volume 被注释为“交易金额”;在另一个数据源示例里,volume 又被注释为“成交额(万元)”。同一个字段名,在不同数据源里表示完全不同的东西。

更隐蔽的是复权数据的时变性。 AKShare 官方文档专门说明:前复权为了保持当前价格不变,每次除权除息后,历史价格都会重新调整。你 3 月拉下来的“2019 年某只股票的前复权价格”,和你 6 月拉下来的同一天的价格,可能不一样。不是数据错了,是复权机制本身就是时变的。

核心原则:不要问字段叫什么,要问字段在这个接口、这个市场、这个日期里到底表示什么。用复权数据,必须记录复权方式和查询日期。

这个问题的工程解法是:在接入层强制做字段语义锁定。比如,一个规范的数据接入层应该明确区分 ticker 快照的价格字段(last_price)和 K 线的价格字段(close),两者不混用;成交量字段必须标注单位(手/股)和市场(A 股/港股/美股);时间戳统一标注单位(毫秒)和时区(UTC),并明确声明“字段精度不等于数据新鲜度”。涉及 24 小时口径字段(如 volume_24h)时,应按具体市场和接口文档确认其可用性,并非所有市场的 ticker 推送都包含此字段。

当你换数据源时,只要接入层的字段映射表更新了,上游所有业务代码都不用动。这才是“迁移成本可控”的真正含义。

模式三:隐性限流与断连——Demo 没事,系统化跑起来才出事

Demo 阶段的调用:写几行代码,查一两只股票,跑一遍,看到结果,收工。不触发任何风控。

系统化使用完全不同:定时轮询几百只股票、并发抓取全市场数据、开盘时段高频请求——这些负载会触发上游的各类限制。

  • 2025 年 6 月,AKShare issue #6239:东方财富网页增加滑动验证码,导致接口直接返回连接错误。
  • 2025 年 4 月,issue #6061:用户按 10 秒周期定时调用同一接口后出现问题。反馈中有一句很实在的话:“免费的服务要省着点用。”
  • 同月,issue #6064:记录了连接超时和远端断开的具体错误信息。

免费抓取路径通常没有面向开发者的 SLA 承诺,上游风控和页面改版不受调用方控制。 Demo 阶段单次调用正常,不代表系统化运行后能保持同样的稳定性。

>

你要做的:在开盘时段做实际负载测试,观察延迟和成功率。做好限频、缓存、退避和备选数据源。

在工程上,一个好的数据接入层应该把错误码语义固定下来——比如“限流(附带 Retry-After 头,应退避重试)”和“鉴权失败(直接阻断,不应重试)”是两种完全不同的失败类型,处理策略截然不同。免费接口通常不提供这种粒度的错误信息,但你应该在接入层自己封装一套,这样换数据源时,错误处理逻辑的改动范围被限制在接入层内部。

模式四:停更与迁移成本——换接口不是换 URL

很多人觉得“这个不行了就换一个”,把迁移想象成改一行 URL。实际上,换一个数据源意味着整个数据管道重做:

  • 字段名全部重新映射(last_price vs latestPrice vs current
  • 数据结构重新适配(data["data"] 是数组还是字典?嵌套层级变了吗?)
  • 复权口径重新对齐(新旧数据源的前复权计算方式可能不同)
  • 错误处理逻辑重写(限流码是 429 还是 3001?超时是抛异常还是返回空?)
  • 历史数据重新下载和对比

开源项目自己也很清楚这一点。AKShare 的 README 写得很诚实:数据用于学术研究、仅供参考、不构成投资建议;基于不可控因素,部分数据接口可能被移除。这不是“免责声明”,而是项目的真实边界——维护者自己也没有承诺生产级稳定性。

Tushare Pro 的权限文档展示了另一个现实:正式行情数据服务设置积分门槛和频次分层,与服务器和带宽成本直接相关。这不是“商业化限制了自由”,而是“可持续性本身有天花板”。

你要做的:检查项目最近更新时间、issue 回复频率、文档维护状态。在接入层做字段映射抽象,降低未来迁移的工程成本。

!image.png

三、从四种失败模式到数据接入层的四个设计约束

上面四种模式分别暴露了四个工程缺口。把它们放在一起,可以提炼出一套数据接入层的设计约束——不管你现在用的是免费接口还是商业接口,这四个约束都是通用的:

失败模式暴露的工程缺口数据接入层应该提供的约束
数据不完整返回数量未校验,分页变化静默丢失返回结构可校验:自动校验返回数量、覆盖范围和字段完整性
字段语义漂移字段名不变但语义变了,跨市场单位不一致锁定字段语义:ticker 用 last_price,K 线用 close,成交量标注单位
隐性限流/断连错误信息不透明,不知道是该重试还是该放弃固定错误码语义:限流退避 vs 鉴权阻断,不同错误不同策略
停更与迁移成本字段映射、错误处理、复权口径全部耦合在业务代码里抽象层隔离:换数据源只改接入层,上游业务代码不动

这就是数据接入层的价值:它不是在卖功能,而是在解决“每换一个数据源就要重写所有校验、映射和错误处理”这个真实的工程痛点。

以 TickDB 作为这套设计约束的一个落地参考,其接口设计体现了这些原则:ticker 端点返回结构为数组,调用方可显式遍历校验每个品种的返回状态;ticker 用 last_price 字段,K 线用 close 字段,分属不同端点,避免混用;错误码区分了 3001(限流,附 Retry-After 头,应退避重试)和 1001(鉴权失败,应直接阻断);REST 与 WebSocket 均输出统一 JSON 结构,字段名和数据结构在文档中有明确约定,迁移时只需更新接入层的映射逻辑。

如果你用其他数据源,可以用同样的四个约束去衡量它:返回结构是否可校验?字段语义是否锁定?错误码是否区分重试和阻断?迁移时接入层能否隔离变更?

!image.png

四、免费不是没有成本,只是成本被转移了

四种失败模式和数据接入层的设计约束,合在一起指向同一个深层逻辑:

免费接口不是没有成本,只是把成本从账单转移到了别的地方。

你付的不是钱,是时间——排查分页变化、验证字段单位、处理限流退避、应对上游改版。你付的不是订阅费,是风险——上游随时可能增加验证码、调整分页规则甚至完全停更,而免费抓取路径通常没有面向开发者的 SLA 承诺。

这不是说免费接口不好。很多开源项目质量很高,文档也很诚实。问题在于使用者自己:你把一个 README 里写了“仅供参考、接口可能移除”的项目,当成了长期系统的数据基石。 这不是项目的错,是你的选型决策出了问题。

而解决这个问题的钥匙,不在于“找一个永远不会变的数据源”——这样的数据源不存在。钥匙在于在架构里留一个“数据源可以换”的位置,也就是前面说的接入层抽象。它不消除风险,但它把风险隔离在一个可控的范围内。

五、使用前检查清单(建议保存)

在把一个免费行情接口写进项目代码之前,先问自己这八个问题:

#问题能回答才通过
1全市场返回数量校验过吗?与交易所公布数量是否一致?
2每个字段的单位确认了吗?volume 是手还是股?
3如果用复权数据,复权方式和查询日期记录了吗?
4限频策略是什么?触发后是降级还是硬报错?
5在开盘时段做过实际负载测试吗?延迟和成功率是否可接受?
6项目最近一次更新是什么时候?issue 还在回复吗?
7如果这个接口明天停更,备选方案是什么?
8迁移成本估算过吗?接入层有没有做字段映射抽象?

项目越接近生产,允许不通过的项应该越少。

六、不能从本文推出什么

  • 不能推出任何免费接口的推荐或排名
  • 不能推出 AKShare、Tushare、easyquotation 等项目“不可靠”——相反,这些项目文档诚实、社区活跃,本文引用的 issue 恰恰来自它们公开透明的维护记录
  • 不能推出投资建议、交易策略或买卖信号
  • 不能替代正式的技术选型和数据质量验证

七、结尾

免费行情 API 真正的风险,不在于“它总有一天会挂”,而在于“它挂的时候你可能完全不知道”——因为它的失败往往是静默的。

你用免费行情接口时,最先遇到的是哪种情况:数据不完整、字段单位搞错、接口突然断连、还是项目直接停更了?

我在排查分页逻辑上花的时间,比写核心逻辑代码还多。欢迎分享你的经历——这些经验比接口列表有用得多。

本文主要参考来源

  1. AKShare issue #5810:东方财富接口分页规则变化导致全市场列表不完整。https://github.com/akfamily/akshare/issues/5810
  2. AKShare issue #5999:Eastmoney 接口变动导致返回列表不全。https://github.com/akfamily/akshare/issues/5999
  3. AKShare issue #6239:东方财富网页增加滑动验证导致接口连接错误。https://github.com/akfamily/akshare/issues/6239
  4. AKShare issue #6061:高频/并发调用疑似触发上游风控。https://github.com/akfamily/akshare/issues/6061
  5. AKShare issue #6064:连接超时与远端断开的具体错误信息。https://github.com/akfamily/akshare/issues/6064
  6. AKShare 官方文档:字段单位说明(A 股成交量单位“手”、成交额“元”)及前复权历史价格时变说明。https://github.com/akfamily/akshare
  7. easyquotation READMEturnover/volume 等字段在不同数据源中的语义不直观。https://github.com/shidenggui/easyquotation
  8. Tushare Pro 权限文档:积分、频次与权限分层说明。https://tushare.pro/document/2?doc_id=290

通过 TickDB API 获取实时行情数据

一个 API 接入外汇、加密货币、美股、港股、A股、贵金属和全球指数的实时行情。支持 WebSocket 低延迟推送,免费开始使用。

免费领取 API Key查看 API 文档

相关文章