> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Rate Limiting & Optimization

> Understanding StateSet API rate limits, implementing efficient request patterns, and optimizing API usage for better performance

# API Rate Limiting & Optimization

StateSet implements rate limiting to ensure fair usage and maintain service reliability for all users. This guide covers understanding rate limits, implementing efficient request patterns, and optimizing your API usage.

## Rate Limit Overview

StateSet uses a **sliding window** rate limiting algorithm that provides smooth, predictable limits across different time windows.

<CardGroup cols={3}>
  <Card title="Per-Second Limits" icon="clock">
    Prevents burst traffic spikes and ensures responsive service
  </Card>

  <Card title="Per-Minute Limits" icon="timer">
    Controls sustained request volume over longer periods
  </Card>

  <Card title="Per-Hour Limits" icon="calendar-clock">
    Manages overall API consumption and prevents abuse
  </Card>
</CardGroup>

## Current Rate Limits

### By Plan Type

<Tabs>
  <Tab title="Starter Plan">
    | Endpoint Category | Per Second | Per Minute | Per Hour |
    | ----------------- | ---------- | ---------- | -------- |
    | **Orders API**    | 10         | 300        | 5,000    |
    | **Customers API** | 5          | 150        | 2,500    |
    | **Returns API**   | 5          | 150        | 2,500    |
    | **Webhooks**      | 20         | 600        | 10,000   |
    | **Analytics**     | 2          | 60         | 1,000    |
  </Tab>

  <Tab title="Growth Plan">
    | Endpoint Category | Per Second | Per Minute | Per Hour |
    | ----------------- | ---------- | ---------- | -------- |
    | **Orders API**    | 50         | 1,500      | 25,000   |
    | **Customers API** | 25         | 750        | 12,500   |
    | **Returns API**   | 25         | 750        | 12,500   |
    | **Webhooks**      | 100        | 3,000      | 50,000   |
    | **Analytics**     | 10         | 300        | 5,000    |
  </Tab>

  <Tab title="Enterprise Plan">
    | Endpoint Category | Per Second | Per Minute | Per Hour |
    | ----------------- | ---------- | ---------- | -------- |
    | **Orders API**    | 200        | 6,000      | 100,000  |
    | **Customers API** | 100        | 3,000      | 50,000   |
    | **Returns API**   | 100        | 3,000      | 50,000   |
    | **Webhooks**      | 500        | 15,000     | 250,000  |
    | **Analytics**     | 50         | 1,500      | 25,000   |
  </Tab>
</Tabs>

### Special Considerations

<AccordionGroup>
  <Accordion title="Batch Operations">
    * Batch endpoints have **higher rate limits** but count as multiple requests
    * Each item in a batch counts toward your rate limit
    * Maximum batch size: **100 items per request**
  </Accordion>

  <Accordion title="List Operations">
    * List endpoints with large `limit` parameters consume more quota
    * Requests with `limit > 100` count as `Math.ceil(limit/100)` requests
    * Use pagination instead of large limit values
  </Accordion>

  <Accordion title="Webhook Endpoints">
    * Webhook deliveries **do not count** against your rate limits
    * Failed webhook retries use separate retry quotas
    * Webhook verification calls are not rate limited
  </Accordion>
</AccordionGroup>

## Rate Limit Headers

Every API response includes rate limit information in the headers:

```http theme={null}
HTTP/1.1 200 OK
X-RateLimit-Limit: 1500
X-RateLimit-Remaining: 1247
X-RateLimit-Reset: 1640995200
X-RateLimit-Window: 60
X-RateLimit-Category: orders-api
Retry-After: 45
```

### Header Descriptions

| Header                  | Description                                                 |
| ----------------------- | ----------------------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the current window              |
| `X-RateLimit-Remaining` | Requests remaining in the current window                    |
| `X-RateLimit-Reset`     | Unix timestamp when the window resets                       |
| `X-RateLimit-Window`    | Window duration in seconds (60 for per-minute limits)       |
| `X-RateLimit-Category`  | API category for this endpoint                              |
| `Retry-After`           | Seconds to wait before retrying (present when rate limited) |

## Implementing Rate Limit Handling

### Basic Rate Limit Detection

```javascript theme={null}
import { StateSetClient } from 'StateSet-node';
import winston from 'winston';

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [new winston.transports.Console()]
});

class RateLimitHandler {
  constructor(client) {
    this.client = client;
    this.requestCounts = new Map();
  }

  async makeRequest(operation, ...args) {
    try {
      const response = await operation(...args);
      this.logRateLimitInfo(response);
      return response;
    } catch (error) {
      if (error.status === 429) {
        return this.handleRateLimit(error, operation, ...args);
      }
      throw error;
    }
  }

  logRateLimitInfo(response) {
    const headers = response.headers || {};
    logger.info('Rate limit status', {
      remaining: headers['x-ratelimit-remaining'],
      limit: headers['x-ratelimit-limit'],
      resetTime: new Date(headers['x-ratelimit-reset'] * 1000),
      category: headers['x-ratelimit-category']
    });
  }

  async handleRateLimit(error, operation, ...args) {
    const retryAfter = error.headers?.['retry-after'] || 60;
    
    logger.warn('Rate limit exceeded, waiting before retry', {
      retryAfter,
      category: error.headers?.['x-ratelimit-category']
    });

    await this.sleep(retryAfter * 1000);
    return this.makeRequest(operation, ...args);
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage
const client = new StateSetClient({ apiKey: process.env.STATESET_API_KEY });
const rateLimitHandler = new RateLimitHandler(client);

const orders = await rateLimitHandler.makeRequest(
  () => client.orders.list({ limit: 50 })
);
```

### Advanced Rate Limiting with Queue

```javascript theme={null}
import PQueue from 'p-queue';

class AdvancedRateLimitHandler {
  constructor(client, options = {}) {
    this.client = client;
    this.queues = new Map();
    this.rateLimitInfo = new Map();
    
    // Default queue options
    this.defaultQueueOptions = {
      concurrency: 5,
      interval: 1000,
      intervalCap: 10,
      ...options.queueOptions
    };
  }

  getQueue(category = 'default') {
    if (!this.queues.has(category)) {
      const queueOptions = this.getCategoryQueueOptions(category);
      this.queues.set(category, new PQueue(queueOptions));
    }
    return this.queues.get(category);
  }

  getCategoryQueueOptions(category) {
    const categorySettings = {
      'orders-api': { concurrency: 10, interval: 1000, intervalCap: 10 },
      'customers-api': { concurrency: 5, interval: 1000, intervalCap: 5 },
      'returns-api': { concurrency: 5, interval: 1000, intervalCap: 5 },
      'analytics': { concurrency: 2, interval: 1000, intervalCap: 2 }
    };

    return {
      ...this.defaultQueueOptions,
      ...categorySettings[category]
    };
  }

  async makeRequest(operation, category = 'default', priority = 0) {
    const queue = this.getQueue(category);
    
    return queue.add(async () => {
      try {
        const response = await operation();
        this.updateRateLimitInfo(category, response);
        return response;
      } catch (error) {
        if (error.status === 429) {
          await this.handleRateLimit(error, category);
          throw error; // Re-queue will happen automatically
        }
        throw error;
      }
    }, { priority });
  }

  updateRateLimitInfo(category, response) {
    const headers = response.headers || {};
    this.rateLimitInfo.set(category, {
      remaining: parseInt(headers['x-ratelimit-remaining']) || 0,
      limit: parseInt(headers['x-ratelimit-limit']) || 1000,
      resetTime: parseInt(headers['x-ratelimit-reset']) || 0,
      lastUpdated: Date.now()
    });
  }

  async handleRateLimit(error, category) {
    const retryAfter = parseInt(error.headers?.['retry-after']) || 60;
    const queue = this.getQueue(category);
    
    logger.warn('Rate limit exceeded for category', {
      category,
      retryAfter,
      queueSize: queue.size,
      pending: queue.pending
    });

    // Pause the queue
    queue.pause();
    
    await this.sleep(retryAfter * 1000);
    
    // Resume the queue
    queue.start();
  }

  getRateLimitStatus(category = 'default') {
    return this.rateLimitInfo.get(category) || null;
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage with priority queueing
const handler = new AdvancedRateLimitHandler(client);

// High priority request (emergency order processing)
const criticalOrder = await handler.makeRequest(
  () => client.orders.get(orderId),
  'orders-api',
  10
);

// Normal priority batch processing
const orders = await Promise.all([
  handler.makeRequest(() => client.orders.list({ page: 1 }), 'orders-api', 0),
  handler.makeRequest(() => client.orders.list({ page: 2 }), 'orders-api', 0),
  handler.makeRequest(() => client.orders.list({ page: 3 }), 'orders-api', 0)
]);
```

## Optimization Strategies

### 1. Efficient Pagination

Instead of requesting large datasets at once:

```javascript theme={null}
// ❌ Inefficient - May trigger rate limits
const allOrders = await client.orders.list({ limit: 1000 });

// ✅ Efficient - Paginated requests
async function getAllOrdersPaginated() {
  const allOrders = [];
  let hasMore = true;
  let cursor = null;

  while (hasMore) {
    const response = await client.orders.list({
      limit: 100,
      cursor: cursor
    });

    allOrders.push(...response.data);
    cursor = response.next_cursor;
    hasMore = response.has_more;

    // Small delay to respect rate limits
    await new Promise(resolve => setTimeout(resolve, 100));
  }

  return allOrders;
}
```

### 2. Batch Operations

Use batch endpoints when available:

```javascript theme={null}
// ❌ Multiple individual requests
for (const order of orders) {
  await client.orders.update(order.id, { status: 'processed' });
}

// ✅ Single batch request
await client.orders.batchUpdate(
  orders.map(order => ({
    id: order.id,
    status: 'processed'
  }))
);
```

### 3. Webhook-First Architecture

Reduce polling by using webhooks:

```javascript theme={null}
// ❌ Constant polling
setInterval(async () => {
  const orders = await client.orders.list({ 
    status: 'pending',
    updated_since: lastCheck 
  });
  processNewOrders(orders);
}, 30000);

// ✅ Webhook-driven updates
app.post('/webhooks/StateSet', (req, res) => {
  const event = req.body;
  
  if (event.type === 'order.updated') {
    processOrderUpdate(event.data);
  }
  
  res.status(200).send('OK');
});
```

### 4. Intelligent Caching

Cache frequently requested data:

```javascript theme={null}
class StateSetCache {
  constructor(client, ttl = 300000) { // 5 minutes default TTL
    this.client = client;
    this.cache = new Map();
    this.ttl = ttl;
  }

  async get(key, fetchFunction) {
    const cached = this.cache.get(key);
    const now = Date.now();

    if (cached && (now - cached.timestamp) < this.ttl) {
      logger.debug('Cache hit', { key });
      return cached.data;
    }

    logger.debug('Cache miss, fetching', { key });
    const data = await fetchFunction();
    
    this.cache.set(key, {
      data,
      timestamp: now
    });

    return data;
  }

  clear(key) {
    if (key) {
      this.cache.delete(key);
    } else {
      this.cache.clear();
    }
  }
}

// Usage
const cache = new StateSetCache(client);

const customer = await cache.get(
  `customer:${customerId}`,
  () => client.customers.get(customerId)
);
```

## Monitoring Rate Limits

### Rate Limit Dashboard

Create a monitoring dashboard for your rate limit usage:

```javascript theme={null}
class RateLimitMonitor {
  constructor() {
    this.metrics = {
      requests: new Map(),
      rateLimits: new Map(),
      errors: new Map()
    };
  }

  recordRequest(category, success = true) {
    const key = `${category}:${this.getTimeWindow()}`;
    const current = this.metrics.requests.get(key) || { success: 0, total: 0 };
    
    current.total++;
    if (success) current.success++;
    
    this.metrics.requests.set(key, current);
  }

  recordRateLimit(category, remainingRequests, totalLimit) {
    const key = `${category}:${this.getTimeWindow()}`;
    this.metrics.rateLimits.set(key, {
      remaining: remainingRequests,
      total: totalLimit,
      utilizationPct: ((totalLimit - remainingRequests) / totalLimit) * 100,
      timestamp: Date.now()
    });
  }

  getTimeWindow() {
    return Math.floor(Date.now() / 60000); // 1-minute windows
  }

  getUtilizationReport() {
    const report = {};
    
    for (const [key, data] of this.metrics.rateLimits) {
      const [category] = key.split(':');
      if (!report[category]) report[category] = [];
      report[category].push(data);
    }

    return report;
  }

  // Alert when utilization is high
  checkAlerts() {
    const report = this.getUtilizationReport();
    
    for (const [category, dataPoints] of Object.entries(report)) {
      const latest = dataPoints[dataPoints.length - 1];
      
      if (latest.utilizationPct > 80) {
        logger.warn('High API utilization detected', {
          category,
          utilization: latest.utilizationPct,
          remaining: latest.remaining
        });
      }
    }
  }
}

// Usage with middleware
const monitor = new RateLimitMonitor();

const monitoredClient = new Proxy(client, {
  get(target, prop) {
    const original = target[prop];
    
    if (typeof original === 'object' && original !== null) {
      return new Proxy(original, {
        get(apiTarget, apiProp) {
          const apiMethod = apiTarget[apiProp];
          
          if (typeof apiMethod === 'function') {
            return async (...args) => {
              const category = `${prop}-api`;
              
              try {
                const result = await apiMethod.apply(apiTarget, args);
                monitor.recordRequest(category, true);
                
                if (result.headers) {
                  monitor.recordRateLimit(
                    category,
                    parseInt(result.headers['x-ratelimit-remaining']),
                    parseInt(result.headers['x-ratelimit-limit'])
                  );
                }
                
                return result;
              } catch (error) {
                monitor.recordRequest(category, false);
                throw error;
              }
            };
          }
          
          return apiMethod;
        }
      });
    }
    
    return original;
  }
});
```

## Best Practices Summary

<CardGroup cols={2}>
  <Card title="Request Optimization" icon="rocket">
    * Use pagination instead of large limit values
    * Implement batch operations where possible
    * Cache frequently requested data
    * Use webhooks to reduce polling
  </Card>

  <Card title="Error Handling" icon="shield-check">
    * Always check rate limit headers
    * Implement exponential backoff for retries
    * Use queues to manage request flow
    * Monitor and alert on high utilization
  </Card>
</CardGroup>

<CardGroup cols={2}>
  <Card title="Architecture Patterns" icon="sitemap">
    * Design webhook-first integrations
    * Implement circuit breakers for resilience
    * Use separate queues for different priorities
    * Cache aggressively with smart invalidation
  </Card>

  <Card title="Monitoring & Alerting" icon="chart-line">
    * Track utilization across all categories
    * Set alerts at 80% utilization threshold
    * Monitor request success rates
    * Review patterns during peak usage
  </Card>
</CardGroup>

## Getting Help

If you're consistently hitting rate limits or need higher limits:

1. **Review your usage patterns** using the monitoring tools above
2. **Optimize your integration** following the strategies in this guide
3. **Consider upgrading** to a higher plan with increased limits
4. **Contact support** at [support@StateSet.com](mailto:support@StateSet.com) for custom rate limits

## Related Resources

* [Error Handling Best Practices](/guides/error-handling-best-practices)
* [Webhook Security Guide](/guides/webhook-security)
* [API Testing Strategies](/guides/api-testing)
* [Performance Optimization](/guides/performance-optimization)
