管理 API 入门
Node4 的管理 API 把控制台里手动做的事交给脚本:列出代理、查本月用量、读订单和账单、管理白名单和客户登录(sub-users)、购买套餐和流量包。它和代理是两套独立的认证:代理认账号密码或白名单 IP,API 只认以 n4k_ 开头的 API 密钥,两者不能互换。本文按顺序走一遍:创建仅授予必要权限的 API 密钥,发第一个请求,读懂响应格式,先做只读调用,了解速率限制,最后是动手写操作之前要知道的事。API 地址以 https://node4.io/api/v1 开头。
准备工作
- 一个 Node4 账号。任何账号都能创建 API 密钥,包括免费套餐,本文的调用在所有套餐上都可用。
- 一个能跑脚本的环境:有 curl 的终端、Node.js 18 或更新版本(内置 fetch),或者 Python 3 加 requests。
- API 只给服务端和脚本用,不给网页用:它不返回 CORS 头,浏览器会拒绝调用,而嵌在前端代码里的密钥任何人都能看到。
操作步骤
创建一个只带所需权限的密钥
在“账号设置”页面的“API 与 Webhook”标签页点击“创建密钥”。弹窗要填三项:密钥名称,按用途起名(例如 inventory-sync),以后在列表里好认;有效期,永久、30、90、180 天或 1 年,一次性试验用的密钥应该设到期;权限(scope),每个 API 只需要一个权限,入门先勾 read:profile、read:proxies、read:usage,其余不勾。
完整密钥以 n4k_ 开头,只在创建成功的弹窗里显示一次,Node4 只保存它的哈希,之后无法再查看。立刻复制到密钥管理工具或环境变量里;丢了就创建新的并删除旧的。权限在创建时固定,不能给已有密钥追加权限,需要新权限就创建新密钥。
两个权限勾选前多想一下:read:proxies 会返回可用的代理用户名和密码,持有它的密钥和代理本身一样敏感;write:orders 会花钱。每个集成用自己的密钥,这样删除或限流一个密钥只影响一个任务。
发第一个请求
GET /profile(权限 read:profile)是验证密钥开销最小的方式,它只读账号记录。把密钥放进环境变量 NODE4_API_KEY,在 Authorization 头里以 Bearer 加一个空格加完整密钥发送。客户端不方便自定义 Authorization 头时,也接受 X-API-Key 头,两者只发一个。
成功返回 200 和账号信息。两种常见的首次失败:401 并提示 Missing or invalid API key,是头缺失、拼错,或值不以 n4k_ 开头(多了引号、写成 Bearer Bearer 都会这样);403 并点名一个权限,例如 This endpoint requires the 'read:profile' permission,是密钥有效但没有这个权限,创建一个带该权限的新密钥。
字段 填写内容 curl curl -s -H "Authorization: Bearer $NODE4_API_KEY" https://node4.io/api/v1/profile Node.js const res = await fetch("https://node4.io/api/v1/profile", { headers: { Authorization: `Bearer ${process.env.NODE4_API_KEY}` }, signal: AbortSignal.timeout(30_000) }); console.log(res.status, await res.json()); Python import os, requests; res = requests.get("https://node4.io/api/v1/profile", headers={"Authorization": f"Bearer {os.environ['NODE4_API_KEY']}"}, timeout=30); print(res.status_code, res.json()) 成功判断 状态码 200,响应体 success 为 true,data 里是您的账号邮箱 读懂响应格式
返回 JSON 的 API 用同一种形状回答。成功:200(购买类 API 是 201),响应体 { "success": true, "data": { ... } }。失败:4xx 或 5xx,响应体 { "error": "给人看的原因" };购买类 API 的失败另带一个稳定的机器可读字段 code,例如 card_declined,有的还带 orderId 等额外字段。
不是每个 API 都返回 JSON。导出类 API 按内容类型回答:GET /proxies/list?format=txt 返回 text/plain 的纯文本,每行一条代理,format=csv 返回 CSV;把它们当 JSON 解析会直接报错。这些 API 的参数错误仍然返回 JSON 错误体,所以失败分支是一致的,只有成功分支要按内容类型区分。
程序先按 HTTP 状态码分支,再按 code 分支。error 的文字是写给人看的,随时可能改措辞,不要用它做匹配。失败响应里永远没有 success 字段。
把这套逻辑包成一个函数:拼接地址、加 Authorization 头、设超时、非 2xx 时解析 JSON 并抛出带状态码和 code 的异常;成功时看响应头 Content-Type,application/json 解析后返回 data,text/plain 和 text/csv 原样返回文本。之后的代码只处理数据。
先做只读调用:账号下的代理和本月用量
GET /proxies(权限 read:proxies)返回账号下所有代理和它们的代理地址、端口和账号密码、状态。列表包含已到期和已冻结的代理,所以按 status 过滤出 active 的再用。用账号密码登录的固定 IP 代理会带上账号密码;账号下没有生效中的代理账号密码时,username 和 password 字段为空。只想要可以直接粘贴的行,GET /proxies/list?format=txt 按 host:port:username:password 每行一条返回生效中的代理,响应是纯文本而不是 JSON 封装,按文本读取(见第 3 步)。动态和住宅代理走网关地址而不是 IP 列表,它们的代理地址、端口和账号密码在网关相关的 API 里。
GET /usage(权限 read:usage)返回本自然月和账号累计的流量,含上传、下载、总字节数和请求数。要和套餐额度对比,再调 GET /subscription(权限 read:subscription),它的 bandwidthLimitBytes 在不限流量的套餐上为 null。用量是全账号、按自然月统计的,套餐额度是按该套餐自己的计费周期算的,两者只在“一个按流量计费的套餐、周期从 1 日开始”时对得上;账号下有多个套餐时,以控制台“概览”页的额度为准。要看历史而不是累计,GET /usage/bandwidth?days=30 返回逐小时的快照。
这三个调用都不改变任何东西,适合作为第一批接入的内容。
速率限制和它的响应头
每个密钥每 60 秒最多 60 次请求,所有套餐相同,各密钥分别计数。每个带认证的响应都告诉您余量:RateLimit-Limit、RateLimit-Remaining、RateLimit-Reset(距窗口重置的秒数)、RateLimit-Policy,以及同义的 X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset(重置时刻的 Unix 时间)。两组的 Reset 含义不同,选一组一直读。
窗口用完后返回 429 Too Many Requests,带 Retry-After 头(秒数),响应体里有 retryAfterSeconds、limit 和 windowSeconds。被限流的请求在任何处理之前就被拦下,什么都没发生,等够 Retry-After 的秒数后原样重发是安全的,写操作也一样。
不是所有 429 都是每分钟限制。POST /proxies/{id}/replace 在当月更换次数用完时也返回 429,响应体带 replacementLimit 和 resetsAt,它的 Retry-After 可能是好几天;自动重试前先检查有没有 resetsAt,否则任务会睡到下个月。另外,同一个 IP 发出的所有 /api 请求还共用一个合计上限,不论用哪个密钥;它的 429 提示 Too many requests from this IP,带 Retry-After,但没有 limit 和 windowSeconds 字段。
少被限流的做法:每个集成一个密钥;RateLimit-Remaining 变低时主动放慢,不要等 429;有事件的地方用 Webhook 代替轮询;套餐列表、账号下的代理、地区列表这类很少变化的内容缓存起来。
字段 填写内容 每密钥上限 每 60 秒 60 次,所有套餐相同 每个响应 RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset(秒)/ RateLimit-Policy,以及 X-RateLimit-*(Reset 为 Unix 时间) 超限时 429,Retry-After 头 + retryAfterSeconds、limit、windowSeconds 字段;等够秒数后原样重发 注意 有 resetsAt 的 429 是月度更换次数用完,不要自动重试;提示 from this IP 的 429 是按来源 IP 的合计上限 写操作之前
只读 API 和两个购买 API(POST /orders、POST /topups)重复发送结果相同,其他写操作每发一次就执行一次,而超时并不能告诉您第一次是否已经执行。所以:任何 GET 超时或 5xx 后可以退避重试;购买 API 必须带 Idempotency-Key 头,重试时用同一个 key 和同一个请求体,24 小时内相同 key 会原样返回已有结果并带 Idempotent-Replayed: true 头,这也是为什么 card_declined 之类的拒绝在同一个 key 下会一直重现,修好原因后要换新 key;更换 IP(POST /proxies/{id}/replace)、创建客户登录(sub-users)、创建 Webhook 不要盲目重试,先 GET 看当前状态。更换 IP 尤其要小心:它退掉旧地址、发新的账号密码,重复一次就再换一次并多消耗一次月度额度。
购买类 API 的 code 分三类处理:idempotency_in_flight、checkout_in_progress、rate_limited、payment_provider_unavailable、unavailable 等待后用同一个 key 重发;outcome_unknown、payment_unconfirmed、duplicate_recent_purchase、payment_held 停下来,先查 GET /orders,不要换 key 重发;card_declined、no_saved_card、plan_unavailable 等没有扣款,修正后用新 key。购买 API 返回 500 时不要自动重试,它可能表示已扣款但开通失败,先查订单。
401 和 403 要分清:401 是没识别出密钥(头缺失、Bearer 后没空格、密钥被截断、值里有引号);403 是密钥被识别但不允许:已删除或到期(创建新的)、账号不活跃(联系客服)、缺权限(创建带权限的新密钥)、套餐不含该功能。查别人账号的资源返回 404,和不存在的资源一样,API 不会确认别人的 id 是否存在;自己的 id 返回 404,先用 GET /profile 确认密钥属于哪个账号。
API 密钥和代理密码是两回事
API 密钥和代理账号密码用途不同,不能互换,却经常被放错位置。以 n4k_ 开头的 API 密钥只用于 https://node4.io/api/v1 下的管理 API,代理不会查它:把它当代理密码用会得到 407。代理账号密码只用于代理,放进 API 的 Authorization 头会得到 401。白名单 IP 只放行代理流量,对 API 没有作用。
API 能做的:读代理、网关、客户登录(sub-users)、实时连接,刷新或更换固定 IP(read:proxies、write:proxies);创建和管理代理登录(write:credentials,它们都是能用的代理登录,所以不被 write:proxies 包含);读用量(read:usage);读套餐、流量自动充值和提醒设置、订单(read:subscription、read:orders);买套餐、流量包和 IP 名额(write:orders);读账单和账户余额(read:billing);改流量自动充值和提醒设置(write:subscription);读账号资料和白名单(read:profile);增删白名单(write:ip-whitelist);读通知(read:notifications);管理 Webhook(write:webhooks)。
API 不能做的:更换或取消套餐、添加支付方式、管理团队成员,这些仍在控制台里。
验证接入
按顺序跑三次:GET /profile 返回 200 和您的邮箱,密钥有效;GET /proxies 返回的列表里 active 的代理数和控制台“我的代理”页面一致,权限正确;GET /usage 的 totalFormatted 和控制台“概览”页的本月用量接近(统计口径见第 4 步),数据读对了。
再故意触发一次错误看封装函数的表现:用一个没有 read:usage 权限的密钥调 GET /usage,应得到 403 和点名权限的 error;把头改成 Bearer 后面不加空格,应得到 401。两种都应该变成您程序里带状态码的异常,而不是被当成成功解析。再调一次 GET /proxies/list?format=txt,封装函数应按文本返回而不是报 JSON 解析错误。
同一个请求 curl 能通、代码不通,对比两边实际发出的 Authorization 头;403 点名了一个您确信勾过的权限,可能用的是旧密钥,“API 与 Webhook”标签页的密钥列表里能看到每个密钥的权限。
常见问题
我把 API 密钥填进了代理的密码框,为什么 407?
代理不认 API 密钥。代理用的是控制台“我的代理”页面上的账号密码,或 IP 白名单;API 密钥只用在管理 API 的 Authorization 头里。反过来也一样,代理账号密码放进 API 会得到 401。
每个调用都 401?
打印出您实际发送的头。值必须是 Bearer 加一个空格加完整的 n4k_ 密钥,没有引号。或者改用 X-API-Key 头,只发其中一个。
403 并提示缺少权限,但我记得勾了?
权限在创建时固定,不能追加。创建一个带该权限的新密钥;也可能代码里用的是另一把旧密钥,到“API 与 Webhook”标签页核对每个密钥的权限。
返回 429,该等多久?
按 Retry-After 头的秒数等,然后原样重发,写操作也安全。每个密钥每 60 秒 60 次;每几秒轮询一次的脚本会撞上它,每几分钟查一次的不会。响应体带 resetsAt 的 429 是更换 IP 的月度额度用完,不要自动等。
购买 API 超时了,要不要再发一次?
要,但必须用同一个 Idempotency-Key 和同一个请求体:它要么返回已存储的结果,要么第一次真正执行。换一个新 key 重发可能造成重复购买。收到 outcome_unknown、payment_unconfirmed 或 duplicate_recent_purchase 时停下来,先查 GET /orders。
有 OpenAPI 文档或客户端库吗?
机器可读的 OpenAPI 文档在 https://node4.io/openapi.json,可以导入 Postman、Insomnia 或代码生成器。完整的 API、字段和权限列表在英文帮助中心的 API 参考里;Node.js 和 Python 客户端库以及 Postman 集合目前不公开发布,可提交工单索取。