Skip to content

Rate Limits

The API enforces rate limits per IP address.

Also See

Check out Query Complexity Limits to learn about per-query cost restrictions that work alongside rate limits.

Limits

60 requests per 60 seconds

The API uses a token bucket algorithm. Tokens refill at 1 per second with a maximum of 60 tokens.

Headers

Every response includes rate limit headers:

HeaderDescription
RateLimit-LimitMaximum number of requests allowed in the time window (60)
RateLimit-RemainingNumber of requests remaining in the current window
RateLimit-ResetUnix timestamp (seconds) when the rate limit window resets

Example Response Headers

RateLimit-Limit: 60
RateLimit-Remaining: 45
RateLimit-Reset: 1704067200

Handling Rate Limits

When you exceed the rate limit, the API returns a 429 Too Many Requests status code with a GraphQL error response:

json
{
  "errors": [
    {
      "message": "Rate limit exceeded. Please retry after the reset time.",
      "extensions": {
        "code": "RATE_LIMIT_EXCEEDED"
      }
    }
  ]
}

The response also includes a Retry-After header indicating how many seconds you should wait before retrying:

Retry-After: 15

Best Practices

  1. Monitor RateLimit-Remaining header
  2. Implement exponential backoff on 429 responses
  3. Cache query results
  4. Request only needed fields
  5. Batch queries using aliases
  6. Choose appropriate page sizes

Token Bucket Details

  • Each IP has a bucket with maximum 60 tokens
  • Tokens refill at 1 per second
  • Each request consumes 1 token
  • Empty bucket results in 429 status

You can burst up to 60 requests then wait for refill. Average rate is 1 request per second.

Checking Headers

bash
# Make a GraphQL request and check headers
curl -I -X POST https://api.lcbo.dev/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{ health { status } }"}'

# Response includes:
# RateLimit-Limit: 60
# RateLimit-Remaining: 59
# RateLimit-Reset: 1704067200

Handling 429 Errors

javascript
async function executeGraphQLQuery(query, variables) {
  const response = await fetch('https://api.lcbo.dev/graphql', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ query, variables }),
  });
  
  if (response.status === 429) {
    const retryAfter = response.headers.get('Retry-After');
    console.log(`Rate limited. Retry after ${retryAfter} seconds`);
    
    // Wait and retry
    await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
    return executeGraphQLQuery(query, variables);
  }
  
  // Check remaining requests
  const remaining = response.headers.get('RateLimit-Remaining');
  if (parseInt(remaining) < 10) {
    console.warn(`Low rate limit remaining: ${remaining}`);
  }
  
  return response.json();
}

GraphQL Queries

  • Field selection doesn't affect rate limit
  • Complex nested queries count as 1 request
  • Multiple aliased queries count as 1 request
  • Batch queries to reduce request count

Query Complexity

While rate limits control how many requests you can make, query complexity limits control what you can request in each query. Both work together to ensure fair API usage.

Next Steps