---
title: "How should an AI agent handle email API rate limits?"
description: "What a 429 from an email API means for an AI agent, how to read Retry-After and the RateLimit headers, and how to retry without sending anything twice."
url: https://www.unitpost.com/blog/agents/ai-agent-email-rate-limits
section: Blog
updated: 2026-11-02
---
# How should an AI agent handle email API rate limits?

When an email API answers 429, the agent should wait the number of seconds in Retry-After and retry the same request with the same idempotency key, so the retry can never send twice. On Unitpost the limit is per workspace, shared by every key, and each response carries RateLimit-Limit, -Remaining and -Reset headers. Batch sends of up to 100 emails use one request instead of a hundred.

## How should an AI agent handle email API rate limits?

> What a 429 from an email API means for an AI agent, how to read Retry-After and the RateLimit headers, and how to retry without sending anything twice.

An agent doesn't get tired of retrying. Given a 429 and no rules, it either gives up on a send that would have worked a second later, or hammers the API until every key in the workspace is throttled. Both are fixable with three habits.

### What do the rate limit headers say?

Every Unitpost API response carries the workspace's budget:

- `RateLimit-Limit`: requests allowed in the window;
- `RateLimit-Remaining`: what's left in it;
- `RateLimit-Reset`: when it refills, as a Unix time in seconds. Subtract the current time to get the wait; on a `429`, `Retry-After` already gives it in seconds.

A request over the budget gets `429` with `Retry-After`, in seconds. The budget is per workspace and shared by every key, so an agent's burst also slows the app that sends your receipts. That's a reason to give agents the patience rules below, not a separate key for speed.

### How should the agent retry?

- Wait `Retry-After`, then retry once with the same body and the same `Idempotency-Key`. The key makes the retry safe: if the first request did get through, the retry returns the original result (with `Idempotent-Replayed: true`) instead of sending again. Keys last 24 hours.
- Back off on `5xx` and network errors (1, 2, 4, 8 seconds), still with the same key, and give up after a few tries.
- Don't retry other `4xx`. A `422` names the field that's wrong, a `403` names the missing permission, and a `409` means the same key was sent with a different body. Report those to the person.

```bash
async function sendWithRetry(body: object, key: string, tries = 4) {
  for (let i = 0; i < tries; i++) {
    const res = await fetch("https://www.unitpost.com/api/v1/email", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.UNITPOST_API_KEY}`,
        "Content-Type": "application/json",
        "User-Agent": "my-agent/1.0",
        "Idempotency-Key": key, // the same key on every try
      },
      body: JSON.stringify(body),
    });
    if (res.status === 429) {
      await new Promise((r) => setTimeout(r, Number(res.headers.get("Retry-After") ?? 1) * 1000));
      continue;
    }
    if (res.status >= 500) {
      await new Promise((r) => setTimeout(r, 2 ** i * 1000));
      continue;
    }
    return res.json(); // success, or a 4xx to report, not retry
  }
  throw new Error("Gave up after retries");
}
```

> **Make the key once per message:** Create the idempotency key when the agent decides to send a message, and pass it into every retry. A key generated inside the retry loop, or inside the tool, changes each time and a lost response becomes a second email.

### How do you send less often?

- Batch. `POST /email/batch` sends up to 100 emails in one request, with one idempotency key for the batch. An agent writing 300 personalized notes makes three requests, not 300.
- Schedule. A batch or a single send can carry `scheduled_at`, so the agent finishes its work in one pass and the sends go out at the right time (scheduling (/blog/agents/schedule-email-sms-from-ai-agent)).
- Campaigns for one-to-many. Marketing to a segment is one campaign call, not a send per contact, and a marketing send to more than one recipient on the transactional endpoint is refused anyway.

### What should the agent tell a person?

When it gives up, it should say what it tried, the last status and the `Retry-After` it saw, and that nothing was sent twice. A message like "Rate limited: 3 of 40 emails are still waiting; retrying them at 14:05 with the same keys" lets a person decide whether to wait or ask for a higher limit.

## FAQ

### What is Unitpost's API rate limit?

10 requests a second per workspace by default, shared across all of its keys. Support can raise it for a workspace. A few write endpoints also apply abuse throttles on top, which answer 429 with Retry-After.

### Should an agent retry every failed request?

No. Retry a 429 after Retry-After, and a 5xx or a network error with backoff, always with the same idempotency key. A 4xx other than 429 means the request is wrong, so an identical retry fails again: fix the request or stop and report it.

### How does an MCP client see a rate limit?

The tool result carries the API's error object plus a hint with retry_after_seconds. The server's instructions tell the agent to wait that long and not to resend an identical call that failed with another 4xx.

## Related

- [How to have an AI agent reply to inbound email on your own domain](https://www.unitpost.com/blog/agents/ai-agent-reply-to-inbound-email): Point MX at a subdomain, verify the signed email.received webhook, dedupe, then send one threaded reply with an idempotency key from the inbound id.
- [How to send appointment reminders by SMS from an AI agent](https://www.unitpost.com/blog/agents/ai-agent-sms-appointment-reminders): Let an AI agent text appointment reminders: consent on record, a registered use case, a fixed template, the local hour, and one idempotency key per reminder.
- [How to handle STOP and HELP when an AI agent sends texts](https://www.unitpost.com/blog/agents/ai-agent-sms-stop-help): Keep STOP and HELP away from the model: handle keywords first, send one confirmation, honor plain-language opt-outs, log consent, check before every send.
