Fixing HubSpot API Rate Limit (429 Too Many Requests) and Timeout Errors
Comprehensive guide to resolving HubSpot API 429 Too Many Requests and timeout errors. Learn burst limits, batch optimization, and how to implement exponential
- HubSpot enforces both a burst limit (e.g., 100-150 requests per 10 seconds) and a daily limit (250,000 to 1,000,000 requests) depending on your API tier and authentication method.
- HTTP 429 'Too Many Requests' indicates you have hit a rate limit, while 504 or timeouts often result from overly large batch operations or unoptimized queries.
- Implement exponential backoff with jitter to gracefully handle 429 responses without instantly exhausting your retries.
- Use Batch APIs and proper pagination techniques to reduce the total number of outbound requests and mitigate network timeout risks.
| Method | When to Use | Implementation Time | Risk of Dropped Data |
|---|---|---|---|
| Exponential Backoff | Standard for all API integrations encountering 429 errors. | Low | Low |
| Batch API Endpoints | When syncing large volumes of contacts, companies, or deals. | Medium | Low |
| Queue/Worker Architecture | For enterprise-grade integrations with asynchronous processing. | High | Very Low |
| Upgrading API Limit | When daily volume legitimately exceeds the current HubSpot tier. | Low (Costly) | None |
Understanding HubSpot API Rate Limits
When integrating with HubSpot, encountering an HTTP 429 Too Many Requests error is a rite of passage for many developers. HubSpot uses rate limiting to ensure platform stability and equitable resource distribution across all tenants. Understanding the nuances of these limits—and the closely related timeout errors—is critical for building resilient applications.
The Anatomy of the Error
When you exceed HubSpot's API limits, the server responds with a 429 Too Many Requests status code. The response body typically looks like this:
{
"status": "error",
"message": "You have reached your secondly limit.",
"errorType": "RATE_LIMIT",
"correlationId": "a1b2c3d4-e5f6-7890-abcd-1234567890ab",
"policyName": "SECONDLY"
}
There are two primary types of rate limits enforced by HubSpot:
- Burst Limits (Secondly/Ten-secondly limits): This restricts how many requests you can make in a short window. For OAuth apps, this is typically 100 requests per 10 seconds per account. For Private Apps, it's 150 requests per 10 seconds.
- Daily Limits: This is a hard cap on the total number of requests a portal can make in a 24-hour rolling window. This ranges from 250,000 for standard accounts up to 1,000,000 for API Add-on subscribers.
HubSpot API Timeouts (502 / 504)
Related to rate limits are timeout errors. When you try to circumvent rate limits by stuffing too much data into a single request (for example, batch updating 10,000 contacts at once without proper chunking), the HubSpot processing engine may take too long to respond, resulting in an HTTP 504 Gateway Timeout or a client-side connection timeout.
Timeouts typically occur because the payload size is too large or the database query generated by your request (such as a complex CRM Search API filter) is unoptimized.
Step 1: Diagnose the Bottleneck
Before refactoring your codebase, you must identify whether you are hitting a burst limit, a daily limit, or a timeout due to payload size.
Check the HTTP response headers returned by HubSpot. They provide critical debugging information:
X-HubSpot-RateLimit-Daily: Your total daily allotment.X-HubSpot-RateLimit-Daily-Remaining: How many requests you have left today.X-HubSpot-RateLimit-Secondly: Your burst limit.X-HubSpot-RateLimit-Secondly-Remaining: Remaining requests in the current 10-second window.
If X-HubSpot-RateLimit-Secondly-Remaining is hitting 0, your application is too aggressive in a short timeframe. If you are seeing connection timeouts before receiving any headers, your payload or query complexity is the culprit.
Step 2: Implement Exponential Backoff
The most robust way to handle 429 Burst Limit errors is to implement an exponential backoff algorithm with jitter. When a 429 is received, the client pauses for a brief period before retrying. If the next request also fails, the pause duration increases exponentially.
Adding 'jitter' (a random variation to the sleep time) prevents the 'thundering herd' problem, where multiple blocked threads all wake up and retry at the exact same millisecond, immediately triggering another 429.
Step 3: Utilize Batch APIs
If you are iterating over an array of 500 contacts and making an individual PATCH /crm/v3/objects/contacts/{contactId} request for each one, you will inevitably hit the burst limit.
Instead, use HubSpot's Batch API endpoints (e.g., POST /crm/v3/objects/contacts/batch/update). Batch endpoints allow you to update up to 100 records in a single HTTP request. This effectively reduces your API call volume by a factor of 100, easily keeping you under the 100 requests / 10 seconds limit while processing thousands of records per minute.
Step 4: Optimize CRM Search and Timeouts
To resolve timeout errors during search or bulk retrieval:
- Reduce
limitparameters: If you are asking for 100 records per page, drop it to 50 or 25. - Limit returned properties: Only request the exact properties you need using the
propertiesquery parameter. Returning massive custom property datasets for every contact drastically increases processing time and payload size. - Index consideration: Ensure your search filters are utilizing indexed fields where possible, and avoid leading wildcard searches in string matching.
Step 5: Architecture for Scale (Message Queues)
For enterprise environments syncing millions of rows, synchronous API calls will eventually fail. Transition to a Message Queue architecture (using RabbitMQ, AWS SQS, or Apache Kafka).
When a record needs updating, push a message to the queue. A background worker consumes from the queue at a strictly regulated pace (e.g., pulling messages and batching them into groups of 100, firing exactly 5 requests per second). This decoupled architecture guarantees you will never exceed the burst limit and provides a durable mechanism for retries in the event of daily limit exhaustion.
Frequently Asked Questions
import time
import random
import requests
from requests.exceptions import RequestException
# Advanced Exponential Backoff Implementation for HubSpot API
def hubspot_request_with_backoff(url, headers, payload=None, method='GET', max_retries=5):
base_delay = 1.0 # Initial delay in seconds
max_delay = 32.0 # Maximum delay between retries
for attempt in range(max_retries):
try:
if method == 'GET':
response = requests.get(url, headers=headers)
elif method == 'POST':
response = requests.post(url, headers=headers, json=payload)
elif method == 'PATCH':
response = requests.patch(url, headers=headers, json=payload)
# Check for Rate Limit Error
if response.status_code == 429:
# HubSpot often returns a Retry-After header
retry_after = response.headers.get('Retry-After')
if retry_after:
sleep_time = float(retry_after)
else:
# Calculate exponential backoff: (2^attempt) * base_delay
sleep_time = min(max_delay, base_delay * (2 ** attempt))
# Add jitter to prevent thundering herd
sleep_time += random.uniform(0, 0.5)
print(f"Rate limit hit (429). Retrying in {sleep_time:.2f} seconds...")
time.sleep(sleep_time)
continue
# Raise for other HTTP errors (500, 502, 504 timeouts)
response.raise_for_status()
return response.json()
except requests.exceptions.Timeout:
print("Request timed out. Consider reducing batch size or complexity.")
sleep_time = min(max_delay, base_delay * (2 ** attempt)) + random.uniform(0, 1)
time.sleep(sleep_time)
except RequestException as e:
print(f"Request failed: {e}")
if attempt == max_retries - 1:
raise
raise Exception("Max retries exceeded")
# Example usage:
# headers = {"Authorization": "Bearer YOUR_PAT"}
# data = hubspot_request_with_backoff("https://api.hubapi.com/crm/v3/objects/contacts", headers)
Error Medic Editorial
Our team of seasoned Site Reliability Engineers and DevOps professionals brings decades of collective experience in diagnosing complex API integrations, database tuning, and cloud infrastructure optimization.