错误与速率限制
Allswap API 的所有错误都用同一个 JSON 信封 —— 你的错误处理只需要支持一种形状。code 给机器用,message 给人看,requestId 是你开 ticket 时要带给我们的。
错误信封
{
"error": {
"code": "quote_expired",
"message": "Quote qt_01HW9... expired at 2026-06-22T10:01:32Z.",
"requestId":"req_01HW9..."
}
}常见错误码
| 错误码 | HTTP | 含义 | 可以重试? |
|---|---|---|---|
unauthorized | 401 | X-Key-Id / Bearer 缺失或无效。 | 否 |
forbidden | 403 | 密钥有效但缺少必需的 scope 权限。 | 否 |
invalid_request | 400 | 请求体格式错误 / 缺少必填字段。 | 先修 |
unsupported_pair | 400 | from → to 之间没有支持的路由。 | 否 |
amount_too_low | 400 | 低于该交易对的最小可交易金额。 | 先修 |
amount_too_high | 400 | 超过该交易对当前的流动性上限。 | 先修 |
quote_expired | 409 | quoteId 已超过 expiresAt —— 重新报价。 | 先修 |
quote_consumed | 409 | quoteId 已被前一次 /v1/swap 调用消费过。 | 否 |
rate_limited | 429 | RPM 预算已用完。请遵守 Retry-After。 | 退避 |
upstream_timeout | 504 | 路由未在超时内响应,可以安全重试。 | 退避 |
internal_error | 500 | 我们这边出问题了。请上报 requestId。 | 退避 |
速率限制
每个密钥有两个限制:每分钟请求数(RPM,控制突发)和 每月报价次数(档位月配额)。具体档位数字见 定价页。
每条响应都包含当前 RPM 桶的状态:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 248
X-RateLimit-Reset: 1718983261当你打到 0,下一次请求返回 429 rate_limited,带 Retry-After header(秒)。务必遵守。
重试策略
按类别推荐做法:
- 你这边的问题 4xx(
invalid_request、unauthorized、forbidden、unsupported_pair):不要 重试。把错误展示给开发者/用户即可;重试只会浪费配额。 - 429 rate_limited:按
Retry-After给出的秒数等待后重试。用值本身 —— 别自己估,我们在故障期间会动态调整这个值。 - 5xx(
upstream_timeout、internal_error):带抖动的指数退避。建议 base = 500ms、cap = 8s、最多重试 4 次。 - 报价相关 409:先重新报价,再重试 swap。
quote_consumed是终态 —— 这个 quoteId 永远废了。
参考:退避伪代码
async function withRetry<T>(fn: () => Promise<T>): Promise<T> {
let attempt = 0;
while (true) {
try {
return await fn();
} catch (err: any) {
const code = err?.body?.error?.code;
const status = err?.status;
if (status === 429) {
const wait = Number(err.headers["retry-after"] ?? 1) * 1000;
await sleep(wait);
continue;
}
const retriable = code === "upstream_timeout" || code === "internal_error";
if (!retriable || attempt >= 4) throw err;
const base = Math.min(8000, 500 * 2 ** attempt);
const jitter = Math.random() * base * 0.3;
await sleep(base + jitter);
attempt++;
}
}
}
