
做后端这几年我评测过的 API 少说也有上百个但真正让我愿意长期留在书签栏里的资料public-apis 算一个。这个开源项目在 GitHub 上攒到了 474k Star本质上就是一份按领域分类的免费 API 终极清单从动物图片、图书数据到金融汇率、机器学习接口你能想到的常见场景里面基本都能找到对应入口。对于不知道去哪找 API、或者不想再靠搜索引擎碰运气的人来说它几乎是一站式答案。我一直觉得找 API 这件事实在太消耗精力了。以前要做一个功能先得想“这种数据哪里提供”然后打开搜索引擎翻半天再挨个点进官网看文档、找鉴权方式、试请求格式运气不好还会碰到文档年久失修的情况。public-apis 把最费时间的“筛选”这一步提前做完了而且所有信息都压缩在一张二维表里一眼就能看出某个接口是否需要鉴权、支不支持 HTTPS、有没有 CORS 跨域支持。这篇文章我就从一个天天跟 API 打交道的开发者视角把这份清单的用法、坑点、以及基于它的实战经验一次讲透。1. public-apis 到底是什么为什么它能攒到 474k Star1.1 项目起源与核心形态public-apis 最早是开发者 davemachado 在 2016 年发起的一个个人项目初衷特别朴素把自己收集的免费 API 整理成一份公开清单方便自己查也方便别人用。结果这个仓库越滚越大后来有大量社区开发者参与维护和贡献慢慢演变成了今天这个拥有一千四百多个 API、几十个一级分类的大型资源库。项目的形态没有很复杂核心就是一份庞大的 Markdown 文件。README 开头先解释这是什么、收录标准是什么然后按字母顺序列出几十个分类每个分类下面是一张表格每一行对应一个 API。表格包含四个关键字段API 名称、API 描述、认证方式Auth、是否支持 HTTPS、是否支持 CORS。很多分类还附了官方的文档链接和项目主页方便你顺着点过去查看具体接口规范。我比较喜欢它的一点是它不追求收录数量上的堆砌而是尽量保留每一个 API 的关键元数据。一个接口如果已经被官方下线或者长期无人维护项目维护者会在 PR 和 issue 里标记出来甚至直接移除。这种社区审核机制让名单里有相当一部分接口是真实可用的比那些只知道堆链接的导航站有含金量得多。1.2 覆盖哪些领域适合谁来用如果你只是偶尔写个小脚本或者做前端 Demo 需要一些模拟数据public-apis 里最容易上手的是 Animals、Books、Food Drink 这类分类。比如你只想在页面上展示一张随机的狗狗图片直接调 Dog CEO 的接口就行连 Key 都不用申请。如果你在公司做项目调研需要找一些金融、地理信息、机器学习相关的公共数据源也可以在 Finance、Geocoding、Machine Learning 这些分类下找到不少企业级平台提供的免费额度。从适用人群来看我认为三类人最受益。第一类是独立开发者和学习者做个人项目的时候需要真实数据来验证功能但又不想一上来就付费买服务。第二类是前端开发者做页面原型时经常需要 mock 数据与其本地造假数据不如直接挂一个真实接口展示效果更有说服力。第三类是产品经理或者研究人员他们不一定会写代码但想快速了解某个领域有哪些公开数据可以合作这份清单就是非常好的行业索引。2. 拿到这份清单后怎么高效检索目标 API2.1 三种浏览方式各有什么适用场景很多人打开 GitHub 仓库看到那么长的 README 文档第一反应是眼晕。其实想用好这份清单不一定非得在网页上一行一行翻。根据你要做的事情不同我建议按这三种方式之一来操作。如果你只是想随便逛逛看看最近有哪些好玩的 API直接在 GitHub 网页上滑浏览就行。公共 APIs 的仓库主页有两种视图一种就是标准 Markdown 渲染后的表格另一种是项目自己提供的 开发者网站里面做了分类筛选用起来更符合普通人的点击习惯。但如果你是带着明确需求来的比如“我想找一个支持中文翻译的免费接口”那在网页上手动找效率就太低了建议把仓库克隆到本地然后用文本工具搜索。git clone https://github.com/public-apis/public-apis.git cd public-apis克隆下来之后README.md 就是一份纯文本资源库。你可以用 grep、编辑器搜索、甚至写一个小脚本把表格解析成 JSON方便后面进一步加工。我自己的习惯是把它直接变成一份本地的 SQLite 数据库再按标签和关键词查询比每次打开 GitHub 快很多。第三种方式是用 GitHub 的在线搜索直接在仓库内搜关键词不过只能搜当前文件内容功能比较受限。2.2 先看 Auth 字段再决定要不要深入表格里的 Auth 字段是第一个要关注的信息因为它直接决定你接入的成本。public-apis 里把认证方式分成了几类No Auth表示无需任何鉴权拿到 URL 就能请求API Key表示需要去官网注册账号、申请一个密钥放到请求头或参数里OAuth表示要走标准的 OAuth 授权流程适合需要操作用户数据的场景接入成本最高还有一类标了User-Agent表示只需要在请求头里加一个自定义的 User-Agent 标识来源即可。我平时做个人项目优先选No Auth的接口因为省事。但如果需要更稳定的服务或者更高的配额我会挑API Key类型的毕竟免费的 Key 一般都有几百到几千次/天的限额够个人项目用。OAuth 的接口建议直接绕开——除非你是专门做第三方登录功能否则为一个简单数据请求付出整套授权流程的代价不太划算。2.3 HTTPS 和 CORS 字段决定了你能不能在前端直接用HTTPS 字段看着很不起眼但实际影响很大。现在主流浏览器对混合内容的限制越来越严格如果一个网页本身是 HTTPS 部署的却去请求一个 HTTP 协议的接口大概率会被浏览器直接拦截。所以只要你的网站上线跑在 HTTPS 下选 API 时就尽量只挑 HTTPS 那一列打了勾的。CORS 字段则是纯前端开发者的命门。如果你的项目是后端渲染的接口请求由服务器发起完全不关心 CORS。但如果你用 Vue、React 这类前端框架直接调接口就要注意浏览器跨域策略了。CORS 字段有三个值Yes表示接口允许跨域调用前端可以直接 fetchUnknown表示尚未验证No表示基本无法绕过跨域限制除非你套一层自己的代理服务。看到No的接口别浪费时间强行调用直接考虑代理方案或者换其它 API。2.4 判断一个 API 是否靠谱的五个维度收藏一个 API 之前我一般会按五个维度做个快速体检。第一个是文档是否完整一个连示例代码都没写的接口调试成本会很高。第二个是返回数据是否结构化优先选择返回 JSON 格式的方便程序解析。第三个是更新维护是否活跃可以看看项目的 GitHub 仓库最近有没有 commit或者官网有没有更新日志。第四个是社区使用情况在搜索引擎里搜一下这个 API 的名称和关键词看看别人是怎么用的、有没有踩坑记录。第五个是条款限制部分免费 API 是禁止商用或者限制使用频率的商用前一定要仔细阅读服务条款。这种“综合评估”看似麻烦却能省下后期很多填坑时间。我遇到过把接口文档页都做完结果上线第三天接口就停止服务的案例从那以后我再接免费 API 之前都会先看它的运营主体和稳定性尽量挑大的平台或者有商业背景支撑的服务商。3. 实战基于 public-apis 搭一个每日卡片小工具3.1 从需求到选型怎么把需求对应到清单分类里理论说太多容易飘不如直接来一个完整的实战。假设我现在想做一个小工具每天自动生成一张卡片图左上角放一张萌宠图片下方配一句冷笑话文案发布到自己的博客侧栏。这个需求看起来简单但要分三步对应到 public-apis 里的分类宠物图片去 Animals 分类找笑话文案去 Text Analysis 或者趣向分类找最终生成图片可以本地处理。按照“认识一个 API 前先看 Auth 和 HTTPS”的原则我挑选了 Dog CEO 和 icanhazdadjoke 两个接口。Dog CEO 提供随机狗狗图片响应里直接返回图片 URL不需要 Key还是纯 HTTPS。icanhazdadjoke 是一个笑话接口通过请求头设置Accept: application/json可以返回 JSON 格式的笑话文本同样 No Auth。这俩接口在社区里知名度很高我在 GitHub 上确认过它们的仓库活跃度都比较稳妥。3.2 获取数据的关键代码与参数解析先实现两个获取数据的函数逻辑不复杂但是要注意几个细节。Dog CEO 的请求地址是/api/breeds/image/random返回结构是{message: 图片 URL, status: success}。icanhazdadjoke 的接口则需要注意请求头不带Accept: application/json的话它会返回一段 HTML 页面这段我一开始就踩过坑。import requests def get_dog_image(): url https://dog.ceo/api/breeds/image/random resp requests.get(url, timeout10) resp.raise_for_status() data resp.json() if data.get(status) success: return data[message] return None def get_joke(): url https://icanhazdadjoke.com/ resp requests.get(url, headers{Accept: application/json}, timeout10) resp.raise_for_status() data resp.json() return data.get(joke)这里有两个实战要点。第一个是记得给请求加timeout参数不要用默认值等很久很多免费 API 偶尔会不稳定不加超时的话一个卡住的请求就能拖垮整个脚本。第二个是请求结束后立刻解析 JSON尽量在 Python 侧做数据结构的校验避免拿到脏数据还继续往下处理。把这个流程跑通之后后续无论换成清单里其它 API逻辑都是相通的。3.3 数据清洗与图片合成把接口数据变成可用产品光拿到数据还不够Dog CEO 返回的是一张原始图片地址笑话文本可能含有这类需要转义的字符。所以我会先用Pillow库拉取狗狗图片做一次裁剪和尺寸调整然后把笑话文本里的特殊字符做转义处理最后将两者绘制到一张背景图上。这一步虽然不涉及 public-apis 本身的 API但也是整个流程不可或缺的一环。from PIL import Image, ImageDraw, ImageFont import requests import textwrap import html dog_url get_dog_image() joke get_joke() joke html.unescape(joke) img Image.open(requests.get(dog_url, streamTrue).raw) img img.resize((640, 400)) card Image.new(RGB, (640, 520), white) card.paste(img, (0, 0)) draw ImageDraw.Draw(card) wrapped_text textwrap.fill(joke, width40) draw.text((20, 420), wrapped_text, fillblack) card.save(daily_card.png)实际运行下来Pillow 在拉取网络图片时有一个容易被忽略的行为直接用Image.open(requests.get(...).stream).raw读取流有时会因为图片的 EXIF 信息导致方向不对遇到手机拍摄的图片时尤其明显。稳妥的做法是先下载到本地临时文件再用 Pillow 打开。这个坑我踩过一次之后每次都老老实实用tempfile中转。3.4 定时运行任务怎么用脚本把流程自动化卡片生成逻辑调通后剩下就是让它每天自动跑一次。最简单的方案是用cron或者systemd timer在服务器上定时执行但如果你没有服务器可以退而求其次用自己电脑的“任务计划程序”或者schedule库。我自己的做法是放在一台常年开机的家用小主机上每天上午 9 点跑一次脚本然后通过scp把生成好的图片传到博客服务器。定时任务的健康监控也要考虑。我建议在脚本开头和结尾打上日志输出当次生成的图片路径和接口响应耗时如果连续三天接口解析失败就触发一个提醒。这个提醒可以简单到发一封邮件或者用微信的 Server 酱推送一条消息总之要让你自己能感知到异常而不是等读者发现页面挂了。4. 用免费 API 时最常见的坑以及我的排查思路4.1 400 错误是参数结构有问题别再检查网址了接触公众 API 多的人应该都有这种经验报错信息里提示400 Bad Request下意识第一反应是网址写错了于是在地址栏里反复检查 URL。其实 400 的含义是请求格式不对要么是某个参数名拼错要么是缺少必填参数要么是 JSON 请求体结构和接口要求的不一致。前几年有些 AI 服务的接口就会报invalid schema这类的 400 错误原因是开发工具传参时漏了一层嵌套或者字段名大小写不匹配。排查这种问题我的方法很简单一步一步做“请求解析对照”。先打开接口文档看它要求的是 GET 还是 POST如果是 POST把请求体的 JSON 结构原封不动地抄下来和代码里发的对照一遍。很多时候问题出在数组和对象的嵌套层级上少一个[]或者多一个引号都会导致 schema 校验失败。调试期间我会先在命令行用curl发一次请求确认接口本身没问题再把同样的参数照搬进代码里这样能快速把问题定位在“参数编辑”还是“代码逻辑”上。4.2 401 和 403 的区别以及 Key 的放置位置401 和 403 初学者经常混淆。401 是“未认证”意思是服务端根本不认识你是谁通常发生在 API Key 缺失、Key 写错了、或者 Key 过期时。403 是“已认证但没有权限”意思是服务端认识你了但你被拒绝了。出现 403常见原因包括免费额度耗尽、IP 不在白名单、或者账号功能被停用。排查认证问题时我第一件事是打开请求的实际发送报文确认 Key 是不是放在了正确的位置。有的 API 要求放在请求头里比如Authorization: Bearer your-key有的则要求放在 query 参数里比如https://api.example.com/data?api_keyyour-key。前者如果被误放进 URL 参数里服务端自然解析不出来就会返回 401。另外确认一下代码里有没有把 Key 写死在小写变量里有些服务端的 Key 是区分大小写的复制粘贴时多了一个空格也会导致认证失败。4.3 429 限流要退避而不是死磕免费 API 的限流机制是最让人头疼的但也是可以应对的。当服务端返回 429说明你在单位时间内的请求次数超过了阈值。很多人遇到 429 就立刻加大请求频率心想“再试一次也许就通过了”结果越试越被限制。正确的做法是停止请求等待一段时间再重试且等待时间逐次递增这叫指数退避。我之前写过一个负责抓汇率数据的脚本对方限制每分钟最多 30 次请求我第一次跑全量更新时一口气发了 200 个请求结果没多久就全被限流了。后来我加了简单的退避逻辑每次遇到 429 就等待2^n秒再重试连续重试 5 次还不行就直接跳过记录把失败的写入日志等人工处理。import time import requests def request_with_retry(url, max_retries5): for attempt in range(max_retries): resp requests.get(url, timeout10) if resp.status_code 429: wait_time 2 ** (attempt 1) time.sleep(wait_time) continue resp.raise_for_status() return resp.json() raise RuntimeError(API rate limit exceeded)这类退避逻辑几乎可以通用于所有免费 API。需要注意的是有些服务商在响应头里会带上Retry-After字段表示你需要等待的具体秒数优先以它为准。如果响应头里有这个值就不要再用自己算出来的退避时间了。4.4 500 错误往往不是你的锅但要有兜底方案500 系列错误表示服务端内部出现问题。遇到这类错误代码层面几乎做不了什么但你要有一个正确的动作要不要重试。像502 Bad Gateway、503 Service Unavailable这类错误通常是服务端正在发布或者过载过几秒再访问大概率能恢复。我的经验是设置最多 3 次重试间隔 1 秒、3 秒、10 秒这样递增三次都失败就放弃记录下来等会儿再试。还有一种比较特殊的情况自建推理服务或者代理服务时返回 500 可能是你的本地进程崩了。我之前跑过一些本地模型服务遇到外部请求稍多的时候进程会直接终止后续所有请求都报 500。这种情况重试是没有用的必须先去查看服务日志恢复进程之后再继续。所以在你能控制服务端的情况下500 错误的排查重点应该在服务端日志而不是客户端代码。4.5 前端调 API 被 CORS 拦截怎么绕我在 2.3 节说过 CORS 字段的重要性这里补充一个实战绕过的方案。如果你看中的 API 不支持 CORS但你前端又确实需要直接调用最常见的方案就是自己搭一个轻量代理。代理的逻辑很直白前端先把请求发给自己的后端由后端转发给目标 API再把响应数据返回给前端因为后端之间没有浏览器的同源策略限制。用 Node.js 的 Express 可以几行代码实现用 Nginx 的反向代理功能也能做到。不过要注意增加代理层意味着你的服务器要承担额外的流量转发压力如果目标 API 本身响应很慢代理的并发连接数可能会积压。建议对代理层做一个缓存相同的请求在短时间内直接返回缓存结果减少走动目标 API 的次数。4.6 免费 API 随时会跑路做好这三点才敢上生产免费 API 的最大风险就是不稳定、不持久。我自己经历过好几个用了很久的接口突然宣布停止服务最夸张的一个文档还在接口已经静默挂掉了没有任何通知。所以在任何生产环境中接入免费 API我都会提前做好三个准备。第一是加超时和重试机制不能因为一个接口没响应就让整个业务流程卡住。第二是做缓存和离线数据,把接口返回的数据尽量落到自己的数据库即使接口挂掉业务也能用缓存数据继续运转。第三是准备备选方案在 public-apis 的对应分类里多找一两个功能相似的接口配置成故障转移主 API 失效时自动切到备用的那个。5. 别只是收藏这份清单还能这样用起来5.1 用 GitHub Actions 做每日 API 健康检查收藏 AP I只需要点一个 Star但真正用好它需要主动去验证和维护。我自己的做法是建了一个私有仓库把比较依赖的几十个 API 地址和配置写在urls.txt里然后用 GitHub Actions 定时跑一个健康检查脚本。这个脚本会逐个请求这些 URL检查 HTTP 状态码是否在 2xx 范围内再把失败的结果汇总发到我的邮箱。健康检查脚本本身不复杂但要注意一个细节不同 API 的响应时间差别很大有的 100 毫秒就返回了有的可能要 3 秒。脚本里的超时设置要留足余量不然会出现误报。同时要注意请求频率尽量把检查任务控制在一天一到两次避免因为频繁请求触发对方的限流。5.2 做一个私有版 API 导航把筛选结果固化成工具原始清单面向所有人但每个开发者的常用 API 其实非常固定。我会定期把 public-apis 里感兴趣的接口筛选出来按照自己的标签体系整理成一份简化版导航只保留接口名称、分类、简单描述、鉴权方式、文档链接。偶尔新项目需要某个方向的 API 时直接查这份导航就能快速定位不用再翻遍原始仓库。如果你愿意花点时间可以把这个导航做成一个有搜索框的静态页面用 GitHub Pages 托管数据放在一个 JSON 文件里。这个页面不需要后端只需要前端读取 JSON 再渲染几分钟就能完成。做出来的效果比直接翻 README 舒服很多下次找 API 时效率可以提升好几倍。5.3 给公众 API 提 PR也是沉淀自己项目的好方法public-apis 是社区项目任何人都可以给它提 PR 贡献新接口。如果你在开发过程中发现某个质量不错的免费 API 不在列表里完全可以按 CONTRIBUTING.md 的要求提交一个 PR把接口添加进去。贡献前有一个关键步骤先检查一下接口是否能正常访问、返回数据是否结构化、有没有明确的文档地址。项目维护者对鸡肋接口的容忍度很低乱提 PR 反而会给维护者添麻烦。给开源项目提 PR 也是一个展示自己能力的方式。我认识一些同行因为持续给这类社区提交高质量的贡献记录在求职时被面试官额外关注。倒不是说这些 PR 技术含量有多高但它们能说明你做事认真、懂得社区协作流程这对工程师来说是很加分的行为习惯。说实话public-apis 的价值不在于它本身的代码有多复杂而在于它替整个开发者社区把“找 API”这件琐事做成了标准品。每次我拿起这份清单都能在里面发现一两个之前不知道的宝藏接口这种效率提升是搜索引擎替代不了的。如果你和我一样常年靠各种公共数据源支撑个人项目和原型验证不妨把它当成一个工具箱的入口先筛出适合自己的两三个 API再基于它们搭起自己的小工具。用起来、跑起来、踩几次坑你才会真正理解这份 474k Star 的清单为什么值得长期保留。