Skip to content
Dark blue and ivory tokens queued on a curved rail beneath a brushed metal gateway with cyan edge lighting.
Outreach operations4 min read

API Rate Limits for Agency Bots

Handle 429 responses with coordinated queues, correct Retry-After parsing, bounded backoff, and a separate recovery path for uncertain write outcomes.

An agency bot starts syncing records for several clients. One service returns 429 Too Many Requests, and every worker immediately tries again. The queue now contains both the original work and a growing pile of retries.

A better response is to slow the affected work, preserve its state, and decide which actions are safe to repeat. This guide is an integration design checklist, not a statement of AllProfiles quotas or an automatic-retry feature.

1. Read the response without guessing the limit

HTTP 429 indicates too many requests within a period. The response may include Retry-After, but that header is optional. The standard does not define one universal counting rule or limit scope: a service can count by resource, credentials, server, or another mechanism. RFC 6585: 429 Too Many Requests

Check the provider's current documentation and the actual error details. Do not infer a requests-per-minute allowance from one failed request, or assume that every 429 blocks the whole client account.

Microsoft Graph illustrates why scope matters: throttling can depend on request type, application, and tenant. Writes can be restricted while reads remain available, and broader throttling is also possible. Treat this as a Graph-specific example, not a rule for every API. Microsoft Graph throttling guidance

2. Queue the work and coordinate the pause

Give each outbound job an identity and a recorded state before dispatch. A practical record includes the target service, client reference, intended operation, attempt count, next eligible time, and latest outcome. Keep tokens and full private message bodies out of ordinary operational logs.

When workers share a documented limit, coordinate their pause. Sleeping one failed worker does little if the others continue sending equivalent requests into the same limit. Conversely, do not stop an unrelated integration unless its work shares that limit or another documented constraint.

Illustrative example: several workers use one integration to sync client records. A shared scheduler delays affected jobs and retains their order and identifiers. The operator can see which jobs are waiting instead of restarting the entire sync. This describes a possible design, not an existing AllProfiles queue API.

3. Respect both forms of Retry-After

Retry-After accepts a nonnegative integer number of seconds after receipt or an HTTP date. RFC 9110: Retry-After

Illustrative header: Retry-After: 30 means wait 30 seconds.

Store the earliest retry time. Never let jitter or a local delay cap move it earlier. If the job expires first, stop it explicitly. Handle malformed values under the provider's recovery guidance, rather than retrying immediately.

4. Bound retries and choose one policy owner

When no usable waiting instruction is available, follow the provider's documented recovery guidance. Graph recommends exponential backoff when its response lacks Retry-After; that recommendation does not establish another service's contract.

Amazon's engineering guidance explains why retries need restraint: they add load, capped exponential backoff increases spacing up to a ceiling, and jitter spreads attempts that would otherwise happen together. Attempts still need a limit. Retries at multiple layers can multiply the downstream work. AWS: Timeouts, retries, and backoff with jitter

Choose which layer owns retries and inspect the HTTP client or SDK before adding another loop. Define an attempt budget, an overall deadline, and the state reached when either is exhausted. Increasing the delay forever is not a complete recovery policy.

5. Separate a delayed request from an unknown write outcome

After a write times out, its outcome can be unknown. HTTP discourages automatic retries of non-idempotent requests unless their actual semantics are known to be idempotent or the original is known not to have been applied. RFC 9110: Idempotent methods

Reconcile against provider records or use its supported idempotency mechanism. Keep the original job reference and route unresolved outcomes to an operator.

The following is an illustrative decision table, not a universal provider policy.

Observed resultNext decision
429 with valid Retry-AfterRespect the wait, then retry only if the operation is safe to repeat
429 without a usable wait instructionFollow provider guidance and the configured bounded backoff policy
Timeout after a write was dispatchedMark the outcome unknown and reconcile before repeating
Retry budget or deadline exhaustedPreserve the job and expose a clear review or failure state

6. Test recovery before relying on it

Use controlled test data to exercise seconds and date headers, absent or malformed values, simultaneous workers, and a restart while work is waiting. Confirm that the next eligible time survives the restart and that canceled or expired work is not resumed accidentally.

Inspect individual operations as well as the outer response. In Graph JSON batching, the envelope can return HTTP 200 while individual requests return 429. Graph SDKs also include retry handlers, so account for their behavior in your total retry policy. Graph throttling and batching guidance

Finally, test an uncertain write outcome and an exhausted retry budget. The operator should be able to distinguish waiting, completed, failed, and unknown jobs. That distinction is more useful than a spinner that restarts the same action without explaining what happened.