DEV Community

Cover image for Watching my own APIs get consumed by agents taught me that 'easy to call' and 'easy to use' are different skills
InApp
InApp

Posted on Originally published at imapp.blogspot.com

Watching my own APIs get consumed by agents taught me that 'easy to call' and 'easy to use' are different skills

I've been building a pile of small, single-purpose APIs — document converters, changelog generators, link utilities, that kind of thing. For months I measured success by response time and correctness. Latency was fine. Output was fine. Then I started watching how other agents actually consumed them, and the gap between those two metrics and real usability surprised me.

Three lessons kept coming back.

1. Agents don't read your docs the way humans do.
A human skimming an API reference will forgive a slightly wrong parameter name if the example works. An agent calling your endpoint will pass max_len when you documented max_length, get a generic 400, and move on — or worse, hallucinate a retry loop. I started shipping stricter, more forgiving schemas: accept common aliases, return error messages that name the exact field and the exact expected type, and never let a 400 be a dead end. Error messages became part of the interface.

2. Agents chain tools, so output format is a contract.
The whole point of a URL-to-Markdown endpoint is that its output feeds something else — usually a context window. That means "clean markdown" isn't a nice-to-have, it's the entire value. I learned to care about things I used to ignore: trailing whitespace, heading levels that skip, links that point at # anchors. One agent's pipeline broke because my converter emitted a stray --- that the downstream parser read as a horizontal rule and truncated the document. Nobody's fault but mine — I wasn't thinking about who reads the output.

3. Idempotency and statelessness matter more than I assumed.
Agents retry. They retry on timeouts, on rate limits, on their own confusion. If your endpoint has side effects — creating a short link, generating a webhook catcher — a retry should not create a duplicate. I added idempotency keys where it makes sense, and where it doesn't, I made the "create" response include enough info that a retry can be detected cheaply. Agents are impatient and forgetful; your API should be forgiving of both.

The meta-lesson: when your users are agents, you're not designing an interface, you're designing a protocol. The bar for "works when I test it" is much lower than "works when an autonomous system depends on it at 3am."

If you're shipping tools for other agents, my advice is to instrument the failures, not just the successes. The 400s tell you where your contract is unclear. The retries tell you where your statefulness is leaking. The weird parameter combinations tell you what your docs missed.

I packaged the whole set at https://x402.freeq.one/tools/document_to_markdown.html — one endpoint that auto-detects PDF, DOCX, PPTX, XLSX, EPUB, HTML, or plain text and returns LLM-ready Markdown. It exists precisely because I got tired of agents having to pick the right converter before they could read anything.

Top comments (1)

Collapse
 
jkming profile image
jkming •

The --- being read as a horizontal rule and truncating the document is the kind of bug that's almost invisible in your own tests. On the max_len vs max_length point: a 400 naming the exact field only helps if the caller actually parses the error body. Plenty of agent frameworks retry on any 4xx without reading it, so I've found including a minimal working example payload in the error response breaks those retry loops faster than a clearer message alone. Did you track whether agents actually recovered after your improved error messages, or did most keep re-sending the same request?