SKILL.md
Clerk Rate Limits
Overview
Understand Clerk's rate limiting system and implement strategies to avoid hitting limits. Covers Backend API rate limits, retry logic, batching, caching, and monitoring.
Prerequisites
- Clerk account with API access
- Understanding of your application's traffic patterns
- Monitoring/logging infrastructure
Instructions
Step 1: Understand Rate Limits
Clerk Backend API enforces rate limits per API key:
| Plan | Rate Limit | Burst |
|---|---|---|
| Free | 20 req/10s | 40 |
| Pro | 100 req/10s | 200 |
| Enterprise | Custom | Custom |
Rate limit headers returned on every response:
X-RateLimit-Limit— max requests per windowX-RateLimit-Remaining— remaining requestsX-RateLimit-Reset— seconds until window resets
Step 2: Implement Rate Limit Handling with Retry
// lib/clerk-api.ts
import { createClerkClient } from '@clerk/backend'
const clerk = createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY! })
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn()
} catch (err: any) {
if (err.status === 429 && attempt < maxRetries) {
// Parse retry-after header or use exponential backoff
const retryAfter = err.headers?.['retry-after']
const waitMs = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, attempt) * 1000
console.warn(`Rate limited. Retrying in ${waitMs}ms (attempt ${attempt + 1}/${maxRetries})`)
await new Promise((resolve) => setTimeout(resolve, waitMs))
continue
}
throw err
}
}
throw new Error('Max retries exceeded')
}
// Usage
export async function getUser(userId: string) {
return withRetry(() => clerk.users.getUser(userId))
}
