API.PLUS 接入教程:Python、PHP、JavaScript 调用实战
先注册获取 apikey,用带超时的 curl 确认调用链路可用;再分别用 Python 的 Session 配合元组超时与 GET 退避重试、PHP 的 cURL 封装函数、JavaScript 的 AbortController 超时来调用,Key 只留在后端,即可做到生产可用。
- Key 属于账户凭证,禁止写入前端 bundle 或公开仓库
- 上线前先用一条带 --max-time 的 curl 命令验证 Key 与网络
- 响应按 HTTP 状态码、业务 code、data 三层分层处理
- Python 用 Session 复用连接,timeout 传元组区分连接与读取超时
- 浏览器 fetch 没有 timeout 选项,必须用 AbortController 中断请求
引言:一次 API 调用要写成可维护的代码#
调用第三方 API 的难点通常不在业务逻辑,而在超时、重试、错误码分层和密钥管理。很多项目在测试环境跑通一次就上线,结果线上遇到 429 或网关抖动时,代码直接把异常抛给用户。本文以 API.PLUS 的公开接口为例,用 Python、PHP、JavaScript 三种语言把「拿到 Key → 发出请求 → 校验响应 → 生产可用」这条链路完整走一遍。
示例选用 GET示例接口(参数少、返回 JSON,适合验证链路是否通)和 随机动漫壁纸接口(返回图片或 JSON 元数据,适合演示内容类型差异)。三份示例代码都可以直接复制到项目里,改掉 Key 就能跑。
一、动手前要确认的三件事:Key、超时、响应约定#
所有请求都要携带 apikey。免费 Key 在 API.PLUS 注册后即可在控制台获取,它属于账户凭证:不要写进前端 bundle、不要提交到公开仓库、不要贴进 issue。浏览器端调用必须经过你自己的后端代理,由后端注入 Key 并做频率限制,否则 Key 等于公开。
写代码之前,先用一条 curl 命令确认 Key 与网络都正常,这是排查问题最快的一步:
示例说明:以下代码仅用于逻辑验证,生产环境需补充异常处理、超时与重试、日志与监控告警及降级策略。
curl -sS --max-time 5 'https://www.api.plus/api/get?apikey=YOUR_KEY&ip=8.8.8.8'
注意 --max-time 这个参数。客户端不设超时是线上事故最常见的来源之一。如果这条命令能返回 JSON,说明 Key、路径、网络都没有问题,剩下的就只是代码问题。
响应要分三层判断
JSON 接口通常是「HTTP 状态码 + 业务 code + data」的三段式结构,三层含义不同,处理方式也不同:
| 层级 | 判断依据 | 处理方式 |
|---|---|---|
| HTTP 状态码 | 200 / 4xx / 5xx | 4xx 不重试(429 除外),5xx 可退避重试 |
| 业务 code | 响应体里的 code 字段 | 非成功码记录 msg 后放弃重试 |
| data | 实际业务字段 | 先做类型与空值校验,再落库或渲染 |
二、Python:Session 复用、元组超时与退避重试#
requests 是最常见的选择。要点有三个:用 Session 复用连接池,用元组分别设置连接超时与读取超时,用 Retry 只对幂等请求和可重试状态码生效。
import time
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
API_KEY = 'YOUR_KEY'
ENDPOINT = 'https://www.api.plus/api/get'
session = requests.Session()
session.headers.update({'User-Agent': 'api-plus-demo/1.0'})
retry = Retry(
total=3,
backoff_factor=0.5,
status_forcelist=(429, 500, 502, 503, 504),
allowed_methods=frozenset(['GET']),
)
session.mount('https://', HTTPAdapter(max_retries=retry, pool_maxsize=20))
def query_ip(ip):
params = {'apikey': API_KEY, 'ip': ip}
try:
resp = session.get(ENDPOINT, params=params, timeout=(3.05, 8))
except requests.RequestException as exc:
raise RuntimeError('请求失败: {}'.format(exc)) from exc
if resp.status_code != 200:
raise RuntimeError('HTTP {}'.format(resp.status_code))
payload = resp.json()
if payload.get('code') not in (200, 0):
raise RuntimeError('业务错误: {}'.format(payload.get('msg', 'unknown')))
return payload.get('data', {})
if __name__ == '__main__':
start = time.time()
print(query_ip('8.8.8.8'))
print('cost {:.0f}ms'.format((time.time() - start) * 1000))
几个容易忽略的细节:timeout=(3.05, 8) 的第一个值是连接超时,第二个是读取超时,而传单个数字会同时作用于两者;Retry 里把 allowed_methods 限定为 GET,是因为查询类接口幂等、可以安全重放,而写操作重放可能产生重复数据;业务 code 的判断放在 HTTP 状态码之后,避免把 200 里的错误响应当成成功。
如果需要提交表单或 JSON,POST 场景可以参考 POST 示例接口。另外不要对超时的 POST 直接重试,应该先用幂等键或查询接口确认服务端状态,再决定是否补发。
三、PHP:用 cURL 封装一个可复用的请求函数#
file_get_contents 写起来快,但超时粒度粗、无法精细控制、错误只能靠 warning 捕获,不适合生产环境。更稳妥的做法是用 cURL 封装一个返回数组的函数,把传输层错误和业务层错误分开抛出。
function api_get(string $url, int $timeoutMs = 8000): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT_MS => 2000,
CURLOPT_TIMEOUT_MS => $timeoutMs,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_USERAGENT => 'api-plus-demo/1.0',
]);
$body = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($errno !== 0) {
throw new RuntimeException('传输层错误 ' . $errno . ': ' . $error);
}
if ($status !== 200) {
throw new RuntimeException('HTTP ' . $status);
}
$data = json_decode((string) $body, true);
if (!is_array($data)) {
throw new RuntimeException('响应不是合法 JSON');
}
if (isset($data['code']) && (int) $data['code'] !== 200) {
throw new RuntimeException('业务错误: ' . ($data['msg'] ?? 'unknown'));
}
return $data['data'] ?? [];
}
$key = getenv('API_PLUS_KEY') ?: 'YOUR_KEY';
$url = 'https://www.api.plus/api/get?apikey=' . urlencode($key) . '&ip=8.8.8.8';
print_r(api_get($url));
这段代码有三个刻意的设计:Key 从环境变量读取,避免硬编码进仓库;用 urlencode 处理参数,防止特殊字符破坏查询串;curl_error 在 curl_close 之前取出,否则拿不到错误信息。调用方只需要一层 try/catch,就能区分网络问题和业务问题,日志里也能留下可定位的错误码。
四、JavaScript:浏览器与 Node 的两套写法#
浏览器端:能调什么、不能调什么
浏览器里绝对不能放 apikey,任何人都能在 DevTools 里看到。带 Key 的请求必须走你自己的后端。对于无需鉴权的公开接口,前端可以直接请求,但必须用 AbortController 设置超时,否则弱网下请求会挂很久:
async function fetchDongman(signal) {
const resp = await fetch('https://www.api.plus/api/dongman?type=json', {
method: 'GET',
signal,
headers: { accept: 'application/json' },
});
if (!resp.ok) {
throw new Error('HTTP ' + resp.status);
}
return resp.json();
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 8000);
try {
const data = await fetchDongman(controller.signal);
console.log(data);
} catch (err) {
console.error(err.name === 'AbortError' ? '请求超时' : err.message);
} finally {
clearTimeout(timer);
}
AbortController 是浏览器端唯一可靠的超时手段,fetch 本身没有 timeout 选项。把 signal 传进去,超时后请求会被真正中断,而不只是抛出一个错误。上面的顶层 await 需要写在 ES Module 里,普通脚本请包进 async 函数。
Node 18+:用 undici 控制连接与超时
Node 18 之后有了全局 fetch,但生产环境里连接复用和分阶段超时更依赖 undici。下面这段是查询 IP 归属地的最小实现:
import { request } from 'undici';
const key = process.env.API_PLUS_KEY;
const url = new URL('https://www.api.plus/api/get');
url.searchParams.set('apikey', key);
url.searchParams.set('ip', '8.8.8.8');
const res = await request(url, {
method: 'GET',
headers: { accept: 'application/json' },
headersTimeout: 5000,
bodyTimeout: 8000,
});
const body = await res.body.json();
if (res.statusCode !== 200) {
throw new Error('HTTP ' + res.statusCode);
}
if ((body.code ?? 200) !== 200) {
throw new Error('业务错误: ' + (body.msg ?? 'unknown'));
}
console.log(body.data);
undici 的 headersTimeout 与 bodyTimeout 分别覆盖「等待响应头」和「读取响应体」两个阶段,比单一超时更贴近真实故障。如果项目已经用了 axios 或 got,思路完全一样:设置超时、限制重试次数、校验响应结构。
五、把示例代码变成生产能力#
示例能跑通只是第一步。真正上线之前,建议逐项确认下面五点:
- 超时分层:连接 2~3 秒、读取 5~8 秒,前端再加一层中断兜底,任何一层都不能省。
- 重试边界:只对 GET 和 429/5xx 重试,指数退避加随机抖动,最多 3 次,避免把对方打垮。
- Key 管理:走环境变量或密钥管理服务,按环境分发并定期轮换,发现泄漏立即作废重发。
- 可观测性:记录 traceId、耗时、状态码与业务 code,日志里不要出现完整 Key,出问题时才能定位。
- 缓存:壁纸这类更新不频繁的内容可以加 Cache-Control 与本地缓存,减少重复请求和配额消耗。
常见故障可以按下表对号入座:
| 现象 | 常见原因 | 处理建议 |
|---|---|---|
| 401 / 403 | Key 缺失、拼写错误或被停用 | 确认 Key 放在查询参数里且未被编码破坏 |
| 429 | 超出配额或频率限制 | 降速并退避重试,读取响应头里的限流信息 |
| 请求长时间无响应 | 没有设置超时 | 连接 2~3 秒、读取 5~8 秒,前端用 AbortController 兜底 |
| 返回 HTML 而不是 JSON | 路径写错或命中网关错误页 | 先看 Content-Type 与状态码,不要直接解析 JSON |
想继续深入限流、缓存与响应设计,可以翻 API 文章中心 里的专题;遇到具体接口问题,走 技术支持 通常比在评论区追问更快。
六、总结#
接入 API.PLUS 只需要三步:先用 curl 验证 URL 与 Key,再用 Session、cURL 或 fetch 封装统一的请求函数,最后补上超时、重试与错误分层。Python 侧重连接复用与退避重试,PHP 侧重传输层与业务层错误分离,JavaScript 侧重中断与分阶段超时,三者的共同点是:永远不要假设一次请求一定会成功。
如果你还没有 Key,可以到 API.PLUS 注册账号,在控制台免费获取 API Key,先用 GET示例接口 跑通本文的示例,再替换成业务真正需要的接口,例如 随机动漫壁纸接口。