home / skills / jeremylongshore / claude-code-plugins-plus-skills / clerk-rate-limits

This skill helps you manage Clerk rate limits and quotas, optimizing API usage and avoiding throttling in high-traffic scenarios.

npx playbooks add skill jeremylongshore/claude-code-plugins-plus-skills --skill clerk-rate-limits

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.3 KB
---
name: clerk-rate-limits
description: |
  Understand and manage Clerk rate limits and quotas.
  Use when hitting rate limits, optimizing API usage,
  or planning for high-traffic scenarios.
  Trigger with phrases like "clerk rate limit", "clerk quota",
  "clerk API limits", "clerk throttling".
allowed-tools: Read, Write, Edit, Grep
version: 1.0.0
license: MIT
author: Jeremy Longshore <[email protected]>
---

# Clerk Rate Limits

## Overview
Understand Clerk's rate limiting system and implement strategies to avoid hitting limits.

## Prerequisites
- Clerk account with API access
- Understanding of your application's traffic patterns
- Monitoring/logging infrastructure

## Instructions

### Step 1: Understand Rate Limits

#### Clerk API Rate Limits (as of 2024)
| Endpoint Category | Free Tier | Pro Tier | Enterprise |
|------------------|-----------|----------|------------|
| Authentication | 100/min | 500/min | Custom |
| User Management | 100/min | 500/min | Custom |
| Session Management | 200/min | 1000/min | Custom |
| Webhooks | Unlimited | Unlimited | Unlimited |

#### Client-Side Limits
- SDK requests are automatically throttled
- Browser session: 10 requests/second
- Token refresh: 1 per 50 seconds (automatic)

### Step 2: Implement Rate Limit Handling
```typescript
// lib/clerk-client.ts
import { clerkClient } from '@clerk/nextjs/server'

interface RateLimitConfig {
  maxRetries: number
  baseDelay: number
}

async function withRateLimitRetry<T>(
  operation: () => Promise<T>,
  config: RateLimitConfig = { maxRetries: 3, baseDelay: 1000 }
): Promise<T> {
  let lastError: Error | null = null

  for (let attempt = 0; attempt < config.maxRetries; attempt++) {
    try {
      return await operation()
    } catch (error: any) {
      lastError = error

      // Check for rate limit error
      if (error.status === 429 || error.code === 'rate_limit_exceeded') {
        const delay = config.baseDelay * Math.pow(2, attempt)
        console.warn(`Rate limited, retrying in ${delay}ms (attempt ${attempt + 1})`)
        await new Promise(resolve => setTimeout(resolve, delay))
        continue
      }

      // Non-rate-limit error, throw immediately
      throw error
    }
  }

  throw lastError
}

// Usage
export async function getUser(userId: string) {
  const client = await clerkClient()
  return withRateLimitRetry(() => client.users.getUser(userId))
}
```

### Step 3: Batch Operations
```typescript
// lib/clerk-batch.ts
import { clerkClient } from '@clerk/nextjs/server'

// Instead of multiple individual calls
async function getBatchedUsers(userIds: string[]) {
  const client = await clerkClient()

  // Use getUserList with userId filter (single API call)
  const { data: users } = await client.users.getUserList({
    userId: userIds,
    limit: 100
  })

  return users
}

// Paginated fetching with rate limit awareness
async function getAllUsers(batchSize = 100, delayMs = 100) {
  const client = await clerkClient()
  const allUsers = []
  let offset = 0

  while (true) {
    const { data: users, totalCount } = await client.users.getUserList({
      limit: batchSize,
      offset
    })

    allUsers.push(...users)
    offset += batchSize

    if (allUsers.length >= totalCount) break

    // Rate limit friendly delay
    await new Promise(resolve => setTimeout(resolve, delayMs))
  }

  return allUsers
}
```

### Step 4: Caching Strategy
```typescript
// lib/clerk-cache.ts
import { unstable_cache } from 'next/cache'
import { clerkClient } from '@clerk/nextjs/server'

// Cache user data to reduce API calls
export const getCachedUser = unstable_cache(
  async (userId: string) => {
    const client = await clerkClient()
    return client.users.getUser(userId)
  },
  ['clerk-user'],
  {
    revalidate: 60, // Cache for 60 seconds
    tags: ['clerk-users']
  }
)

// In-memory cache for high-frequency lookups
const userCache = new Map<string, { user: any; timestamp: number }>()
const CACHE_TTL = 30000 // 30 seconds

export async function getUserWithCache(userId: string) {
  const cached = userCache.get(userId)
  if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
    return cached.user
  }

  const client = await clerkClient()
  const user = await client.users.getUser(userId)

  userCache.set(userId, { user, timestamp: Date.now() })
  return user
}
```

### Step 5: Monitor Rate Limit Usage
```typescript
// lib/clerk-monitor.ts
interface RateLimitMetrics {
  endpoint: string
  remaining: number
  limit: number
  resetAt: Date
}

const metrics: RateLimitMetrics[] = []

export function trackRateLimit(response: Response) {
  const remaining = response.headers.get('x-ratelimit-remaining')
  const limit = response.headers.get('x-ratelimit-limit')
  const reset = response.headers.get('x-ratelimit-reset')

  if (remaining && limit) {
    metrics.push({
      endpoint: response.url,
      remaining: parseInt(remaining),
      limit: parseInt(limit),
      resetAt: reset ? new Date(parseInt(reset) * 1000) : new Date()
    })

    // Alert if approaching limit
    if (parseInt(remaining) < parseInt(limit) * 0.1) {
      console.warn('Approaching rate limit:', {
        remaining,
        limit,
        endpoint: response.url
      })
    }
  }
}

export function getRateLimitMetrics() {
  return metrics.slice(-100) // Last 100 entries
}
```

## Output
- Rate limit handling with retries
- Batched API operations
- Caching implementation
- Monitoring system

## Rate Limit Headers
```
x-ratelimit-limit: 100
x-ratelimit-remaining: 95
x-ratelimit-reset: 1704067200
```

## Best Practices

1. **Batch requests** - Use getUserList instead of multiple getUser calls
2. **Cache aggressively** - User data rarely changes in real-time
3. **Use webhooks** - Let Clerk push updates instead of polling
4. **Exponential backoff** - Retry with increasing delays
5. **Monitor usage** - Track rate limit headers

## Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| 429 Too Many Requests | Rate limit exceeded | Implement backoff, cache more |
| quota_exceeded | Monthly quota hit | Upgrade plan or reduce usage |
| concurrent_limit | Too many parallel requests | Queue requests |

## Resources
- [Clerk Rate Limits](https://clerk.com/docs/backend-requests/resources/rate-limits)
- [API Best Practices](https://clerk.com/docs/backend-requests/overview)
- [Pricing & Quotas](https://clerk.com/pricing)

## Next Steps
Proceed to `clerk-security-basics` for security best practices.

Overview

This skill helps you understand Clerk rate limits and implement concrete strategies to avoid or recover from throttling. It explains rate headers, common error codes, and provides patterns for retries, batching, caching, and monitoring. Use it to make Clerk API usage more resilient and cost‑efficient.

How this skill works

The skill inspects Clerk rate limit behavior and response headers (x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset) and recommends code patterns for handling 429 and quota errors. It provides retry logic with exponential backoff, batch fetching examples, caching strategies (both edge and in-memory), and lightweight monitoring hooks to track usage. It also maps error types to practical remediation steps like queueing, caching, or plan upgrades.

When to use it

  • You see 429 Too Many Requests or rate_limit_exceeded errors
  • Planning capacity for expected traffic spikes or launches
  • Optimizing API costs and reducing redundant calls
  • Building background jobs that iterate over many users
  • Designing client apps to avoid excessive token refreshes

Best practices

  • Batch requests where possible (e.g., getUserList with multiple userIds)
  • Cache user data aggressively and set sensible TTLs for edge and in-memory caches
  • Implement exponential backoff and limited retry attempts for 429 responses
  • Monitor x-ratelimit headers and alert when remaining drops below a threshold
  • Prefer webhooks for change notifications instead of frequent polling

Example use cases

  • Wrap Clerk client calls with a withRateLimitRetry helper that retries on 429 with backoff
  • Fetch users in paginated batches with small delays to avoid bursts
  • Use unstable_cache or an in-memory Map for high-frequency user lookups
  • Track recent rate limit metrics and warn when usage approaches 90% of the limit
  • Apply queuing or concurrency limits when running bulk jobs to avoid concurrent_limit errors

FAQ

What headers tell me how close I am to a limit?

Check x-ratelimit-limit, x-ratelimit-remaining, and x-ratelimit-reset in responses to calculate remaining capacity and reset time.

How should I retry after a 429?

Use exponential backoff with a small maxRetries (e.g., 3) and increasing delays, and avoid retrying non-rate-limit errors.