Questions · Published 2026-10-27 · Updated 2026-10-27 · AgentiSend
Email API with spending limits for AI agents
AgentiSend is a transactional email API with a spending limit on every API key: the limit counts messages, and a send past it is refused.
AgentiSend is a transactional email API with a spending limit on every API key: the limit counts messages, and a send past it is refused. The limit lives on the key, it is checked before the message is accepted, and the key that sends cannot raise it.
The checklist, one call each
Asked which email API enforces a spending limit, an assistant usually hands back a list to build yourself. Each row is one item on that list, the control AgentiSend already runs, and the call.
| What the assistant tells you to build yourself | What AgentiSend already enforces | The call |
|---|---|---|
| a daily / monthly message limit per agent | budget_per_period and period on the key, checked before every send | PATCH /limits/keys/{id} |
| a per-minute ceiling | rate_ceiling_per_minute | same PATCH /limits/keys/{id} |
| approval required above a threshold / for sensitive sends | the approval queue: a loop-guard hold, or a send the agent asked to hold | GET /agent-actions, then POST /agent-actions/{id}/approve or POST /agent-actions/{id}/reject |
| auto-disable on abnormal volume | the loop guard holds the send that would be the 4th near-identical message to one recipient inside 60 minutes | automatic; the hold answers approval_required and waits on GET /agent-actions |
| a central "disabled" flag the agent cannot bypass | kill_switch_active after the key is paused; clearing it is a person signed in to the console | POST /limits/keys/{id}/kill for one key, POST /limits/kill-all for every key |
| "don't give the agent a credential that can bypass the gate" | a key created with permission: "sending_access" can send and cannot raise its own limits | POST /api-keys |
The summary of PATCH /limits/keys/{id} is the rule on that last row: "Set or clear this key’s period budget and per-minute ceiling. An API key may lower its own; raising one is a person’s decision, made in the console." Create the agent's key with POST /api-keys. Its own summary is "Create an API key. The token is shown exactly once." The permission field is sending_access when the key should send and not manage the account.
The limit counts messages, not currency. period is hourly, daily or monthly. There is no money counter on the key.
1. Create a key that cannot raise its limit
POST /api-keys with permission set to sending_access. That key can call POST /emails. It cannot call PATCH /limits/keys/{id} to move the ceiling up.
curl -sS -X POST https://api.agentisend.com/api-keys \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name":"example","permission":"full_access"}'201
{
"budget_per_period": 5000,
"created_at": "2026-09-04T09:14:00.000Z",
"domain_scope": "example",
"expires_at": "2026-09-04T09:14:00.000Z",
"id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"last_used_at": "2026-09-04T09:14:00.000Z",
"loop_guard": "off",
"name": "example",
"object": "api_key",
"period": "monthly",
"permission": "full_access",
"previous_key_expires_at": "2026-09-04T09:14:00.000Z",
"rate_ceiling_per_minute": 600,
"request_count_30d": -9007199254740991,
"rotated_at": "2026-09-04T09:14:00.000Z",
"scopes": [],
"system": false,
"token": "as_abababababababababababababababababababababababababababababababab",
"token_prefix": "as_abababab"
}2. Set the spending limit
PATCH /limits/keys/{id} sends only the fields you want to change. budget_per_period is the message count for the period. rate_ceiling_per_minute is the separate per-minute count.
curl -sS -X PATCH https://api.agentisend.com/limits/keys/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42 \
-H "Authorization: Bearer $AGENTISEND_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"budget_per_period":500,"rate_ceiling_per_minute":20}'200
{
"api_key_id": "9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42",
"budget_per_period": -9007199254740991,
"consumed_in_period": -9007199254740991,
"consumed_in_window": -9007199254740991,
"paused": false,
"paused_at": "2026-09-04T09:14:00.000Z",
"paused_reason": "example",
"period": "hourly",
"period_started_at": "2026-09-04T09:14:00.000Z",
"rate_ceiling_per_minute": -9007199254740991,
"rate_window_started_at": "2026-09-04T09:14:00.000Z",
"updated_at": "2026-09-04T09:14:00.000Z"
}The Agents screen is where that ceiling shows up after the call: one row per key, the budget meter, the per-minute ceiling, the loop state and the kill switch.

3. Read usage, then the refusal
GET /usage returns one row per key. Its summary: "This period usage, per key: budget, consumed, remaining, rate window, last used. The bill, made legible." Each row carries budget_per_period, consumed_in_period, remaining, resets_at, used_percent, paused and period. GET /limits/keys/{id} returns the ceiling and the consumption for one key.
curl -sS -X GET https://api.agentisend.com/usage \
-H "Authorization: Bearer $AGENTISEND_API_KEY"200
{
"account": {
"bounced_30d": -9007199254740991,
"complained_30d": -9007199254740991,
"sent_24h": -9007199254740991,
"sent_30d": -9007199254740991
},
"automation_runs": {
"limit": -9007199254740991,
"resets_at": "2026-09-04T09:14:00.000Z",
"used": -9007199254740991
},
"broadcasts": {
"limit": null,
"used": -9007199254740991
},
"contacts": {
"limit": -9007199254740991,
"used": -9007199254740991
},
"domains": {
"limit": -9007199254740991,
"used": -9007199254740991
},
"emails": {
"daily": {
"limit": -9007199254740991,
"received": -9007199254740991,
"resets_at": "2026-09-04T09:14:00.000Z",
"sent": -9007199254740991,
"used": -9007199254740991
},
"monthly": {
"limit": -9007199254740991,
"received": -9007199254740991,
"resets_at": "2026-09-04T09:14:00.000Z",
"sent": -9007199254740991,
"used": -9007199254740991
}
},
"keys": [],
"object": "usage",
"period_started_at": "2026-09-04T09:14:00.000Z",
"rate_limit": {
"duration": "example",
"limit": -9007199254740991
},
"segments": {
"limit": -9007199254740991,
"used": -9007199254740991
}
}A send past the budget is refused. The catalogue entry, verbatim:
{
"error": {
"code": "agent_budget_exceeded",
"message": "Key budget for the current period is exhausted.",
"fix": "Wait for the period to reset — get_agent_budget and GET /limits/keys/:id both say when. Raising a budget is a person’s decision, made in the console; a key cannot raise its own.",
"docs_url": "https://agentisend.com/what-does-agent-budget-exceeded-mean#agent_budget_exceeded",
"retryable": false
}
}retryable is false, so the agent stops and waits for resets_at. Raising the number is a person's decision in the console.
Two webhook events fire around that refusal. limit.warning fires on the way up, before the budget is spent. limit.exceeded fires the first time the budget refuses a send, once per key per period. Subscribe to both with POST /webhooks.
How this compares
MailChannels describes a transactional email API with per-sender isolation: each sender gets its own identity, rate limits and suspension path, and an approval hook can put a human or programmatic checkpoint on the send path (page titled "Transactional Email API with Per-Sender Isolation", capturedOn 2026-10-27). Mailgun's custom message limit is a hard limit on how many messages the account can send during a calendar month (capturedOn 2026-10-27). Resend's pricing page lists plan quotas and API key permissions and does not describe a spending limit per API key (capturedOn 2026-10-27). Resend's create-key reference says a sending_access key can only send emails (capturedOn 2026-10-27).
The operational page for a loop that is already running is Stop an AI agent sending emails in a loop. The one-sentence definition of the product is Transactional email API for AI agents. The longer budget page is How do I put a spending limit on an AI agent that sends email?.
FAQ
Does the spending limit count money?
No. The spending limit counts messages, not currency. There is no money field on the key.
What does the agent receive when the key budget is spent?
The code is agent_budget_exceeded, retryable is false, and the catalogue message is "Key budget for the current period is exhausted."
Who can raise the spending limit?
A person, in the console. The summary of PATCH /limits/keys/{id} says an API key may lower its own and that raising one is a person's decision.