export enum EndpointCategory {
GAMMA_API = "gamma_api",
CLOB_MARKET_DATA = "clob_market_data",
CLOB_GENERAL = "clob_general",
DATA_API = "data_api",
BRIDGE_API = "bridge_api",
}
interface BucketConfig {
maxTokens: number;
refillRate: number; // tokens per second
}
const RATE_LIMITS: Record<EndpointCategory, BucketConfig> = {
[EndpointCategory.GAMMA_API]: { maxTokens: 750, refillRate: 75 },
[EndpointCategory.CLOB_MARKET_DATA]: { maxTokens: 200, refillRate: 20 },
[EndpointCategory.CLOB_GENERAL]: { maxTokens: 5000, refillRate: 500 },
[EndpointCategory.DATA_API]: { maxTokens: 300, refillRate: 30 },
[EndpointCategory.BRIDGE_API]: { maxTokens: 100, refillRate: 10 },
};
class TokenBucket {
private tokens: number;
private lastRefill: number;
private readonly maxTokens: number;
private readonly refillRate: number;
constructor(config: BucketConfig) {
this.maxTokens = config.maxTokens;
this.refillRate = config.refillRate;
this.tokens = config.maxTokens;
this.lastRefill = Date.now();
}
private refill() {
const now = Date.now();
const elapsed = (now - this.lastRefill) / 1000;
this.tokens = Math.min(this.maxTokens, this.tokens + elapsed * this.refillRate);
this.lastRefill = now;
}
async acquire(count = 1): Promise<void> {
this.refill();
if (this.tokens >= count) {
this.tokens -= count;
return;
}
const needed = count - this.tokens;
const waitMs = (needed / this.refillRate) * 1000;
await new Promise((r) => setTimeout(r, Math.max(waitMs, 10)));
this.refill();
this.tokens -= count;
}
}
class RateLimiter {
private buckets = new Map<EndpointCategory, TokenBucket>();
constructor() {
for (const [cat, config] of Object.entries(RATE_LIMITS)) {
this.buckets.set(cat as EndpointCategory, new TokenBucket(config));
}
}
async acquire(category: EndpointCategory): Promise<void> {
const bucket = this.buckets.get(category);
if (bucket) await bucket.acquire();
}
}
export const rateLimiter = new RateLimiter();