Error Medic

Troubleshooting HubSpot API Rate Limit (429) and Timeout (504) Errors

Fix HubSpot API rate limit (429) and timeout (504) errors. Learn to implement exponential backoff, request batching, webhook architecture, and payload optimizat

Last updated:
Last verified:
1,649 words
Key Takeaways
  • HubSpot enforces two primary rate limits: a 10-second rolling burst limit (100-150 requests) and a daily total limit (250k-500k requests).
  • HTTP 429 Too Many Requests errors trigger when either limit is exceeded, accompanied by an errorType of RATE_LIMIT or TEN_SECONDLY_ROLLING.
  • HTTP 504 Gateway Timeout and 502 Bad Gateway errors occur during unoptimized Search API queries, massive batch operations, or large CRM associations.
  • Implement exponential backoff with jitter on all API clients to gracefully handle 10-second rolling rate limits.
  • Migrate from sequential individual requests to HubSpot's Batch and Bulk API endpoints to drastically reduce network overhead and API call consumption.
  • Reduce timeout frequency by requesting only specific properties (e.g., ?properties=email,firstname) rather than pulling entire contact objects.
Rate Limit and Timeout Mitigation Strategies Compared
Mitigation StrategyBest Used ForImplementation TimeRisk/Complexity
Exponential Backoff & RetriesHandling 10-second burst limits and transient 50x network dropsLow (1-2 hours)Low
Batch/Bulk API EndpointsInitial data syncing, massive CRM imports, and cron jobsMedium (1-2 days)Medium
Payload/Query OptimizationFixing frequent 504 Gateway Timeouts on the Search APILow (Hours)Low
Webhooks via WorkflowsReplacing polling architectures for real-time system syncsHigh (Days/Weeks)High
Upgrading HubSpot Tier/API Add-onConsistently hitting the hard daily 24-hour request limitInstantFinancial Cost

Understanding the Error

When scaling integrations with the HubSpot API, engineers inevitably encounter friction in the form of rate limits and timeouts. Because HubSpot operates as a multi-tenant SaaS platform, it employs aggressive throttling and load balancers that will terminate long-running queries to protect the infrastructure. Understanding the exact nature of these errors is the first step toward building resilient integrations.

The Anatomy of a HubSpot 429 Rate Limit Error

HubSpot categorizes rate limits into two distinct buckets:

  1. The 10-Second Rolling Limit (Burst): Depending on your authentication method (OAuth vs. Private App) and HubSpot tier (Free vs. Enterprise), you are typically limited to 100 or 150 requests per 10 seconds. Exceeding this triggers a localized timeout.
  2. The Daily Limit: This ranges from 250,000 to 500,000 requests per day (resetting at midnight EST) depending on your subscription tier and whether you have purchased the API limit increase add-on.

When you hit a limit, HubSpot returns an HTTP 429 Too Many Requests status code. The JSON response body looks exactly like this:

{
  "status": "error",
  "message": "You have reached your ten secondly limit.",
  "errorType": "RATE_LIMIT",
  "correlationId": "1a2b3c4d-5e6f-7a8b-9c0d-1a2b3c4d5e6f",
  "policyName": "TEN_SECONDLY_ROLLING"
}

If you exhaust your daily limit, the policyName will read DAILY instead of TEN_SECONDLY_ROLLING.

The Anatomy of a HubSpot 504 Gateway Timeout

Unlike 429s, which are policy-driven, 504 Gateway Timeout and 502 Bad Gateway errors are infrastructure-driven. HubSpot's API gateway will terminate any HTTP request that takes longer than 10-15 seconds to execute. This almost exclusively happens when:

  • Querying the CRM Search API with overly complex filters (e.g., heavily nested OR conditions on non-indexed custom properties).
  • Fetching contacts with massive association histories (e.g., a contact associated with thousands of page views, emails, and deals).
  • Using Batch endpoints with maximum limits (e.g., trying to pull 100 records at once where each record has 500 custom properties).

Step 1: Diagnose the Bottleneck

Before refactoring your infrastructure, you need to determine which limit you are hitting or why you are timing out. HubSpot includes rate limit data in the HTTP response headers of every successful (and failed) API call.

Inspect your API client logs or use curl to view these headers:

curl -i -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  "https://api.hubapi.com/crm/v3/objects/contacts?limit=1"

Look for the following headers in the response:

  • X-HubSpot-RateLimit-Daily: Your total allowed daily requests.
  • X-HubSpot-RateLimit-Daily-Remaining: The number of daily requests you have left.
  • X-HubSpot-RateLimit-Interval-Milliseconds: The rolling window (usually 10000ms).
  • X-HubSpot-RateLimit-Max: The max requests allowed in that 10-second window.
  • X-HubSpot-RateLimit-Remaining: How many requests you have left in the current 10-second window.

If X-HubSpot-RateLimit-Remaining frequently hits 0, you have a burst problem. If X-HubSpot-RateLimit-Daily-Remaining hits 0 by mid-afternoon, you have a volume problem. If neither is 0 but you are receiving 504s, you have a payload/query optimization problem.

Step 2: Fix 429 Errors (Rate Limits)

Strategy A: Implement Exponential Backoff with Jitter

If you are hitting the 10-second limit, your immediate fix should be introducing a robust retry mechanism. Never wrap an API call in a simple while/sleep(1) loop, as a sudden surge of retrying clients will immediately trigger the rate limit again (the Thundering Herd problem).

Instead, use Exponential Backoff with Jitter. This means the client waits progressively longer between each retry (1s, 2s, 4s, 8s) and adds a random millisecond delay (jitter) to stagger requests from concurrent workers.

Strategy B: Migrate to Batch Endpoints

The most common architectural flaw in HubSpot integrations is using the standard CRUD endpoints in loops. For example, updating 1,000 contacts by calling PATCH /crm/v3/objects/contacts/{contactId} inside a for loop will consume 1,000 API calls and trigger a 429 error within 2 seconds.

Instead, use the Batch API (POST /crm/v3/objects/contacts/batch/update). This endpoint accepts an array of up to 100 contact objects. Updating 1,000 contacts now takes exactly 10 API calls, virtually eliminating the risk of a 10-second rate limit.

Strategy C: Replace Polling with Webhooks

If your daily limit is exhausted because your application runs a cron job every 5 minutes asking "Have any deals closed recently?", you are wasting thousands of API calls on empty responses.

Shift your architecture to an event-driven model. Create a HubSpot Workflow that triggers when a Deal stage changes to 'Closed Won'. Have that workflow send a Webhook POST directly to your application's endpoint. This reduces API consumption for data synchronization to zero.

Step 3: Fix 504/502 Errors (Timeouts)

Strategy A: Optimize Search API Queries

HubSpot's Search API (POST /crm/v3/objects/contacts/search) is powerful but prone to timeouts if abused. To fix timeouts here:

  • Filter on Indexed Properties: Filtering by standard properties like email, hs_object_id, or createdate is lightning fast. Filtering by string-matching on massive custom text-area properties forces full table scans and leads to 504s.
  • Reduce Payload Weight: By default, HubSpot returns a standard set of properties. If you only need the Contact ID and Email, explicitly request them. Shrinking the JSON payload drastically reduces the serialization time on HubSpot's servers.
    • Bad: Requesting a contact and receiving 150 properties back.
    • Good: Sending "properties": ["email", "firstname"] in your Search API JSON body.
Strategy B: Decrease Batch Sizes

While Batch endpoints (up to 100 objects) are great for avoiding rate limits, they can occasionally trigger 504 timeouts if the objects are incredibly complex (e.g., thousands of timeline events or associations). If a batch of 100 consistently times out, throttle your batch size down to 50, or even 25. It will cost slightly more API calls, but it guarantees network stability.

Frequently Asked Questions

python
# Recommended Python implementation using requests and urllib3
# This script demonstrates robust error handling for HubSpot API rate limits
# utilizing automatic exponential backoff and jitter.

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import json

def get_hubspot_session():
    session = requests.Session()
    
    # Configure the retry strategy
    # backoff_factor=1 means sleep for [0.5s, 1s, 2s, 4s, 8s...] between retries
    retry_strategy = Retry(
        total=5, # Maximum number of retries
        status_forcelist=[429, 500, 502, 503, 504],
        allowed_methods=["HEAD", "GET", "PUT", "POST", "PATCH", "DELETE"],
        backoff_factor=1,
        respect_retry_after_header=True # Crucial for 429s if HubSpot includes it
    )
    
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)
    session.mount("http://", adapter)
    
    # Setup default headers
    session.headers.update({
        "Authorization": "Bearer YOUR_HUBSPOT_PRIVATE_APP_TOKEN",
        "Content-Type": "application/json"
    })
    
    return session

# Example Usage: Safe API call that automatically handles limits/timeouts
if __name__ == "__main__":
    http = get_hubspot_session()
    
    url = "https://api.hubapi.com/crm/v3/objects/contacts/search"
    payload = {
        "filterGroups": [
            {
                "filters": [
                    {
                        "propertyName": "createdate",
                        "operator": "GTE",
                        "value": "1672531200000" # Jan 1, 2023
                    }
                ]
            }
        ],
        # OPTIMIZATION: Only ask for what you need to prevent 504 Timeouts
        "properties": ["email", "firstname", "lastname"], 
        "limit": 50
    }

    try:
        print("Initiating HubSpot Search API Request...")
        response = http.post(url, json=payload, timeout=15)
        response.raise_for_status() # Raises an error if final retry fails
        
        data = response.json()
        print(f"Success! Retrieved {len(data.get('results', []))} contacts.")
        
        # Optional: Log the rate limit headers to monitor capacity
        limits_remaining = response.headers.get('X-HubSpot-RateLimit-Remaining', 'N/A')
        print(f"10-Second Rate Limit Remaining: {limits_remaining}")
        
    except requests.exceptions.RetryError as e:
        print("FATAL: Max retries exceeded. The API is consistently blocking or timing out.")
    except requests.exceptions.ReadTimeout as e:
        print("FATAL: 504 Gateway Timeout. The query payload is too large or complex.")
    except requests.exceptions.HTTPError as e:
        print(f"FATAL: HTTP Error occurred: {e}")
E

Error Medic Editorial

Our SRE and DevOps experts provide battle-tested solutions for enterprise infrastructure, API integrations, and scalable automation.

Sources

Related Guides