ADACTION
DOCK

ACTIONDOCK FIELD NOTES

AI Agent API Rate Limits: How to Handle 429 Without Duplicate Writes

Handle HTTP 429 in AI-agent workflows: identify which API rate-limited the request, honor Retry-After, and recover approved writes without duplicate effects.

A cluster of glass request tiles waits at a metering aperture while evenly spaced tiles pass through.

Start by identifying which API returned 429

When an AI agent encounters HTTP 429 Too Many Requests, pause requests to the system that imposed the limit, honor its Retry-After guidance when available, and reduce the rate of new calls. Before retrying a write, establish whether you are repeating a submission that was refused or creating another attempt against the external provider.

In ActionDock, these are different cases. An HTTP 429 with error.code: rate_limit_exceeded from the ActionDock submission endpoint rejects that request before it creates a job. Wait, then repeat the same action, input, and idempotency key. If an approved write instead receives provider HTTP 429, ActionDock records a terminal failed job with the provider response; it does not automatically resend the write. A justified new provider attempt needs a new proposal, a new idempotency key, and a fresh owner approval.

That distinction prevents a useful recovery loop from becoming a duplicate-write loop. For the underlying submission contract, see AI-agent API retries and idempotency.

What HTTP 429 tells you

RFC 6585, section 4 defines 429 as rate limiting: too many requests in a given period. A response may include Retry-After, but the header is optional. The standard does not prescribe whether a service counts requests by account, credential, resource, server, or some combination.

Do not assume one universal requests-per-minute allowance. Several agents can share the same provider account, and different endpoints may have different limits. The useful evidence is the response from the service you called, its official throttling documentation, and the current job state.

For example, GitHub’s REST guidance describes retry-after, a separate reset time when remaining allowance is zero, and increasing delays with a defined retry limit for continuing secondary-limit errors. Those are GitHub-specific rules, not ActionDock defaults or a promised integration.

Read the response at the correct layer

Observation Meaning in the current ActionDock implementation Next action
Action submission returns HTTP 429 with rate_limit_exceeded This attempt was stopped by ActionDock’s request limiter before the submission handler ran Wait for ActionDock’s Retry-After, then repeat the exact submission with its original key
Job read returns HTTP 200, but the job is failed and output.providerResponse.statusCode is 429 The approved external write received a provider rejection Inspect the provider response and throttling contract; consider a newly approved attempt only when justified
A poll of an existing job returns HTTP 429 The read request was throttled; it does not report a new outcome for that job Keep the job ID, slow polling, and read that same job later
The write job is execution_unknown after a timeout, transport failure, or uncertain response The external effect cannot be established from ActionDock’s record Reconcile provider state before submitting another write

An outer HTTP status and a stored provider status answer different questions. HTTP 200 from GET /v1/jobs/{id} means the job record was returned successfully; it does not mean the external operation succeeded. Inspect status and the recorded response as well. The agent integration guide documents authenticated job reads and the approved API write workflow explains the approval boundary.

When ActionDock rate-limits the submission

ActionDock’s API-key-authenticated request limiter is shared by requests within a workspace. It runs before the action handler and returns a positive integer Retry-After in seconds. This illustrative response uses a sample wait value, not a fixed published allowance:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
Cache-Control: no-store
Content-Type: application/json

{"error":{"code":"rate_limit_exceeded","message":"Rate limit exceeded"}}

Persist the original idempotency key and input before the first submission. After this response, wait at least the indicated delay and repeat those same values. If an earlier attempt timed out after creating a job, the repeated key can recover that existing job. The latest 429 does not prove that no earlier attempt succeeded in creating one.

Once you have a job ID, follow that job rather than repeatedly submitting the action. awaiting_approval means the owner still has a decision to make; queued or running means the existing job is progressing. A same-key submission retrieves the existing job and never becomes an instruction to rerun its provider call.

When the provider rate-limits an approved write

Suppose an agent proposes a PATCH to update an inventory record, the owner approves it, and the external API returns 429. ActionDock classifies that provider 4xx response as a rejected write, records failed, and preserves the approved preview and provider response. The relevant subset of the returned job looks like this; other fields are omitted:

{
  "status": "failed",
  "output": {
    "providerResponse": {
      "statusCode": 429,
      "ok": false
    }
  }
}

The current receipt retains the response body and three selected headers: contentType, etag, and lastModified. It does not retain or forward the provider’s Retry-After or other rate-limit headers. Do not write client logic that expects output.providerResponse.headers.retryAfter. Consult the provider’s official recovery rules and available provider records; do not invent a reset time from a missing field.

Repeating the original idempotency key returns this failed job. If another provider attempt is appropriate, check that the intended change is still current, wait according to the provider’s rules, then submit a fresh proposal with a new idempotency key for owner approval. The new attempt may itself be throttled, and an approval is not an exemption from the provider’s limits.

For reads, there is another detail: integration.fetch can complete as succeeded while its output contains statusCode: 429 and ok: false. The read action received a response; the requested provider data was not successfully fetched. Check the output before using it to prepare a write.

Parse Retry-After before choosing a delay

RFC 9110, section 10.2.3 permits two forms:

Retry-After: 30
Retry-After: Tue, 06 Oct 2026 01:05:00 GMT

The first means seconds after receiving the response. The second is an HTTP date: calculate the remaining time using a reliable clock. A general API client should parse both forms, even though ActionDock’s own limiter currently emits only integer seconds. Treat a missing or malformed value as a reason to use the service’s documented fallback, not as permission for an immediate loop.

For retryable operations, use a bounded scheduling policy. These are recommendations for the calling workflow, not built-in ActionDock provider retries:

  1. Respect a valid server wait time. If it exceeds your workflow’s allowed waiting budget, defer or stop; do not shorten it to fit a local backoff cap.
  2. If the service supplies no usable wait time, use increasing delays with a maximum interval and small random jitter, chosen for that service and workload.
  3. Limit both the number of attempts and total elapsed time. Record why the workflow stopped so an operator can act.
  4. Keep one layer responsible for retries. Inspect SDK defaults before adding another loop around them.
  5. Coordinate calls that share the same limiting scope. Independent agents retrying together can recreate the original burst.

Google Cloud Storage’s retry guidance recommends backoff with jitter only when both response and idempotency criteria are met, and warns about unlimited or layered retries. Its specific client behavior does not transfer automatically to another API.

Waiting answers when another request may be attempted. It does not establish whether a write is safe to repeat. If the actual job is execution_unknown, use the provider reconciliation process in the retry guide, regardless of a previous throttling response. For a time-sensitive proposal, also enforce a latest-dispatch deadline; a long rate-limit delay can make the business instruction obsolete.

Reduce the calls that trigger throttling

Measure submissions and status polls separately. Slow or coordinate redundant polling, retain job IDs, and use ActionDock’s signed terminal callbacks when configured. Verify callback signatures before using a notification to resume the agent; the callback verification guide covers that contract.

Track which boundary returned 429, the job ID if one exists, the observed wait guidance, and whether recovery reused an existing submission or created a newly approved attempt. A useful metric is the number of new provider attempts per logical operation, not just the number of HTTP calls your agent made. That gives an operator enough evidence to fix request pacing without hiding failed writes or uncertain outcomes.