Skip to main content

Working with API Rate Limits

Improve API reliability with smart rate limit handling

Updated over 3 weeks ago

Overview

You may encounter 429 Too Many Requests errors when you hit an API rate limit. This can happen in two scenarios:

  • Monthly billing limits - based on your subscription plan:

    • Plans with an overage allowance will apply additional charges when you go beyond your plan limits.

    • Plans without overage allowance will block API calls when limits are reached.

NOTE: Standard iPaaS.com plans do not have monthly billing limits.

  • Volume restrictions occur when API resources are overwhelmed by high request volume.

Example: Handling API Rate Limits

This example demonstrates how to handle HTTP 429 Too Many Requests as a result of hitting rate limits. Adapt this approach for your integration's API.

Logic Flow

Design Decisions

Our implementation uses these principles:

  • 60-second threshold: Implemented to avoid exceeding request limits on the system.

  • Automatic retry vs failure: Balances user experience with system performance.

  • Dual logging: Provides both user-friendly messages and technical details.

  • Cancellation support: Allows interrupting the wait period if needed.

Implementation

This example uses the Shopify API. Modify the code for your integration.

// Check if the API response indicates we've hit the rate limit
if (resp.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
// Extract the "Retry-After" header which tells us how long to wait before trying again
var retryAfter = ReadResponseHeader(resp, "Retry-After");

// Parse the retry time (usually in seconds) and store it in our response object
double.TryParse(retryAfter, out apiResponse.LimitTimeResetSeconds);

// Shopify's API allocates requests per minute, so reset time should be under 60 seconds
// If it's reasonable (< 60 seconds), we'll wait and retry automatically
// If it's longer, we'll give up and throw an error instead of waiting too long
if (apiResponse.LimitTimeResetSeconds < 60)
{
// Build a technical error message for logging
var techErr = $"Received TooManyRequests response from Shopify. The call was rejected, but we will wait for more calls to be available in {apiResponse.LimitTimeResetSeconds} seconds";

// Include response details if available
if (resp.Content != null)
techErr += $". Details: {resp.Content}";

// Log a user-friendly activity message (Warning level)
connection.Logger.LogActivityTracker(
"Failed API Call to Shopify (Quota)",
$"Received TooManyRequests response from {actionCustomerFacing}. You are over your API quota but we will wait for more calls to be available in {apiResponse.LimitTimeResetSeconds} seconds",
"Warning",
(int)mappingCollectionType
);

// Log technical details for developers/debugging
connection.Logger.LogTechnical(
"D", // Debug level
$"ShopifyCallWrapper.{action}",
techErr
);

// Wait for the specified number of seconds (convert to milliseconds)
// Uses a cancellation-aware sleep method
await StandardUtilities.SleepWithCancelSupport(Convert.ToInt16(apiResponse.LimitTimeResetSeconds) * 1000);

// Tell the calling code to retry this request
apiResponse.action = IntegrationAPIResponse.ResponseAction.Retry;
return apiResponse;
}
else
{
// If the wait time is too long (>= 60 seconds), don't wait - just fail

// Create a response object and copy quota information
var response = new Integration.Abstract.Model.ResponseObject();
StandardUtilities.AssignQuotaValues(response, apiResponse);

// Log an error-level activity message for the user
connection.Logger.LogActivityTracker(
"Failed API Call to Shopify (Quota)",
$"Received TooManyRequests response from {actionCustomerFacing}. You are over your API quota but more calls should be available in {apiResponse.LimitTimeResetSeconds} seconds",
"Error",
(int)mappingCollectionType
);

// Log technical error details
connection.Logger.LogTechnical(
"E", // Error level
$"ShopifyCallWrapper.{action}",
$"Too many API calls made to Shopify. The call was rejected. Most calls should be available in {apiResponse.LimitTimeResetSeconds} seconds"
);

// Throw an exception to stop processing
throw new Exception("Shopify API Quota Exception. Too Many Requests. Available in: " + apiResponse.LimitTimeResetSeconds);
}
}
Did this answer your question?