intercom-rate-limits
'Handle Intercom API rate limits with backoff, queuing, and header monitoring.
Allowed Tools
Provided by Plugin
intercom-pack
Claude Code skill pack for Intercom (24 skills)
Installation
This skill is included in the intercom-pack plugin:
/plugin install intercom-pack@claude-code-plugins-plus
Click to copy
Instructions
Intercom Rate Limits
Overview
Intercom enforces rate limits per app and per workspace. Handle 429 errors
gracefully with exponential backoff, queue-based throttling, and proactive
header monitoring. This skill gives you five composable defenses — a retry
wrapper, a live monitor, a request queue, request batching, and metrics — so a
high-volume integration stays under the ceiling instead of spraying 429s.
The full, copy-paste TypeScript for all five lives in
references/implementation.md; this page carries
the limits, the header contract, and a lean skeleton so you can wire it up from
here and drill in for depth.
Rate Limit Tiers
| Scope | Limit | Notes |
|---|---|---|
| Private app | 10,000 req/min | Per app |
| Public app (OAuth) | 10,000 req/min | Per app |
| Workspace total | 25,000 req/min | Across all apps |
| Search endpoints | 1,000 req/min | /contacts/search, /conversations/search |
| Scroll endpoints | 100 req/min | Bulk data export |
Rate Limit Headers
Every response includes these headers — read them to throttle proactively:
X-RateLimit-Limit: <max requests per window>
X-RateLimit-Remaining: <remaining requests>
X-RateLimit-Reset: <unix timestamp when window resets>
Prerequisites
- An Intercom access token in
INTERCOMACCESSTOKEN(Developer Hub > Your App
> Authentication) — all requests below send it as Authorization: Bearer.
- The official SDK:
npm install intercom-client. - For queue-based throttling:
npm install p-queue. - Read an existing client wrapper before editing so you extend it rather
than duplicate it.
Instructions
Apply the defenses in order — each builds on the previous one. Use Write to
create a new intercom-rate-limit.ts module, or Edit to fold these into an
existing client wrapper.
- Wrap every call in header-aware retry. On
429, wait until
X-RateLimit-Reset; on 5xx, exponential backoff with jitter. Skeleton:
async function withRateLimitRetry<T>(op: () => Promise<T>): Promise<T> {
// On 429: delay = (X-RateLimit-Reset * 1000) - Date.now() + 1000
// On 5xx: delay = baseDelay * 2^attempt + jitter, capped at maxDelayMs
}
- Add a proactive monitor. Feed response headers into a monitor and call
waitIfNeeded() before firing when usage crosses ~90%, so you slow down
before a 429.
- Throttle with a queue. Route requests through a
p-queuecapped at
~150 req/s to keep bursts under the per-app ceiling.
- Batch to cut request count. Replace N individual lookups with OR-batched
contacts.search queries.
- Emit metrics. Log
remaining,usagepercent, andmsuntil_resetso
you can alert before saturation.
Full implementation for every step:
Output
Wiring these in yields three concrete artifacts in your integration:
- A resilient client wrapper — every call routed through
withRateLimitRetry + queuedRequest, so transient 429/5xx are absorbed
automatically instead of surfacing as failures.
- Proactive throttling — the monitor pauses outbound traffic before the
window is exhausted, converting hard 429s into short, controlled waits.
- Observability — structured
intercom.rate_limitmetrics (remaining,
usage percent, ms-until-reset) ready for dashboards and alerts.
Error Handling
| Scenario | Strategy | Implementation |
|---|---|---|
| 429 with reset header | Wait until reset | Parse X-RateLimit-Reset |
| 429 without headers | Exponential backoff | 1s, 2s, 4s, 8s, 16s |
| Approaching limit (>90%) | Proactive throttle | Check remaining before request |
| Bulk operations | Queue-based | p-queue with intervalCap |
| Multiple apps hitting workspace limit | Coordinate | Shared rate limit monitor |
Examples
Absorb a burst of contact lookups. Fan out hundreds of contacts.find
calls without tripping the limit by routing each through the queue + retry
wrapper:
const contacts = await Promise.all(
userIds.map(id =>
queuedRequest(() => client.contacts.find({ contactId: id }))
)
);
Precise wait on a 429. When a response carries X-RateLimit-Reset, wait
exactly until that epoch (plus a 1s buffer) instead of guessing a backoff — see
Step 1 in references/implementation.md.
Batch email lookups. Replace 100 single-contact requests with 10 OR-batched
searches — full findContactsByEmails helper in
Resources
- Rate Limiting
- Pagination
- p-queue
- Full implementation — the five defenses in copy-paste TypeScript
Next Steps
Rate limiting is one layer of a hardened Intercom integration. Once retries and
throttling are in place, pair them with intercom-common-errors for full
status-code triage, and intercom-security-basics for token scoping and
webhook-signature verification.