OptStuff

Rate Limiting

How OptStuff rate limiting works — sliding window algorithm, dual-layer limits, configuration, response format, and tuning recommendations.

Rate limiting protects your account from abuse and unexpected costs. Each API key has independent rate limits, enforced globally across all server instances.

Dual-Layer Limits

Every request is checked against two independent limits. Both must pass.

LayerDefaultWindowPurpose
Per-day10,000 requests24 hoursCatch sustained overuse
Per-minute60 requests1 minuteCatch sudden bursts

Algorithm: Sliding Window

Unlike fixed windows that can allow up to 2× burst at boundaries, the sliding window algorithm produces smooth limits:

Configuration

Rate limits are stored per API key in the database:

SettingRangeDefault
rateLimitPerMinute1 – 10,00060
rateLimitPerDay1 – 1,000,00010,000

New API keys are created with the default limits. Custom per-key rate limits are not yet configurable through the dashboard UI — this feature is planned for a future release. If you need to adjust limits, update the api_key table directly:

  • rateLimitPerMinute — valid range: 1 – 10,000
  • rateLimitPerDay — valid range: 1 – 1,000,000

Before modifying: back up the api_key table and validate that your values fall within the ranges above (the schema enforces integer type but not range — out-of-range values may cause unexpected behaviour). If you are unsure about the correct limits for your use case, contact support.

Changes take effect within 60 seconds (cache TTL).

Response When Rate Limited

{
  "error": "Rate limit exceeded",
  "reason": "Too many requests per minute",
  "retryAfter": 12,
  "limit": 60
}

retryAfter is the number of seconds to wait before retrying (consistent with the HTTP Retry-After header).

HTTP headers:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0

Clients should respect the Retry-After header to avoid further 429 responses.

Tuning Recommendations

Traffic LevelRecommendation
Low (< 100 req/day)Defaults work well
Medium (100 – 10,000 req/day)Monitor analytics; increase per-day limit if hit legitimately
High (> 10,000 req/day)Set per-key limits based on expected traffic to avoid false 429s

Client-Side Retry Strategy

When receiving a 429 response, implement exponential backoff with jitter:

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

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

    if (attempt === maxRetries) return response;

    const retryAfter = response.headers.get("Retry-After");
    const baseDelay = retryAfter
      ? Number(retryAfter) * 1000
      : Math.pow(2, attempt) * 1000;
    const jitter = Math.random() * 1000;
    await new Promise((resolve) => setTimeout(resolve, baseDelay + jitter));
  }

  throw new Error("Unreachable");
}

Key principles:

  • Respect Retry-After — always prefer the server-provided delay over your own backoff
  • Add jitter — prevents multiple clients from retrying in sync (thundering herd)
  • Limit retries — 3 retries is usually sufficient; more indicates a capacity problem

CDN and Rate Limiting Interaction

If you place a CDN (Cloudflare, CloudFront, Vercel Edge) in front of OptStuff:

ScenarioRate Limit Impact
CDN cache hitNo request reaches OptStuff — rate limit is not consumed
CDN cache missRequest reaches OptStuff — rate limit is consumed
First request for a URLAlways consumes quota (cache is cold)

This means rate limits primarily affect unique or uncached requests. For high-traffic sites with good CDN cache hit ratios, rate limits are rarely a concern.

See CDN and Caching for cache optimization strategies.

Monitoring Recommendations

What to MonitorWhySuggested Alert
429 response rateDetect legitimate traffic hitting limitsAlert when 429 rate exceeds 5% of total requests
Per-key daily usageCatch keys approaching limitsAlert at 80% of daily quota
Redis connectivityRate limiter depends on RedisAlert on Redis connection errors

When the rate limiter fails open (Redis unavailable), monitor for abnormal traffic patterns and consider temporary upstream WAF rules until Redis recovers.

Design Notes

  • Rate limits are checked after signature verification so that unauthenticated requests cannot exhaust quota.
  • The per-day limit is checked before the per-minute limit to minimize token waste (see Redis Schema for details).
  • Both GET and HEAD requests consume quota because both run through the same authentication and abuse-protection pipeline.
  • If Redis is temporarily unavailable, the limiter fails open (allowed) to prioritize image-serving availability; monitor infrastructure health accordingly.

Last updated on

On this page