Burst and daily limits are different
HubSpot’s current guidance describes short-window limits commonly expressed as requests per ten seconds, with plan-dependent values. The exact table can change, so verify it before publishing or tuning a production worker. A daily limit is a separate account-level budget shared by activity that uses the relevant app or account allowance.
Do not treat all 429s as transient.
| Symptom | Likely cause | Correct response |
|---|
| 429 during a spike | Burst limit | Backoff and lower concurrency |
| 429 all day | Daily limit | Pause and defer work |
| One account fails | Account-specific usage | Throttle by account |
| One endpoint dominates | Inefficient workflow | Cache, batch, or redesign |
Inspect response evidence
Record endpoint, method, account ID alias, request timestamp, response status, Retry-After if provided, and relevant rate-limit headers. Redact tokens and personal data. Group the events by account and workflow so one busy customer does not look like a platform-wide failure.
If the connector hides headers, instrument the HTTP client or use a controlled direct request. Avoid making repeated production calls solely to discover a limit.
Implement bounded backoff
Use exponential backoff with jitter:
Honor Retry-After when HubSpot provides it. Cap attempts and move the job to a queue after the cap. Keep an idempotency key so the same create is not performed twice after a timeout.
Do not retry 400 validation errors unchanged. A 401 is credential-related, 403 is permission-related, and 404 may be an endpoint or record problem. Correct classification prevents unnecessary traffic.
delay = min(maxDelay, baseDelay * 2 ** attempt)
delay = delay + randomJitter()
Limit concurrency per account
A global worker limit is unsafe when several HubSpot accounts share the same process. Key the queue and limiter by account and credential. Reserve capacity for urgent webhook or user-triggered work rather than allowing a bulk import to consume every slot.
When a 429 rate rises, reduce concurrency gradually and observe recovery. A fixed sleep after every request may waste capacity; adaptive control is better when traffic varies.
Remove redundant calls
Cache property definitions and association metadata. Store external-to-HubSpot ID mappings. Use batch read or batch write endpoints where the current API supports them. Coalesce repeated updates to the same record during a short window if the business process allows.
Do not fetch a complete record three times for three downstream branches. Fetch once, normalize it, and share the result.
Bulk imports and replay
Split bulk jobs into chunks with checkpoints. Persist the last confirmed source row and HubSpot ID. On failure, replay only the uncertain chunk and deduplicate by external ID. Keep a dead-letter report for rows that fail for validation or permissions rather than retrying them as if they were throttled.
Test a partial outage: some rows succeed, the next rows receive 429, and the worker restarts. Your result should be recoverable without duplicate contacts or deals.
Add monitoring before the incident
Track requests per account, 429 count, Retry-After, queue age, calls per record, daily job completion, and success rate. Alert at rising throttling, not only after the queue is hours old. A dashboard should tell an operator whether to pause one job, one account, or all traffic.
Review new workflows for hidden polling and N-plus-one associations. These are common causes of quota growth after a feature launch.
The trap: adding a long fixed sleep globally. It can slow every customer while failing to protect the one account that exceeded its burst or daily budget.
A capacity-planning example
Imagine a job that processes 5,000 contacts and makes one property lookup, one company lookup, and one write for each row. The visible job is 5,000 records, but the API sees roughly 15,000 calls before retries. Caching property definitions, deduplicating companies, and persisting source-to-HubSpot IDs can remove most of that overhead without reducing business coverage.
Measure calls per record before and after each change. Also measure completion time and failure rate. A larger page size may reduce request count but increase response size and retry cost. The best setting completes reliably with headroom for interactive and webhook traffic.
Recovery after exhaustion
When evidence indicates a daily limit rather than a short burst, stop the bulk worker and preserve its checkpoint. Do not keep hammering the endpoint. Resume from the checkpoint with reduced concurrency and idempotency checks. If only one account is affected, keep other tenants running.
Design a quota-aware worker
Give every queued job an account key, priority, attempt count, and checkpoint. The limiter should decide whether the job can run for that account, not merely whether the application process has spare CPU. Keep interactive requests ahead of a large reconciliation job when the business requires quick user feedback.
Use a circuit breaker after repeated 429 responses. Open the circuit for a bounded period, preserve the queue, and expose the reason to operators. When the circuit closes, start with lower concurrency and increase gradually. Do not release every waiting request at once because that recreates the burst.
For writes, retain the source ID and deterministic operation key. A timeout can mean the server accepted a request, so recovery must check state before replay. For reads, allow a small overlap and deduplicate by HubSpot ID. These safeguards keep rate-limit recovery from becoming a duplication incident.
Review the worker after adding polling, associations, imports, or property lookups. These features can multiply calls even when the number of visible workflows has not changed.
Add a monthly calls-per-record review to catch gradual quota growth.
Add a monthly calls-per-record review to catch gradual quota growth.
A useful 429 evidence record
For every incident sample, retain the portal ID, app identity, endpoint, request rate, concurrency, `Retry-After`, `policyName`, and whether the request used OAuth or a private app. Compare the first response with the first response after throttling. This lets an operator prove that the limiter reduced pressure instead of merely waiting for an unrelated recovery. Never use a copied limit number as the limiter’s only configuration.
Where these facts come from