HelloJohn / docs
API Reference

Rate Limits

Rate limiting policies, limits by endpoint category, headers, and strategies for handling 429 errors.

Rate Limits

HelloJohn applies rate limits to protect the platform and ensure fair usage. Limits vary by endpoint category, authentication type, and plan.

How Rate Limiting Works

Requests are tracked using a sliding window algorithm per API key and tenant. When the limit is exceeded, the API returns a 429 Too Many Requests error.

{
  "error": "rate_limit_exceeded",
  "message": "Rate limit exceeded. Retry after 23 seconds.",
  "retry_after": 23
}

Rate Limit Headers

Every API response includes the following headers:

HeaderDescription
X-RateLimit-LimitRequests allowed per window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait before retrying (only on 429)
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 842
X-RateLimit-Reset: 1705399200

Limits by Category

Authentication Endpoints

Stricter limits apply to auth endpoints to prevent brute-force attacks.

EndpointLimitWindow
POST /v1/auth/sign-in101 minute per IP
POST /v1/auth/sign-up51 minute per IP
POST /v1/auth/magic-link510 minutes per IP
POST /v1/auth/password/forgot510 minutes per email
POST /v1/mfa/verify510 minutes per user
POST /v1/auth/token/refresh601 minute per key

IP-based limits apply per IP address. User-based limits apply per user ID.

Data API Endpoints

TierLimitWindow
Read (GET)1,0001 minute per key
Write (POST, PATCH, DELETE)5001 minute per key
Bulk operations501 minute per key

Webhook Endpoints

EndpointLimitWindow
POST /v1/webhooks/*/test101 minute per key
Delivery retries1001 hour per key

Email Endpoints

EndpointLimitWindow
POST /v1/email/config/test51 hour per key
Template previews301 minute per key

Limits by Plan

PlanRead (per min)Write (per min)Auth (per IP/min)
Free100305
Starter50015010
Pro1,00050020
EnterpriseCustomCustomCustom

Handling Rate Limits

Exponential Backoff

When you receive a 429, implement exponential backoff:

async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status !== 429) {
      return response;
    }

    if (attempt === maxRetries) {
      throw new Error("Rate limit exceeded after max retries");
    }

    const retryAfter = parseInt(response.headers.get("Retry-After") ?? "1", 10);
    const delay = retryAfter * 1000 * Math.pow(2, attempt);

    console.log(`Rate limited. Retrying in ${delay}ms...`);
    await new Promise((resolve) => setTimeout(resolve, delay));
  }
}

Monitor Before Hitting Limits

Check the X-RateLimit-Remaining header and slow down proactively:

async function apiRequest(url: string) {
  const response = await fetch(url, { headers: { Authorization: "Bearer sk_live_..." } });

  const remaining = parseInt(response.headers.get("X-RateLimit-Remaining") ?? "999", 10);
  const reset = parseInt(response.headers.get("X-RateLimit-Reset") ?? "0", 10);

  if (remaining < 50) {
    const waitMs = (reset * 1000) - Date.now();
    if (waitMs > 0) {
      console.warn(`Approaching rate limit. Waiting ${waitMs}ms...`);
      await new Promise((resolve) => setTimeout(resolve, waitMs));
    }
  }

  return response.json();
}

Batch Requests

For bulk operations, use batch-friendly endpoints instead of looping over individual calls:

// ❌ Slow — N API calls
for (const userId of userIds) {
  await fetch(`/v1/users/${userId}`);
}

// ✅ Fast — 1 API call
await fetch("/v1/admin/users/search", {
  method: "POST",
  body: JSON.stringify({ filters: { ids: userIds } }),
});

Rate Limit Exemptions

The following are not counted against rate limits:

  • Webhook delivery callbacks (your server receiving events)
  • JWKS endpoint (/.well-known/jwks.json) — cached at CDN edge
  • Health check endpoint (/health)

Increasing Limits

Contact support@hellojohn.dev to request higher limits for your plan, or upgrade to Enterprise for custom limits with SLA guarantees.


On this page