TypeScript

How to Resolve 429 Too Many Requests Error

The 429 Too Many Requests error is a typical HTTP status code that indicates that a client made too many requests in a certain period of time. This issue is generally caused by server-side rate-limiting methods, and understanding how to fix it is critical for ensuring seamless online or API interactions.

We shall take a look at the following:

  1. What causes the 429 error.
  2. How to fix it step by step.

Let's Deep Dive Into 429 Errors

The 429 error is mainly caused by server-imposed rate limits to prevent against:

  • Abusive traffic: Several requests from bots or scripts.
  • API rate limits: Exceeding the allotted amount of API calls within a certain timeframe.
  • Server overload: Too many request from many people at the same time
  • Misconfigured code: A loop or bug sending the same requests.

Technical Way To Resolve Such Error

1. Implement Retry with Exponential Backoff:

The most reliable way to deal with rate constraints is this. By lengthening the interval between retries, exponential backoff lowers server load without sacrificing functionality.

async function fetchWithExponentialBackoff(url, maxRetries = 5) {
  const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      
      if (response.ok) return response;
      
      if (response.status === 429) {
        // Calculate delay with exponential backoff
        const delay = Math.min(1000 * Math.pow(2, i), 10000);
        console.log(`Rate limited. Retrying in ${delay}ms`);
        await wait(delay);
        continue;
      }
      
      throw new Error(`HTTP ${response.status}`);
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}

2. Break Data Being Sent To The Server Into Chunks

Rate constraints can be avoided when working with huge datasets by breaking down the data into smaller chunks:

async function processInChunks(requests, chunkSize = 5, delayBetweenChunks = 1000) {
  for (let i = 0; i < requests.length; i += chunkSize) {
    const chunk = requests.slice(i, i + chunkSize);
    console.log(`Processing chunk: ${i / chunkSize + 1}`);
    
    // Process the current chunk
    await Promise.all(chunk.map(async (url) => {
      try {
        const response = await fetch(url);
        console.log(`Response from ${url}:`, await response.json());
      } catch (error) {
        console.error(`Error fetching ${url}:`, error);
      }
    }));
    
    // Delay before the next chunk
    if (i + chunkSize < requests.length) {
      console.log(`Waiting ${delayBetweenChunks}ms before the next chunk...`);
      await new Promise((resolve) => setTimeout(resolve, delayBetweenChunks));
    }
  }
}
  1. Using forEach in Sequential Processing

Sequential processing can be more dependable when handling rate constraints, even though it is slower than Promise.all(): It's slower nature helps to add a natural delay between each request.

  • Requests are handled one by one
  • To prevent overloading the server, a setTimeout is used to introduce a wait after each request.
async function processSequentiallyWithDelay(requests, delay = 1000) {
  for (const url of requests) {
    try {
      const response = await fetch(url);
      console.log(`Response from ${url}:`, await response.json());
    } catch (error) {
      console.error(`Error fetching ${url}:`, error);
    }
    console.log(`Waiting ${delay}ms before the next request...`);
    await new Promise((resolve) => setTimeout(resolve, delay));
  }
}

Usage Examples

1. Using Exponential Backoff in API Calls

// Example: Fetching user data with exponential backoff
async function fetchUserData(userId) {
  try {
    const response = await fetchWithExponentialBackoff(`/api/users/${userId}`);
    const userData = await response.json();
    return userData;
  } catch (error) {
    console.error(`Failed to fetch user data for ID ${userId}:`, error);
    throw error;
  }
}

// Example usage in an application
async function updateUserProfiles() {
  const userIds = ['user1', 'user2', 'user3'];
  
  try {
    const results = await Promise.all(
      userIds.map(id => fetchUserData(id))
    );
    console.log('Successfully updated user profiles:', results);
  } catch (error) {
    console.error('Error updating user profiles:', error);
  }
}
  1. Processing Huge Datasets with Chunks
// Example: Uploading multiple images
async function uploadImages(images) {
  const uploadResults = await processInChunks(images, 5);
  
  // Process results
  const successful = uploadResults.filter(r => r.ok).length;
  console.log(`Successfully uploaded ${successful} of ${images.length} images`);
  
  return uploadResults;
}

// Usage example
const images = [
  { id: 1, data: 'image1_data' },
  { id: 2, data: 'image2_data' },
  // ... more images
];

uploadImages(images)
  .then(results => console.log('Upload complete:', results))
  .catch(error => console.error('Upload failed:', error));
  1. Using For Each Method to sequentially Process Rate Sensitive APIS
// Example: Sending emails with rate limits
async function sendBulkEmails(emailList) {
  const emailResults = await processSequentially(emailList);
  
  // Analyze results
  const sent = emailResults.filter(r => r.status === 'sent').length;
  console.log(`Successfully sent ${sent} of ${emailList.length} emails`);
  
  return emailResults;
}

// Usage example
const emails = [
  { to: 'user1@example.com', subject: 'Hello', content: 'Message 1' },
  { to: 'user2@example.com', subject: 'Hi', content: 'Message 2' },
  // ... more emails
];

sendBulkEmails(emails)
  .then(results => console.log('Email campaign complete:', results))
  .catch(error => console.error('Email campaign failed:', error));

Why These Approaches Resolve This Error

  • Exponential Backoff: Assists in lowering aggressive retries that could put additional strain on the server.
  • Chunking: Limits the number of concurrent requests to avoid overloading the server.
  • Sequential processing with delays allows the server to manage load in a smooth manner by providing adequate buffer time between requests.

About

At DevelopersMonk, we share tutorials, tips, and insights on modern programming frameworks like React, Next.js, Spring Boot, and more. Join us on our journey to simplify coding and empower developers worldwide!

Email: developersmonks@gmail.com

Phone: +23359*******

Quick Links

  • Home
  • About
  • Contact Us
  • Categories

  • Java
  • TypeScript
  • JavaScript
  • React
  • Nextjs
  • Spring Boot
  • DevOps