Too Many Requests Error

https://bessapay.net/errors/too-many-requests

Overview

A Too Many Requests error occurs when you have exceeded the rate limits for API calls. This error is returned with HTTP status code 429 and indicates that you should slow down the rate at which you are making requests.

Details

  • Error Code: TOO_MANY_REQUESTS
  • HTTP Status: 429
  • Error Type: Client Error

Common scenarios

  • • Exceeded requests per second limit
  • • Exceeded daily or monthly API quota
  • • Multiple concurrent requests to the same endpoint
  • • Batch operations with too many items
  • • Automated scripts without proper rate limiting

Example

Too Many Requests Error Response
1{
2  "type": "https://bessapay.net/errors/too-many-requests",
3  "title": "Too Many Requests",
4  "status": 429,
5  "detail": "Rate limit exceeded. Please wait before making additional requests.",
6  "instance": "/api/v1/transactions",
7  "errorCode": "TOO_MANY_REQUESTS",
8  "timestamp": "2023-06-15T16:45:12Z",
9  "rateLimit": {
10    "limit": 100,
11    "remaining": 0,
12    "reset": 1623772512
13  }
14}

How to Fix

Implement rate limiting

Add rate limiting logic to your client applications to stay within the API limits. Use the rate limit information provided in response headers to adjust your request rate.

Implement exponential backoff

When encountering a 429 error, implement an exponential backoff strategy where you wait progressively longer between retries (e.g., 1s, 2s, 4s, 8s).

Exponential Backoff Example
1async function fetchWithBackoff(url, options, maxRetries = 5) {
2  let retries = 0;
3  
4  while (retries < maxRetries) {
5    try {
6      const response = await fetch(url, options);
7      
8      if (response.status !== 429) {
9        return response;
10      }
11      
12      // If we got a 429, get retry information
13      const retryAfter = response.headers.get('Retry-After');
14      const waitTime = retryAfter ? parseInt(retryAfter, 10) * 1000 : Math.pow(2, retries) * 1000;
15      
16      console.log(`Rate limited. Waiting ${waitTime}ms before retry.`);
17      await new Promise(resolve => setTimeout(resolve, waitTime));
18      
19      retries++;
20    } catch (error) {
21      throw error;
22    }
23  }
24  
25  throw new Error('Maximum retries exceeded');
26}

Monitor rate limits

BessaPay API includes rate limit information in response headers:

  • X-RateLimit-Limit: Maximum number of requests per time window
  • X-RateLimit-Remaining: Number of requests remaining in the current window
  • X-RateLimit-Reset: Unix timestamp when the rate limit window resets

Optimize request patterns

Consider batching multiple operations into single requests when possible. Cache responses that don't change frequently to reduce API calls.

Upgrade your plan

If you consistently hit rate limits, consider upgrading to a plan with higher limits that better suits your usage patterns.

Code Example: Handling Rate Limit Errors

JavaScript Rate Limit Handling
1// Complete example of handling rate limits with queuing
2class ApiClient {
3  constructor(apiKey, baseUrl = 'https://api.semuni.com/v1') {
4    this.apiKey = apiKey;
5    this.baseUrl = baseUrl;
6    this.requestQueue = [];
7    this.isProcessingQueue = false;
8    this.rateLimitRemaining = null;
9    this.rateLimitReset = null;
10  }
11
12  async request(endpoint, options = {}) {
13    // Add to queue and process
14    return new Promise((resolve, reject) => {
15      this.requestQueue.push({
16        endpoint,
17        options,
18        resolve,
19        reject
20      });
21      
22      this.processQueue();
23    });
24  }
25
26  async processQueue() {
27    if (this.isProcessingQueue || this.requestQueue.length === 0) {
28      return;
29    }
30
31    this.isProcessingQueue = true;
32
33    // Check if we need to wait for rate limit reset
34    if (this.rateLimitRemaining === 0 && this.rateLimitReset) {
35      const now = Math.floor(Date.now() / 1000);
36      if (now < this.rateLimitReset) {
37        const waitTime = (this.rateLimitReset - now) * 1000;
38        console.log(`Rate limit reached. Waiting ${waitTime}ms before continuing.`);
39        await new Promise(resolve => setTimeout(resolve, waitTime));
40      }
41    }
42
43    const { endpoint, options, resolve, reject } = this.requestQueue.shift();
44
45    try {
46      const response = await fetch(`${this.baseUrl}${endpoint}`, {
47        ...options,
48        headers: {
49          'Authorization': `Bearer ${this.apiKey}`,
50          'Content-Type': 'application/json',
51          ...options.headers
52        }
53      });
54
55      // Update rate limit info from headers
56      this.rateLimitRemaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '1000', 10);
57      this.rateLimitReset = parseInt(response.headers.get('X-RateLimit-Reset') || '0', 10);
58
59      if (response.status === 429) {
60        // Put the request back in the queue
61        const retryAfter = parseInt(response.headers.get('Retry-After') || '1', 10);
62        console.log(`Rate limited. Retrying after ${retryAfter} seconds.`);
63        
64        setTimeout(() => {
65          this.requestQueue.unshift({ endpoint, options, resolve, reject });
66          this.isProcessingQueue = false;
67          this.processQueue();
68        }, retryAfter * 1000);
69        return;
70      }
71
72      const data = await response.json();
73      resolve(data);
74    } catch (error) {
75      reject(error);
76    } finally {
77      this.isProcessingQueue = false;
78      // Continue processing queue after a small delay
79      setTimeout(() => this.processQueue(), 100);
80    }
81  }
82}
83
84// Usage example
85const client = new ApiClient('your-api-key');
86client.request('/transactions')
87  .then(data => console.log(data))
88  .catch(error => console.error(error));