Resolving HubSpot API Rate Limit (429 Too Many Requests) and Timeout (504) Errors
Fix HubSpot API 429 Too Many Requests and 504 Gateway Timeouts by implementing exponential backoff, request batching, and optimizing CRM search payload sizes.
- Burst limits (100-150 requests per 10 seconds) trigger TEN_SECOND_ROLLING_WINDOW_EXCEEDED 429 errors.
- Daily limits (250,000 to 1,000,000 requests) trigger DAILY_LIMIT_EXCEEDED 429 errors and require an API Add-on or architectural changes.
- 504 Gateway Timeouts occur during unoptimized CRM Search queries or massive association fetching.
- Quick fix summary: Implement HTTP 429 retry logic using Exponential Backoff with Jitter, switch to Batch API endpoints, and reduce requested properties.
| Method | When to Use | Time | Risk |
|---|---|---|---|
| Exponential Backoff | Handling 429 Burst Limits | 2-4 Hours | Low |
| Batch API Endpoints | Bulk creates/updates/reads | 1-2 Days | Low |
| Webhook Migration | Replacing polling architectures | 1-2 Weeks | Medium |
| Payload Optimization | Fixing 504 Gateway Timeouts | Hours | Low |
Understanding HubSpot API Rate Limits and Timeouts
When scaling integrations with the HubSpot API, engineers inevitably encounter two major bottlenecks: HTTP 429 (Too Many Requests) and HTTP 504 (Gateway Timeout). Because HubSpot acts as the central CRM for mission-critical operations, failing to handle these errors gracefully can lead to data desynchronization, missed lead routing, and broken reporting pipelines.
The Anatomy of a HubSpot 429 Error
HubSpot enforces two distinct tiers of rate limits depending on your authentication method (OAuth vs. Private App) and your account tier (Free, Starter, Professional, Enterprise, or API Add-on).
- The Burst Limit (10-Second Rolling Window): This is the most frequently hit limit. HubSpot restricts accounts to either 100 or 150 requests per 10 seconds. When you exceed this, you receive a
429 Too Many Requestsresponse with theTEN_SECOND_ROLLING_WINDOW_EXCEEDEDerror category. - The Daily Limit: Depending on your tier, you are allocated between 250,000 and 1,000,000 requests per day (resetting at midnight EST). Exceeding this triggers the
DAILY_LIMIT_EXCEEDEDcategory.
A typical 429 error payload looks like this:
{
"status": "error",
"message": "You have reached your ten second limit.",
"errorType": "RATE_LIMIT",
"correlationId": "a1b2c3d4-e5f6-7890-1234-56789abcdef0",
"category": "TEN_SECOND_ROLLING_WINDOW_EXCEEDED"
}
Diagnosing API Limits Using Response Headers
Before implementing a fix, you must diagnose which limit you are hitting. HubSpot includes vital rate-limit headers in every API response:
X-HubSpot-RateLimit-Daily: Your total daily quota.X-HubSpot-RateLimit-Daily-Remaining: The number of calls remaining for the day.X-HubSpot-RateLimit-Remaining: The number of calls remaining in the current 10-second rolling window.X-HubSpot-RateLimit-Max: The maximum calls allowed per 10-second window.
Monitor X-HubSpot-RateLimit-Remaining. If this number consistently approaches zero, you need to throttle your outbound requests.
Step 1: Implementing Exponential Backoff for Burst Limits
The industry standard for handling 429 burst limits is Exponential Backoff with Jitter. If a request fails with a 429, the application should sleep for a short duration and retry. If it fails again, the sleep duration doubles, and so on. Jitter (a randomized variance) is added to prevent "thundering herd" scenarios where multiple parallel threads wake up and retry at the exact same millisecond.
Do not simply sleep(10) blindly. A smart implementation checks the error category. If it is DAILY_LIMIT_EXCEEDED, retrying is useless until the next day. You must log a critical alert and halt the queue. If it is TEN_SECOND_ROLLING_WINDOW_EXCEEDED, a brief backoff (e.g., 1-3 seconds) is usually sufficient to clear the rolling window.
Step 2: Transitioning to the Batch API
If you are hitting rate limits while syncing databases or creating multiple records, you are likely using the single-object endpoints (e.g., POST /crm/v3/objects/contacts). This is an anti-pattern for bulk operations.
HubSpot offers Batch APIs (e.g., POST /crm/v3/objects/contacts/batch/create) that allow you to process up to 100 records in a single API call. By batching your requests, you reduce your API footprint by 99%, instantly resolving most 10-second and daily rate limit issues.
Step 3: Diagnosing and Fixing 504 Gateway Timeouts
Unlike 429s, a 504 Gateway Timeout indicates that HubSpot's servers took too long to process your request and forcefully terminated the connection. This almost never happens on simple reads by ID. It predominantly occurs on the CRM Search API (POST /crm/v3/objects/{objectType}/search) under the following conditions:
- Over-fetching Properties: Requesting hundreds of properties in the
propertiesarray forces HubSpot's database to join massive amounts of data. - Complex Filter Groups: Using deeply nested
AND/ORlogic with wildcardCONTAINS_TOKENoperators on non-indexed fields. - Deep Associations: Attempting to read objects and resolve complex associations across thousands of records simultaneously.
How to Fix HubSpot 504 Timeouts:
- Reduce Payload Size: Only include the exact properties you need in your request body. Never use a wildcard or fetch properties "just in case."
- Optimize Search Filters: Avoid
CONTAINS_TOKENorHAS_PROPERTYon massive datasets. Prefer exact match operators (EQ,IN) on standard, indexed properties likeemailordomain. - Paginate Properly: Use the
limitparameter to fetch smaller chunks (e.g., 50 instead of 100) and rely on theaftercursor for pagination. - Isolate Associations: If you need associations, do not append them to a massive search query. Run a lightweight search to get the Object IDs, then use the Batch Read API to fetch the specific properties and associations by ID.
Frequently Asked Questions
import requests
import time
import logging
import random
logging.basicConfig(level=logging.INFO)
def hubspot_request_with_retry(method, url, headers, data=None, max_retries=5):
"""
Executes a HubSpot API request with exponential backoff and jitter
to handle 429 TEN_SECOND_ROLLING_WINDOW_EXCEEDED limits.
"""
base_delay = 1.0 # initial delay in seconds
for attempt in range(max_retries):
try:
response = requests.request(method, url, headers=headers, json=data)
# If successful or client error (not rate limit), return immediately
if response.status_code != 429 and response.status_code != 504:
response.raise_for_status()
return response.json()
if response.status_code == 429:
error_data = response.json()
category = error_data.get("category")
if category == "DAILY_LIMIT_EXCEEDED":
logging.error("HubSpot Daily Limit Exceeded. Halting operations.")
raise Exception("DAILY_LIMIT_EXCEEDED")
logging.warning(f"429 Burst Limit Hit. Retrying attempt {attempt + 1}/{max_retries}")
elif response.status_code == 504:
logging.warning(f"504 Gateway Timeout. Retrying attempt {attempt + 1}/{max_retries}")
except requests.exceptions.RequestException as e:
logging.warning(f"Network error: {e}")
# Calculate exponential backoff with jitter
# attempt 0: ~1s, attempt 1: ~2s, attempt 2: ~4s, attempt 3: ~8s
sleep_time = (base_delay * (2 ** attempt)) + random.uniform(0.1, 0.5)
logging.info(f"Sleeping for {sleep_time:.2f} seconds before retry...")
time.sleep(sleep_time)
raise Exception("Max retries exceeded for HubSpot API request.")
# Usage example:
# headers = {"Authorization": "Bearer YOUR_PAT"}
# data = hubspot_request_with_retry("GET", "https://api.hubapi.com/crm/v3/objects/contacts", headers)Error Medic Editorial
A collective of Senior Site Reliability Engineers and Integration Architects dedicated to untangling the modern SaaS ecosystem. We provide battle-tested code and production-ready architectures.