Skip to main content

Rate Limits

The Flow API uses rate limiting to ensure fair usage and system stability. This guide explains how rate limits work and how to handle them.

Overview

Rate limits are applied per API key and are based on your subscription plan. Limits are enforced using a sliding window algorithm.

Rate Limit Headers

Every API response includes rate limit information in headers:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1703123456
Retry-After: 60

Header Descriptions

HeaderDescription
X-RateLimit-LimitMaximum number of requests allowed in the current window
X-RateLimit-RemainingNumber of requests remaining in the current window
X-RateLimit-ResetUnix timestamp (seconds) when the rate limit window resets
Retry-AfterNumber of seconds to wait before retrying (only present on 429 responses)

Rate Limit Tiers

Rate limits vary by subscription plan:

PlanRequests per MinuteRequests per HourRequests per Day
Free601,00010,000
Stream1205,00050,000
River30015,000150,000
Ocean60030,000300,000

Note: Rate limits are subject to change. Check your plan details for current limits.

Rate Limit Windows

Rate limits use sliding windows:

  • Per-minute limit: Counts requests in the last 60 seconds
  • Per-hour limit: Counts requests in the last 3600 seconds
  • Per-day limit: Counts requests in the last 86400 seconds

This means requests don't reset at fixed intervals (e.g., top of the hour), but rather continuously based on the sliding window.

Handling Rate Limits

429 Too Many Requests

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

{
"error": "Rate limit exceeded",
"message": "You have exceeded the rate limit. Please try again later.",
"retry_after": 60
}

Retry Strategy

  1. Read the Retry-After header - This tells you exactly when to retry
  2. Wait before retrying - Don't retry immediately
  3. Implement exponential backoff - If retry fails, wait longer

Example implementation:

async function makeRequestWithRateLimitHandling(url: string, options: RequestInit) {
const maxRetries = 3;

for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);

if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');

if (attempt < maxRetries) {
console.log(`Rate limited. Waiting ${retryAfter} seconds before retry...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
continue;
}

throw new Error('Rate limit exceeded. Max retries reached.');
}

return response;
}
}

Monitoring Rate Limits

Always check rate limit headers to avoid hitting limits:

async function checkRateLimit(response: Response) {
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '0');
const limit = parseInt(response.headers.get('X-RateLimit-Limit') || '0');
const reset = parseInt(response.headers.get('X-RateLimit-Reset') || '0');

// Log or alert when approaching limit
if (remaining < limit * 0.1) { // Less than 10% remaining
console.warn(`Rate limit warning: ${remaining}/${limit} requests remaining`);
}

return { remaining, limit, reset };
}

Best Practices

1. Monitor Rate Limit Headers

Always check X-RateLimit-Remaining to know how many requests you have left:

const response = await fetch(url, options);
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '0');

if (remaining < 10) {
// Slow down or queue requests
await delayRequests();
}

2. Implement Request Queuing

Queue requests when approaching limits:

class RateLimitedQueue {
private queue: Array<() => Promise<any>> = [];
private processing = false;

async add<T>(fn: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await fn();
resolve(result);
} catch (error) {
reject(error);
}
});

this.process();
});
}

private async process() {
if (this.processing || this.queue.length === 0) return;

this.processing = true;
while (this.queue.length > 0) {
const fn = this.queue.shift();
if (fn) await fn();

// Small delay between requests to avoid hitting limits
await new Promise(resolve => setTimeout(resolve, 100));
}
this.processing = false;
}
}

3. Use Bulk Operations

When possible, use bulk endpoints to reduce API calls:

// Instead of multiple POST requests:
for (const post of posts) {
await flow.posts.create(post); // 10 API calls
}

// Use bulk endpoint:
await flow.posts.batchCreate(posts); // 1 API call

4. Cache Responses

Cache GET requests to reduce API calls:

const cache = new Map<string, { data: any; expires: number }>();

async function getCached<T>(key: string, fn: () => Promise<T>, ttl = 60000): Promise<T> {
const cached = cache.get(key);
if (cached && cached.expires > Date.now()) {
return cached.data;
}

const data = await fn();
cache.set(key, { data, expires: Date.now() + ttl });
return data;
}

// Usage
const posts = await getCached('posts', () => flow.posts.list(), 60000);

5. Parallel Requests (with Caution)

Make independent requests in parallel, but respect rate limits:

// Good: Parallel requests for independent resources
const [channels, webhooks] = await Promise.all([
flow.channels.list(),
flow.webhooks.list(),
]);

// Bad: Too many parallel requests (may hit rate limit)
const posts = await Promise.all(
Array(100).fill(0).map(() => flow.posts.create({ ... }))
);

Rate Limit by Endpoint

Some endpoints have stricter rate limits:

EndpointLimit
POST /v1/postsSame as plan limit
GET /v1/postsSame as plan limit
POST /v1/posts/batch10 requests/minute (bulk operations)

Increasing Rate Limits

To increase your rate limits:

  1. Upgrade your plan - Higher tiers have higher limits
  2. Contact support - Enterprise customers can request custom limits
  3. Optimize usage - Use bulk operations and caching to reduce API calls

Testing Rate Limits

In the test environment, rate limits are more lenient to allow for testing:

  • Test environment: 1,000 requests/minute
  • Production: Based on your plan

SDK Support

The Flow SDKs automatically handle rate limits:

TypeScript SDK

import { Flow } from '@flowdev/sdk';

const flow = new Flow('flow_sk_live_...');

// SDK automatically:
// - Retries on 429 with exponential backoff
// - Respects Retry-After header
// - Logs rate limit warnings

Python SDK

from flow_sdk import Flow

flow = Flow(api_key="flow_sk_live_...")

# SDK automatically handles rate limit retries

Monitoring and Alerts

Set up monitoring for rate limit usage:

// Example: Alert when rate limit is low
function monitorRateLimit(response: Response) {
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '0');
const limit = parseInt(response.headers.get('X-RateLimit-Limit') || '0');
const usagePercent = ((limit - remaining) / limit) * 100;

if (usagePercent > 80) {
// Send alert
sendAlert(`Rate limit usage: ${usagePercent.toFixed(1)}%`);
}
}

Common Issues

Issue: Hitting Rate Limits Frequently

Solutions:

  • Implement request queuing
  • Use bulk operations
  • Cache GET requests
  • Upgrade your plan

Issue: Retry Loops

Solutions:

  • Always respect Retry-After header
  • Implement maximum retry limits
  • Use exponential backoff

Issue: Rate Limit Headers Missing

Solutions:

  • Check that you're using the latest API version
  • Ensure you're authenticated (rate limit headers only appear for authenticated requests)
  • Contact support if headers are consistently missing

Support