速率限制
本文档详细介绍 Zanbara API 的速率限制机制,包括限制规则、响应头信息、超限处理以及优化建议。
速率限制概述
Zanbara 实施速率限制以确保:
系统稳定性: 防止单个用户占用过多资源
公平性: 保证所有用户获得公平的服务质量
安全性: 防止 DDoS 攻击和恶意请求
速率限制同时基于 IP 地址和 API Key 进行计算。
限制规则
基础限制
公开端点 - 市场数据
IP
60 请求
N/A
1 分钟
公开端点 - 系统信息
IP
20 请求
N/A
1 分钟
私有端点 - 查询类
IP + Key
120 请求
600 请求
1 分钟
私有端点 - 交易类
IP + Key
30 请求
120 请求
1 分钟
私有端点 - 批量操作
IP + Key
10 请求
30 请求
1 分钟
端点分类详情
公开端点 - 市场数据
限制: 60 请求/分钟 (IP)
包含以下端点:
GET /v1/market/ticker/{symbol}
GET /v1/market/orderbook/{symbol}
GET /v1/market/trades/{symbol}
GET /v1/market/klines/{symbol}
GET /v1/market/funding-rate/{symbol}
GET /v1/market/symbols建议: 使用 WebSocket API 订阅实时数据,避免频繁轮询。
公开端点 - 系统信息
限制: 20 请求/分钟 (IP)
包含以下端点:
GET /v1/system/time
GET /v1/system/status
GET /v1/health私有端点 - 查询类
限制:
IP: 120 请求/分钟
API Key: 600 请求/分钟
包含以下端点:
GET /v1/account/info
GET /v1/account/balance
GET /v1/account/positions
GET /v1/order/list
GET /v1/order/{order_id}
GET /v1/position/list
GET /v1/position/{symbol}
GET /v1/trade/history私有端点 - 交易类
限制:
IP: 30 请求/分钟
API Key: 120 请求/分钟
包含以下端点:
POST /v1/order/place
POST /v1/order/cancel
POST /v1/order/amend
POST /v1/position/close
POST /v1/position/set-leverage
POST /v1/position/set-margin-mode
POST /v1/position/add-margin私有端点 - 批量操作
限制:
IP: 10 请求/分钟
API Key: 30 请求/分钟
包含以下端点:
POST /v1/order/cancel-all
POST /v1/order/cancel-batch
POST /v1/order/place-batch
POST /v1/position/close-all提示: 批量操作虽然限制更严格,但单次请求可处理多个对象,总体效率更高。
VIP 用户限制
VIP 用户享有更高的速率限制:
VIP 1
2x
2x
2x
VIP 2
5x
3x
3x
VIP 3
10x
5x
5x
VIP 做市商
20x
10x
10x
示例: VIP 2 用户的查询类限制为 3000 请求/分钟 (600 × 5)。
如需升级 VIP 等级,请联系 [email protected]。
速率限制响应头
每个 API 响应都包含速率限制信息的响应头:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 115
X-RateLimit-Reset: 1696752060
X-RateLimit-Type: api_key响应头详解
X-RateLimit-Limit
当前时间窗口的请求上限。
示例:
X-RateLimit-Limit: 120表示当前端点在 1 分钟内最多允许 120 次请求。
X-RateLimit-Remaining
当前时间窗口剩余可用请求次数。
示例:
X-RateLimit-Remaining: 95表示当前时间窗口内还可以发送 95 次请求。
监控建议:
if (response.headers['x-ratelimit-remaining'] < 10) {
console.warn('接近速率限制,降低请求频率');
await sleep(1000);
}X-RateLimit-Reset
速率限制重置时间 (Unix 时间戳,秒)。
示例:
X-RateLimit-Reset: 1696752060表示速率限制将在 1696752060 (Unix 时间戳) 重置。
计算剩余时间:
const resetTime = parseInt(response.headers['x-ratelimit-reset']);
const now = Math.floor(Date.now() / 1000);
const remainingSeconds = resetTime - now;
console.log(`速率限制将在 ${remainingSeconds} 秒后重置`);X-RateLimit-Type
当前限制类型,可能值:
ip- 基于 IP 地址的限制api_key- 基于 API Key 的限制
示例:
X-RateLimit-Type: api_key说明: 当同时存在 IP 和 API Key 限制时,返回更严格的那个限制类型。
超过限制的处理
当超过速率限制时,API 返回 HTTP 429 Too Many Requests。
响应示例
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded: 120 requests per minute. Please retry after 45 seconds.",
"retry_after": 45,
"limit": 120,
"reset_at": 1696752060
},
"timestamp": 1696752000000
}响应头
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1696752060
Retry-After: 45Retry-After: 建议的重试等待时间 (秒)。
错误字段说明
code: 错误代码RATE_LIMIT_EXCEEDEDmessage: 人类可读的错误描述retry_after: 建议的重试等待时间 (秒)limit: 当前限制值reset_at: 限制重置时间 (Unix 时间戳)
重试策略
基础重试
async function callAPIWithRetry(endpoint, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(endpoint, options);
if (response.status === 429) {
const data = await response.json();
const retryAfter = data.error.retry_after || 60;
console.log(`速率限制,等待 ${retryAfter} 秒后重试...`);
await sleep(retryAfter * 1000);
continue;
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
throw new Error('Max retries exceeded');
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}指数退避重试
async function callAPIWithExponentialBackoff(endpoint, options, maxRetries = 5) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(endpoint, options);
if (response.status === 429) {
// 指数退避: 2^i 秒,加上随机抖动
const backoffTime = Math.pow(2, i) * 1000;
const jitter = Math.random() * 1000;
const waitTime = backoffTime + jitter;
console.log(`重试 ${i + 1}/${maxRetries},等待 ${(waitTime / 1000).toFixed(2)} 秒`);
await sleep(waitTime);
continue;
}
return response;
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
throw new Error('Max retries exceeded');
}智能重试 (推荐)
class RateLimiter {
constructor(requestsPerMinute) {
this.limit = requestsPerMinute;
this.tokens = requestsPerMinute;
this.lastRefill = Date.now();
}
async acquire() {
this.refill();
if (this.tokens > 0) {
this.tokens--;
return;
}
// 等待令牌补充
const waitTime = 60000 / this.limit;
await sleep(waitTime);
return this.acquire();
}
refill() {
const now = Date.now();
const elapsed = now - this.lastRefill;
if (elapsed >= 60000) {
this.tokens = this.limit;
this.lastRefill = now;
}
}
}
// 使用示例
const limiter = new RateLimiter(100); // 100 请求/分钟
async function callAPI(endpoint, options) {
await limiter.acquire();
return fetch(endpoint, options);
}优化建议
1. 使用 WebSocket 代替轮询
不推荐 (浪费速率限制):
// 每秒轮询订单簿
setInterval(async () => {
const orderbook = await fetch('/v1/market/orderbook/SOL-PERP');
}, 1000);推荐 (使用 WebSocket):
const ws = new WebSocket('wss://ws.zanbarax.com');
ws.send(JSON.stringify({
type: 'subscribe',
channel: 'orderbook',
symbol: 'SOL-PERP'
}));
ws.onmessage = (event) => {
const orderbook = JSON.parse(event.data);
updateUI(orderbook);
};2. 批量操作
不推荐 (多次请求):
for (const orderId of orderIds) {
await fetch(`/v1/order/cancel`, {
method: 'POST',
body: JSON.stringify({ order_id: orderId })
});
}推荐 (批量请求):
await fetch('/v1/order/cancel-batch', {
method: 'POST',
body: JSON.stringify({ order_ids: orderIds })
});3. 本地缓存
缓存不经常变化的数据:
class CachedAPIClient {
constructor() {
this.cache = new Map();
}
async getSymbolInfo(symbol) {
// 缓存 1 小时
const cacheKey = `symbol:${symbol}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < 3600000) {
return cached.data;
}
const data = await fetch(`/v1/market/symbols/${symbol}`).then(r => r.json());
this.cache.set(cacheKey, {
data,
timestamp: Date.now()
});
return data;
}
}4. 请求合并
合并短时间内的多个相同请求:
class RequestDeduplicator {
constructor() {
this.pending = new Map();
}
async fetch(url, options) {
const key = `${url}:${JSON.stringify(options)}`;
if (this.pending.has(key)) {
return this.pending.get(key);
}
const promise = fetch(url, options)
.finally(() => this.pending.delete(key));
this.pending.set(key, promise);
return promise;
}
}
// 使用示例
const dedup = new RequestDeduplicator();
// 即使同时发起 10 个相同请求,实际只会发送 1 个
const promises = Array(10).fill().map(() =>
dedup.fetch('/v1/account/balance', { headers: authHeaders })
);
const results = await Promise.all(promises);5. 监控剩余配额
class RateLimitMonitor {
constructor() {
this.stats = {
limit: 0,
remaining: 0,
resetAt: 0
};
}
updateFromHeaders(headers) {
this.stats.limit = parseInt(headers['x-ratelimit-limit'] || 0);
this.stats.remaining = parseInt(headers['x-ratelimit-remaining'] || 0);
this.stats.resetAt = parseInt(headers['x-ratelimit-reset'] || 0);
const usage = ((this.stats.limit - this.stats.remaining) / this.stats.limit * 100).toFixed(2);
console.log(`速率限制使用: ${usage}% (${this.stats.remaining}/${this.stats.limit} 剩余)`);
if (this.stats.remaining < this.stats.limit * 0.1) {
console.warn('⚠️ 速率限制即将耗尽,请降低请求频率');
}
}
async callAPI(url, options) {
const response = await fetch(url, options);
this.updateFromHeaders(response.headers);
return response;
}
}6. 错峰请求
避免在整点发送大量请求:
function getRandomDelay(baseDelay = 0) {
// 添加 0-5 秒的随机延迟
return baseDelay + Math.random() * 5000;
}
async function scheduledTask() {
await sleep(getRandomDelay(0));
await performTask();
}
// 每分钟执行,但时间随机化
setInterval(scheduledTask, 60000);企业级解决方案
分布式速率限制
使用 Redis 实现多实例速率限制:
const Redis = require('ioredis');
const redis = new Redis();
class DistributedRateLimiter {
constructor(key, limit, windowMs) {
this.key = key;
this.limit = limit;
this.windowMs = windowMs;
}
async acquire() {
const now = Date.now();
const windowStart = now - this.windowMs;
// 使用 Redis sorted set 实现滑动窗口
const multi = redis.multi();
multi.zremrangebyscore(this.key, 0, windowStart);
multi.zadd(this.key, now, `${now}-${Math.random()}`);
multi.zcard(this.key);
multi.expire(this.key, Math.ceil(this.windowMs / 1000));
const results = await multi.exec();
const count = results[2][1];
if (count > this.limit) {
throw new Error('Rate limit exceeded');
}
return true;
}
}
// 使用示例
const limiter = new DistributedRateLimiter('api:user:123', 100, 60000);
await limiter.acquire();多 API Key 负载均衡
class APIKeyPool {
constructor(apiKeys) {
this.keys = apiKeys.map(key => ({
key,
remaining: Infinity,
resetAt: 0
}));
this.currentIndex = 0;
}
selectKey() {
// 选择剩余配额最多的 Key
const sorted = [...this.keys].sort((a, b) => b.remaining - a.remaining);
const best = sorted[0];
if (best.remaining === 0 && Date.now() < best.resetAt * 1000) {
throw new Error('All API keys exhausted');
}
return best;
}
updateKey(apiKey, headers) {
const key = this.keys.find(k => k.key === apiKey);
if (key) {
key.remaining = parseInt(headers['x-ratelimit-remaining'] || Infinity);
key.resetAt = parseInt(headers['x-ratelimit-reset'] || 0);
}
}
async callAPI(endpoint, options) {
const { key } = this.selectKey();
const response = await fetch(endpoint, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${key}`
}
});
this.updateKey(key, response.headers);
return response;
}
}
// 使用示例
const pool = new APIKeyPool([API_KEY_1, API_KEY_2, API_KEY_3]);
const response = await pool.callAPI('/v1/account/balance', {});特殊场景处理
做市商高频交易
做市商需要频繁下单/撤单,建议:
申请 VIP 做市商权限: 获得 10-20 倍速率限制
使用批量 API: 单次请求处理多个订单
WebSocket 推送: 接收实时成交、订单簿更新
本地订单簿维护: 减少查询请求
class MarketMaker {
constructor() {
this.localOrderbook = new Map();
this.pendingOrders = new Set();
}
async placeOrders(orders) {
// 批量下单
const response = await fetch('/v1/order/place-batch', {
method: 'POST',
body: JSON.stringify({ orders })
});
const data = await response.json();
data.data.orders.forEach(order => {
this.pendingOrders.add(order.id);
});
return data;
}
async cancelAllOrders() {
// 批量撤单
await fetch('/v1/order/cancel-all', {
method: 'POST'
});
this.pendingOrders.clear();
}
onWebSocketUpdate(update) {
// 更新本地订单簿,无需 HTTP 请求
if (update.type === 'orderbook') {
this.localOrderbook.set(update.symbol, update.data);
}
}
}数据分析与回测
大量历史数据查询时:
使用数据导出 API: 一次性导出大量数据
分页查询: 避免单次请求数据量过大
本地缓存: 已查询的数据缓存到本地
async function fetchAllHistoricalTrades(symbol, startTime, endTime) {
const trades = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(
`/v1/market/trades/${symbol}?start=${startTime}&end=${endTime}&page=${page}&limit=1000`
);
const data = await response.json();
trades.push(...data.data.items);
hasMore = data.data.has_next;
page++;
// 尊重速率限制
await sleep(1000);
}
return trades;
}监控与告警
Prometheus 指标
Zanbara 提供 Prometheus 格式的速率限制指标:
# 请求总数
zanbara_api_requests_total{endpoint="/v1/order/place",status="200"} 1250
# 速率限制触发次数
zanbara_api_rate_limit_exceeded_total{endpoint="/v1/order/place"} 5
# 剩余配额
zanbara_api_rate_limit_remaining{endpoint="/v1/order/place",user="user_123"} 95告警规则
groups:
- name: api_rate_limit
rules:
- alert: RateLimitNearExhaustion
expr: zanbara_api_rate_limit_remaining < 10
for: 1m
annotations:
summary: "API 速率限制即将耗尽"
description: "用户 {{ $labels.user }} 在端点 {{ $labels.endpoint }} 的剩余配额低于 10"
- alert: RateLimitExceededFrequently
expr: rate(zanbara_api_rate_limit_exceeded_total[5m]) > 0.1
for: 5m
annotations:
summary: "频繁触发速率限制"
description: "端点 {{ $labels.endpoint }} 在过去 5 分钟内频繁触发速率限制"常见问题
Q: 为什么我的 VIP 等级没有生效?
A: VIP 等级仅对 API Key 限制生效,IP 限制保持不变。确认您使用的是通过 API Key 认证的请求。
Q: 测试环境有速率限制吗?
A: 测试环境的速率限制相对宽松:
公开端点: 200 请求/分钟
私有端点: 500 请求/分钟
但仍建议合理使用,避免影响其他开发者。
Q: 可以申请提高速率限制吗?
A: 可以。请通过 [email protected] 联系我们,说明您的使用场景和需求。我们将评估后调整您的限制。
Q: 速率限制是全局的还是按端点的?
A: 按端点分类计算。不同类型的端点有不同的限制,互不影响。
相关文档
文档版本: v1.0.0 最后更新: 2025-10-07 维护: Zanbara API Team
Last updated