Skip to main content

Rate Limits & Quotas

CrowdProof uses a sliding-window rate limiter per API key. Limits vary by tier.

Tier Limits

TierRequests/SecondMonthly QueriesWebhooksPrice
Free10100$0/mo
Starter10010,0003$49/mo
Growth500100,00010$199/mo
Enterprise2,000Unlimited50Contact us

Rate Limit Headers

Every API response includes rate limit information in headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per second for your tier
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait (only on 429 responses)

Checking Usage

GET /api/v1/billing/usage
X-API-Key: cp_live_abc123...
{
"queriesThisMonth": 847,
"monthlyLimit": 10000,
"remaining": 9153
}

What Counts as a Query?

ActionCounts?
GET /api/v1/reputation/{address}Yes (1 query)
GET /api/v1/reputation/{address}/{category}Yes (1 query)
POST /api/v1/reputation/batch (10 addresses)Yes (10 queries)
POST /api/v1/reputation/proveYes (1 query)
POST /api/v1/reputation/verifyNo (free)
GET /api/v1/identity/{did}No (free)
GET /api/v1/billing/usageNo (free)
GET /healthNo (free)

Handling Rate Limits

When you hit a rate limit, the API returns 429 Too Many Requests:

{
"error": "Rate limit exceeded",
"details": "Free tier allows 10 requests per second. Upgrade at /api/v1/billing/checkout"
}
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;

const retryAfter = parseInt(response.headers.get('Retry-After') || '1');
await new Promise(r => setTimeout(r, retryAfter * 1000));
}
throw new Error('Rate limit exceeded after retries');
}

Upgrading

To upgrade your tier, create a Stripe checkout session:

POST /api/v1/billing/checkout
{
"planId": "growth",
"successUrl": "https://your-app.com/billing/success"
}