Freshworks REST APIs throttle by tenant plan. A typical Freshdesk Pro tenant gets 700 calls per minute, Enterprise gets more. Hit the cap and you get HTTP 429 with a Retry-After header. The right architecture treats rate limits as a permanent constraint, not an edge case.
Read the headers, every response
Every API response includes X-RateLimit-Remaining and X-RateLimit-Total. Log them. When remaining drops below 20% of total, slow your concurrency.
const remaining = res.headers["x-ratelimit-remaining"];
if (remaining < 0.2 * total) await sleep(2000);
Backoff that respects Retry-After
When you hit 429, use the server-provided Retry-After value. Do not hand-roll exponential backoff for rate limits — that under-uses your quota.
if (res.status === 429) {
const wait = parseInt(res.headers["retry-after"], 10) * 1000;
await sleep(wait);
return retry(req);
}
Batch where the API allows
Bulk endpoints exist for tickets, contacts, and notes. One batch call counts as one against the limit while updating up to 100 records.
POST /api/v2/tickets/bulk_update
{
"ids": [1, 2, 3, 4],
"properties": { "status": 5 }
}
Cache hot lookups
Group names, agent details, and ticket field metadata change rarely. Cache them in Redis with 1-hour TTL. Most integration call volume is the same five lookups in a loop.
Use webhooks instead of polling
Polling for ticket changes burns quota fast. Switch to webhook-driven incremental updates and only fall back to polling for reconciliation. A nightly reconciliation job catches missed events.
Per-app vs per-tenant limits
If you build a Marketplace app, your app shares the tenant’s limit with everyone else. Be a good citizen: cache aggressively, batch always, document your call patterns.
Monitoring
Emit rate-limit headroom as a metric to your observability stack. Alert when sustained headroom is below 30% for an hour — that means you are one outage retry storm away from a 429 cascade.
What to do this week
Add header logging to your Freshworks client, switch hot lookups to a 1-hour cache, audit any polling loops for webhook replacement, and emit headroom as a Datadog metric.