Rate Limits & Quotas

Truthlock enforces rate limits to ensure fair usage and platform stability. This guide explains limits by tier and how to handle rate limiting gracefully.

Limits by Tier

TierRequests/minAttestations/dayIssuersKeys/Issuer
Free6010022
Starter30010,000105
Professional1,000100,0005010
EnterpriseCustomCustomUnlimitedUnlimited
Burst Capacity: All tiers include 2x burst capacity for up to 10 seconds. This allows handling traffic spikes without immediate rate limiting.

Per-Endpoint Limits

Some endpoints have additional specific limits:

EndpointLimitWindowReason
POST /v1/attestations/mint100/min60sSigning is CPU-intensive
POST /v1/verifyNo limit-Public endpoint, cached
POST /v1/issuers10/hour1hPrevent issuer spam
POST /v1/api-keys20/day24hSecurity measure
GET /v1/audit/events30/min60sDatabase-heavy query

Rate Limit Headers

Every API response includes headers to help you track your usage:

HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1705147260
X-RateLimit-Policy: "1000;w=60"
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the window
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the window resets
X-RateLimit-PolicyPolicy description (requests;window=seconds)

Handling 429 Responses

When you exceed the rate limit, you'll receive a 429 status code:

HTTP/1.1 429 Too Many Requests
Retry-After: 32
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705147260

{
  "code": "RATE_LIMITED",
  "message": "Rate limit exceeded. Retry after 32 seconds.",
  "http_status": 429,
  "details": {
    "limit": 1000,
    "window_seconds": 60,
    "retry_after_seconds": 32
  }
}
Important: Always respect the Retry-Afterheader. Repeatedly hitting rate limits may result in temporary IP blocks.

Best Practices

Exponential Backoff

async function withBackoff<T>(
  fn: () => Promise<T>,
  maxRetries = 5,
  baseDelayMs = 1000
): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error.status !== 429 || attempt === maxRetries - 1) {
        throw error;
      }
      
      const retryAfter = error.headers?.['retry-after'] 
        ? parseInt(error.headers['retry-after']) * 1000
        : baseDelayMs * Math.pow(2, attempt);
      
      const jitter = Math.random() * 1000;
      await new Promise(r => setTimeout(r, retryAfter + jitter));
    }
  }
  throw new Error('Max retries exceeded');
}

Request Batching

// Instead of minting one at a time:
for (const user of users) {
  await client.attestations.mint({ ... });
}

// Batch with controlled concurrency:
import pLimit from 'p-limit';

const limit = pLimit(10); // Max 10 concurrent requests
const results = await Promise.all(
  users.map(user => 
    limit(() => client.attestations.mint({ ... }))
  )
);

Monitor Usage

// Track remaining quota in your metrics
function trackRateLimit(response: Response) {
  const remaining = response.headers.get('X-RateLimit-Remaining');
  const limit = response.headers.get('X-RateLimit-Limit');
  
  metrics.gauge('truthlock.ratelimit.remaining', parseInt(remaining));
  metrics.gauge('truthlock.ratelimit.usage_percent', 
    (1 - parseInt(remaining) / parseInt(limit)) * 100
  );
}

Requesting Quota Increases

If your usage requires higher limits:

  1. Starter/Professional: Contact support with your use case and expected volume
  2. Enterprise: Custom limits are negotiated during contract discussions
  3. Temporary Increase: For events or migrations, request a temporary burst increase 48 hours in advance
Tip: Include metrics showing your current usage patterns and growth projections when requesting increases.

Next Steps