TestingAPIs is still being built. If something breaks, email support@testingapis.com.

AI agents are reading your API errors now

Share

In March, Mintlify added up a month of traffic to the documentation sites it hosts. That came to about 790 million requests, and 45.3% of them were made by AI coding agents. Browsers made 45.8%. Claude Code on its own sent a quarter of everything (Mintlify's numbers).

That's traffic to docs, not to APIs. But the agents reading the docs are the same ones that then write the request, run it and read whatever comes back. In Postman's 2025 State of the API survey, 24% of developers said they already design APIs with agents in mind. That sounded early when it came out. It doesn't anymore.

Most of what we build here is about failure: endpoints that return 500s, hang, or stop halfway through a response. So the question we kept asking was a narrow one. When an agent gets an error from your API, does it have enough to do the right thing next?

An agent takes your error message at its word

A developer who gets {"error":"E4021"}back searches for the code, finds nothing, and asks a colleague. An agent can't ask anyone. It works with the body you sent, and if the body says nothing useful it guesses: it retries the same request, or changes a field that was fine.

Here's what our products endpoint says when the ID doesn't exist:

Terminal
$ curl -i https://api.testingapis.com/api/Products/99999
HTTP/1.1 404 Not Found
content-type: application/json; charset=utf-8

{"status":404,"error":"Not Found","message":"Product with ID 99999 does not exist."}

It's a dull sentence, and that's fine. It names the resource and the ID, so whoever reads it, person or program, knows the ID is the problem and that sending it again won't help.

If you'd like a standard shape for errors, RFC 9457 (Problem Details for HTTP APIs) defines type, title, status, detail and instance, served as application/problem+json. It replaced RFC 7807 in 2023. We don't think the exact format matters much. What matters is that there's a plain sentence in there somewhere.

Say how long to wait

Agents retry. If your 429 or 503 doesn't say how long to back off, the client has to pick a number, and an agent working through a task in a loop will keep picking. Retry-After has been part of HTTP for decades and it fixes this with one header:

Terminal
$ curl -i https://api.testingapis.com/api/status/429
HTTP/1.1 429 Too Many Requests
content-type: application/json; charset=utf-8
retry-after: 5

{"status":429,"statusText":"Too Many Requests","category":"client-error","message":"This is a 429 response. Use /api/status/{code} to request any status."}
GET/api/status/429

The IETF is also working on RateLimit and RateLimit-Policy headers, which let a client see its quota before it runs out rather than after. Draft 11 came out in May. The syntax looks like this:

Headers
RateLimit-Policy: "default";q=100;w=60
RateLimit: "default";r=50;t=30

That reads as "100 requests per 60 seconds, 50 left, window resets in 30". It's still a draft, so we wouldn't drop the X-RateLimit-* headers your clients already read. Sending both costs very little.

Retries create duplicates

Retrying a GET is harmless. Retrying a POST is not, if the first attempt reached the server and only the response got lost. You end up with two orders. A person usually spots the double charge. An agent sees that its second attempt worked and moves on.

The usual fix is an idempotency key. The client sends a unique Idempotency-Keyheader with the write, and if the same key arrives again, the server returns the first result instead of doing the work twice. Stripe has worked this way for years, and there's an IETF draft for the header, though it isn't an RFC yet. If your API takes payments or creates anything that's annoying to clean up, it's worth adding before agents start calling it.

MCP servers are turning into ordinary HTTP APIs

Plenty of agents won't call your REST API directly. They'll go through an MCP server that wraps it. The MCP specification released on 28 July removed the session handshake and the Mcp-Session-Id header. Each request now carries its own protocol version and client details, and the method and tool names travel in HTTP headers, so a gateway can route and rate-limit requests without parsing the body.

The upshot is that an MCP server now behaves much like any other stateless HTTP service. It can sit behind a plain load balancer, and the advice above applies to it as it is. When a tool call fails, the agent needs a reason and a wait time from your MCP server just as it would from your API.

A quick check

Here's the script we ran while writing this. It sends four requests that fail and checks two things for each one: is there a reason a program can read, and is there a Retry-After?

check-errors.mjs
// For each failure: is there a reason a program can read, and a time to wait?
const BASE = "https://api.testingapis.com";

const cases = [
  ["missing product", "/api/Products/99999"],
  ["rate limited", "/api/status/429"],
  ["server down", "/api/Products/1?error=503"],
  ["bad request", "/api/status/400"],
];

for (const [name, path] of cases) {
  const res = await fetch(BASE + path);
  const body = await res.json().catch(() => ({}));
  const reason = body.message ?? body.detail ?? body.title;
  const wait = res.headers.get("retry-after");
  console.log(
    name.padEnd(16),
    res.status,
    reason ? "reason: yes" : "reason: NO ",
    wait ? `retry-after: ${wait}s` : "retry-after: -",
  );
}
Output (Node 24, 9 October 2026)
missing product  404 reason: yes retry-after: -
rate limited     429 reason: yes retry-after: 5s
server down      503 reason: yes retry-after: -
bad request      400 reason: yes retry-after: -

Every response has a reason, which is good. But look at the third line. /api/status/503 sends Retry-After: 5, and ?error=503on a real endpoint sends nothing. A client tested against it would never learn when to try again. That one's on our list.

Point the same script at your own API's failure paths. If you can't make your API fail on demand, the error responses guide shows how to get a 500, a 429, a timeout or a truncated body from ours.

None of this is new advice. Clear error messages and Retry-Afterwere good ideas long before anyone built an agent. What's changed is that a confused person eventually emails you, and an agent just tries again.