Skip to main content

The Problem

LLM APIs like OpenAI have strict rate limits on both:
  • Tokens per minute (e.g., 40,000 TPM)
  • Requests per minute (e.g., 1,000 RPM)
Exceeding these limits causes failures and impacts user experience. You need to enforce these limits in your application while maintaining high throughput.

Solution: Dual Rate Limits with Sharding

Use two separate rate limits (tokens and requests) with sharding to handle high concurrency without database contention.

Configuration

convex/rateLimits.ts
Why sharding? With many concurrent requests, sharding prevents database contention by distributing the load across multiple rate limit buckets while maintaining overall correctness.

Implementation

Basic Token Counting

convex/ai.ts

Internal Mutations for Rate Limiting

convex/ai.ts

Advanced: Reservation Pattern

For better throughput, use the reservation pattern to queue requests:
convex/ai.ts

Accurate Token Counting

For precise token counting, use a tokenizer library:
convex/ai.ts
Install the tokenizer:

Testing High Throughput

convex/test.ts

Client-Side Usage

src/AIChat.tsx

Common Variations

Configure tiers:
Sharding trade-off: With 10 shards, each shard has 1/10th the capacity (4,000 tokens). The power-of-two selection helps balance load, but you may occasionally be rate limited when overall capacity exists. This is the trade-off for avoiding database contention.
Token estimation: Always overestimate token counts when checking limits. It’s better to reserve too many tokens than to exceed your LLM provider’s limits and get throttled.
Use the reservation pattern (reserve: true) for better throughput. It allows requests to queue automatically instead of failing, maximizing your API limit utilization.