LinkHarbor API 每分钟允许多少次请求、单个请求可以有多大、如何读取响应头,以及如何正确处理 429。
为了让 API 对所有人都保持快速,LinkHarbor 会限制每分钟的请求数。这些限制与套餐无关,也没有每月的 API 调用配额。本文列出所有限制,并说明你的代码如何妥善处理它们。
请求如何计数
- 每个请求都恰好归入一个桶(bucket),例如已认证的请求归入 user,换取令牌归入 token
- 按整分钟(UTC)计数;每到新的一分钟,计数器从零重新开始
- 根据桶的不同,API 按工作区、按人、按连接或按 IP 地址计数
- 在令牌、权限范围或结构校验阶段就失败的请求(401、403 insufficient_scope、400、结构校验产生的 422 validation_failed)不计入;之后才出现的错误(例如 403 feature_not_available)会计入
所有限制
| 桶(X-RateLimit-Bucket) | 限制 | 计数单位 | 适用于 |
|---|---|---|---|
| user | 每分钟 600 次 | API 密钥的令牌按工作区计(工作区的所有密钥和 AI 工具合计);控制台登录按人计 | 所有没有单独桶的、对 /v1 的已认证请求 |
| token | 每分钟 30 次 | IP 地址 | POST /v1/token |
| public | 每分钟 120 次 | IP 地址 | 无需身份验证的 API 请求,例如 GET /v1/plans 和 GET /v1/pages/available |
| callback | 每分钟 120 次 | IP 地址 | POST /v1/callbacks/sale |
| internal | 每分钟 120 次 | IP 地址 | “Custom (API)”集成的接入 URL(POST /v1/ingest/{id}) |
| mcp | 每分钟 120 次 | AI 连接(访问令牌) | 位于 /mcp 的 MCP 服务器 |
| oauth | 每分钟 300 次 | IP 地址 | AI 助手的 OAuth 登录和发现 |
| auth | 每分钟 30 次 | IP 地址 | 登录、注册、重置密码(控制台和应用) |
| refresh | 每分钟 120 次 | IP 地址 | 延长控制台会话 |
| discover | 每分钟 60 次 | 按人计,未登录时按 IP 地址 | 应用中的“发现” |
| subs_read | 每分钟 120 次 | 按人计,未登录时按 IP 地址 | 读取你关注的页面(应用) |
| subs_write | 每分钟 20 次 | 按人计,未登录时按 IP 地址 | 更改你关注的页面(应用) |
每个工作区共用一份额度
同一工作区中所有 API 密钥的令牌共享每分钟 600 次请求,无论你创建了多少个密钥。已连接 AI 助手的工具也计入其中,因为它们在内部调用 API。所以增加密钥并不会提高上限。如果大量负载分散在多个程序中,请按这份共享额度来规划。
响应头
- X-RateLimit-Limit
- 该桶每分钟的上限,例如 600。
- X-RateLimit-Remaining
- 当前这一分钟还剩多少次请求。
- X-RateLimit-Bucket
- 请求归入的桶,例如 user。
- Retry-After
- 仅在 429 rate_limited 时出现:距下一分钟开始还有多少秒。出现 api_key_locked 时,等待时间在 details.retry_after_sec 中。
达到上限时
达到上限后,API 会返回 HTTP 429 和以下响应体。message 的文本使用账户语言(参见 API 错误格式和错误代码),此处为英语。请以错误代码 rate_limited 为准:
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Bucket: user
{ "error": "rate_limited", "message": "Too many requests. Please try again in a moment.", "retry_after_sec": 37 } - 读取 Retry-After 或 retry_after_sec
- 至少等待这么多秒,再加上一点随机时间,避免所有进程在同一时刻重新开始
- 最多重试几次
- 如果一直收到 429,请把工作分散到更长的时间里
const BASE = 'https://dock.at-harbor.com';
let cached = null; // { token, expiresAt }
async function getToken() {
if (cached && Date.now() < cached.expiresAt - 60_000) return cached.token;
const res = await fetch(BASE + '/v1/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
key_id: process.env.LH_KEY_ID,
key_secret: process.env.LH_KEY_SECRET,
}),
});
if (!res.ok) throw new Error('Token exchange failed: HTTP ' + res.status);
const data = await res.json();
cached = { token: data.token, expiresAt: Date.now() + data.expires_in * 1000 };
return cached.token;
}
async function lh(method, path, body, attempt = 0) {
const headers = { Authorization: 'Bearer ' + (await getToken()) };
if (body !== undefined) headers['Content-Type'] = 'application/json';
const res = await fetch(BASE + path, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
});
if (res.status === 429 && attempt < 3) {
const wait = Number(res.headers.get('Retry-After') ?? 5);
await new Promise((r) => setTimeout(r, (wait + Math.random() * 2) * 1000));
return lh(method, path, body, attempt + 1);
}
if (res.status === 401 && attempt === 0) {
cached = null; // 令牌已过期,用新令牌再试一次
return lh(method, path, body, attempt + 1);
}
const data = await res.json().catch(() => null);
if (!res.ok) throw new Error((data && data.error) + ' (HTTP ' + res.status + ')');
return data;
}
// 示例:const { items } = await lh('GET', '/v1/pages'); 处理大量数据的建议
- 缓存令牌,只在它 15 分钟后过期前不久再续期
- 留意 X-RateLimit-Remaining,余量变少时放慢速度
- 逐个发送请求,而不是同时开几十个并行连接
- 一次性加载列表并在本地筛选,而不是逐个查询每个页面
- 把大型同步分散到几分钟内完成
- 5xx 错误以逐渐延长的间隔重试,4xx 错误则不要重试
其他限制
| 项目 | 限制 | 响应 |
|---|---|---|
| 登录失败 | 同一邮箱地址和 IP 失败 5、10 和 20 次后分别锁定 60 秒、5 分钟、30 分钟 | 429 account_locked |
| 表单提交 | 每个 IP 地址每小时 10 次,所有表单合计 | 429 form_rate_limited |
换取令牌时的失败尝试会暂时锁定密钥和 IP,参见 API 密钥和访问令牌。推送通知只能在控制台中发送,它们的限制请参阅关注者和推送通知。
套餐的内容限额(页面、链接、模块、短链接等)不是速率限制,而是固定上限。它们列在套餐对比中。
请求大小
每个请求体在 API 读取之前都会先测量大小。允许的大小取决于端点:
| 端点 | 最大请求体 |
|---|---|
| 登录、注册以及 /v1/auth 下其他无需身份验证的调用,POST /v1/token,POST /v1/oauth/exchange,/oauth 下供 AI 助手使用的 OAuth 端点及其授权,POST /v1/ingest/{id},“支持”模块的结账,应用的订阅相关调用(关注、收藏夹、推送设备),切换工作区,POST /v1/auth/logout-all,删除账户,设置双重验证,接受邀请,启用推荐计划,以及连接集成时的返回步骤 | 64 KB |
| 所有其他 JSON 端点,包括在页面上提交表单 | 1 MB |
| POST /v1/webhooks/{id}/verify-signature 和位于 /mcp 的 MCP 服务器 | 2 MB |
| 文件上传(POST /v1/assets 和 POST /v1/pages/{id}/uploads,multipart/form-data) | 整个请求 11 MB,文件本身最多 10 MB |
如果请求体更大,API 会返回 413 request_too_large;details.max_bytes 和 details.max_kb 给出上限。上传的文件过大会导致 413 payload_too_large。没有请求体的端点(例如 DELETE 调用)不接受请求体,或只接受 {}。
HTTP/1.1 413 Payload Too Large
{ "error": "request_too_large", "message": "…", "details": { "max_bytes": 65536, "max_kb": 64 }, "request_id": "…" } 请求体内部也有限制:每段文本都有最大长度,每个列表都有最大条目数,未知字段会被拒绝。具体数值在 API 参考中按端点列出。违反时会返回 422 validation_failed,details 会指出字段、规则和上限。请求的路径和查询字符串加起来最多 16 KB。
你可能还想了解
没找到答案?
告诉我们你需要什么,我们的客服团队会亲自帮你。