API strategy

Agent infrastructure

REST API design best practices for the agent era

REST API design best practices for 2026: resource modelling, error contracts, pagination, versioning, OAuth, and observability — updated for agent callers.

10 minute read
Decorative imagery showcasing Pontil's brand

REST has been the default answer to "how should we design an API?" for roughly fifteen years. The Fielding constraints, resource-oriented URLs, HTTP verbs, hypermedia — most SaaS platforms shipped a version of that and moved on. Then agents arrived and started reading the same endpoints, and a lot of the old advice started producing worse outcomes.

This piece takes a position: REST is still the right foundation, but the best practices need updating. The question isn't whether to design a RESTful API in 2026 — it's which of the old rules still hold, which ones need reinterpreting when a language model is on the other end, and which ones actively hurt you now. We'll cover resource modelling, error contracts, pagination and filtering, versioning and change management, authentication and authorisation, and observability. Along the way, we'll say the quiet part out loud: even a well-designed REST API only exposes a fraction of what your product can actually do.

Resource modelling still matters, but the agent stresses it differently

The classic REST advice is to model resources as nouns, use HTTP methods as verbs, and keep URLs predictable. GET /invoices/{id}, POST /invoices, PATCH /invoices/{id}. That's still correct. What's changed is who has to reason about the resource graph.

A human developer reads your docs, forms a mental model, and writes code that assumes that model. If your Invoice has a customer_id but the customer object lives under /accounts/{id}/contacts/{id}, the developer figures it out once. An agent has to work it out every call, from the schema and the descriptions alone. Resource models that were merely awkward for humans become genuinely expensive for agents — more tool calls to traverse relationships, more chances to pick the wrong endpoint, more tokens burned on figuring out the graph.

Two practical consequences. First, keep resource names concrete and singular in meaning. Invoice, Contact, Deployment — not Item, Record, Object. Ambiguous nouns force the agent to guess from context. Second, expose relationships in the schema, not just in the docs. If an invoice belongs to an account, the response should include account_id and, ideally, a link relation the agent can follow. Hypermedia never became the default for humans; it turns out to be genuinely useful for agents that can follow structured links without needing a mental map.

Nesting: less than you think

Deep nesting (/accounts/{id}/projects/{id}/tasks/{id}/comments/{id}) reads well and behaves badly. Every level of nesting is a constraint the caller has to satisfy. For agents, that means more state to track across a trajectory. Flat resources with foreign keys — /comments/{id} with a task_id field — cost less to reason about and let you filter and query without inventing new endpoints. Nest one level when the child genuinely can't exist without the parent. Otherwise, keep it flat.

Error contracts are where agents actually break

Humans read error messages. Agents match on them. That single sentence explains why so many production agent failures trace back to error handling that was fine for developer traffic and inadequate for tool calls.

The old advice — return the right HTTP status, include a human-readable message — isn't wrong. It's insufficient. An agent that gets back 400 Bad Request with { "error": "invalid input" } has almost no signal about what to do next. Retry? Change a parameter? Give up? The RFC 9457 Problem Details for HTTP APIs format is the current best answer: structured type, title, status, detail, and instance fields, plus extensions for anything domain-specific. Adopt it.

The part most teams miss is the machine-readable error code. Not the HTTP status — that's a category. A specific code like invoice.line_item.currency_mismatch tells the caller exactly what happened and, more importantly, lets you build a retry policy against it. 429 says slow down. 409 says the resource changed. 422 says the input was rejected. But invoice.line_item.currency_mismatch says: don't retry, fix the currency field, and the agent can act on that without hallucinating.

The retry-safety signal

Every error response should implicitly or explicitly answer one question: is it safe to retry? Idempotent operations with transient failures (network timeout, upstream unavailable, rate limit) should be retried. Validation errors and permission failures shouldn't. Bake this into the contract by making non-idempotent write endpoints require an idempotency key on the request — a pattern we covered in more depth in our practical guide to idempotency for AI agents. If the caller doesn't send one, reject the request. It sounds strict; it's the only way to make retries safe at agent scale.

Pagination, filtering, and the cost of "just call it again"

Pagination is where REST design habits from the human era age worst. Offset pagination (?page=3&per_page=50) is easy to document and terrible under load. It breaks when data changes mid-traversal, it can't cache well, and it invites agents into O(n) list traversals that would make any human quit.

Cursor pagination is the current default for a reason. The server returns an opaque cursor; the client passes it back. Order is stable, cost is bounded, and the agent doesn't have to reason about page numbers. If you're designing a new endpoint, use cursor pagination. If you're maintaining an old one that uses offsets, add cursor support alongside it and deprecate offsets on the schedule you'd use for any breaking change.

Filtering is the other half of the same problem. If your only way to find the right invoice is to list all invoices and scan, agents will list all invoices and scan — and burn tokens, latency, and rate limit doing it. Give callers real filter parameters. Support the fields that matter (status, customer_id, created_after), keep the syntax simple, and document what's indexed on the server side so callers know what's cheap. A tiny bit of query language (RSQL, a Stripe-style subset, or well-named parameters) goes a long way; a full GraphQL surface tends to invite queries you can't afford to serve.

Versioning and change management: the agent-era rewrite

The old versioning debate — URL versioning (/v1/...) vs header versioning vs date-based versioning — matters less than the deprecation policy behind it. Agents will call your API for as long as the tool definition points at it. If you break the response shape on a Tuesday, every agent trajectory that runs on Wednesday fails, and the failure will look like the agent "hallucinating" rather than your team shipping a breaking change.

The practical rules:

  • Pick one versioning scheme and stay with it. URL versioning is the most legible; date-based versioning (in the style Stripe uses) works well if you're prepared to run many versions concurrently. Both are fine. Mixing them isn't.
  • Additive changes (new optional fields, new endpoints, new enum values documented as extensible) don't require a version bump. Removals, renames, and semantic changes do.
  • Publish a Sunset HTTP header on deprecated endpoints, per RFC 8594, so callers get a machine-readable signal months before the endpoint disappears.
  • Announce deprecations in a place agents' operators actually watch — a changelog feed, an email list, ideally both.

We went deeper on this in our post on API versioning strategy for SaaS platforms in the agent era. The short version: what used to be a developer relations problem is now a production reliability problem.

Authentication: OAuth is table stakes, delegated access is the point

API keys are still the fastest way to onboard a developer. They're also increasingly the wrong pattern for anything an agent will call on behalf of an end user. If your agent runs as a shared service account, every action shows up in your audit log as that service account, every permission check runs against that service account's scopes, and every data leak has the blast radius of that service account's access.

The correct pattern is OAuth 2.1 with delegated access: the agent acts as the authenticated user, tokens carry that user's scopes, and the audit trail reflects the human on whose behalf the action was taken. This isn't new — Google, Microsoft, and Okta have supported it for years. What's new is that it's now the minimum bar for anything an agent will touch in production.

When you design your API, design the scopes for the way an agent will actually be constrained. Broad scopes (read, write) are easy to grant and impossible to reason about. Narrower scopes (invoices.read, invoices.write, contacts.read) let a security review actually approve a specific set of agent capabilities. Yes, that's more scopes to maintain. It's also the difference between passing an enterprise procurement review and stalling in it.

Observability: the API design decisions that make agent traffic legible

Most REST design guides stop at the request-response contract. That was fine when the caller was a developer who'd tell you when something broke. Agent callers don't file support tickets. They fail silently, retry aggressively, and produce logs that look like the model "got confused" when in fact your API returned a subtly wrong shape.

Three design choices matter for observability:

  • Correlation IDs are a contract, not a nice-to-have. Accept an inbound trace header (traceparent, per W3C Trace Context) and return your own request ID on every response. When something goes wrong three tool calls into a trajectory, you need to reconstruct the chain.
  • Rate limit headers should be honest. X-RateLimit-Remaining, X-RateLimit-Reset, and Retry-After on 429s. Agents can be taught to back off. They can't be taught to guess.
  • Response shapes should be stable enough to log-scan. If your status field is sometimes a string and sometimes a boolean, or your timestamps are sometimes ISO 8601 and sometimes epoch seconds, you're paying for that inconsistency in every incident review.

How Pontil fits

Everything above is real advice for teams building or evolving a REST API. It's also, at some point, insufficient. The uncomfortable fact behind the agent era is that most established SaaS products already have a REST API — often a good one — and their agent projects still stall. Not because the API is badly designed. Because the API only covers a fraction of what the product can do. The UI reaches deeper into the product's real capabilities than the API ever did, and no amount of REST-design polish closes that gap.

Pontil is a Tools-as-a-Service platform. We scan the systems you already own — your codebase, your existing APIs — and generate the tools your agents need to reach the parts of your product that the API never exposed. Your REST contract stays your contract. Your CI, tests, and observability keep working. And the tools stay current as the product changes, because generation and maintenance run alongside your SDLC rather than around it. If your agent project has hit the ceiling that better API design alone can't lift, that's the gap we close.

What's the next best practice we haven't written down yet?

The honest answer is that REST design in 2026 is still adjusting to a caller that didn't exist when the constraints were formalised. Agents read specs, match on errors, follow links, and burn tokens in ways that reward some old patterns (predictable resources, stable shapes, honest headers) and punish others (offset pagination, ambiguous nouns, human-readable-only errors). The best practices in this piece are the ones we're confident hold up today.

What's less settled is the layer above the REST contract. When an agent needs to complete a workflow that spans six endpoints, three retries, and two authentication contexts, is the right answer a better REST API — or a tool that composes those endpoints into a single agent-legible action? Our bet is that the answer is increasingly the latter, and that the REST API remains the load-bearing contract underneath it. The next best practice, we suspect, is going to be about designing for that split: an API surface for humans and their code, and a tools surface for agents, both grounded in the same underlying resources. We'll write more when we know more.

Join our weekly newsletter

Stay up to date on the ever changing agentic landscape.

POSTS

Related content

API strategy

Platform integration

OpenAPI specification best practices: writing specs agents and SDK generators can actually use

9 minute read

API strategy

Platform integration

API versioning strategy for SaaS platforms in the agent era

9 minute read

API strategy

Agent infrastructure

API rate limiting best practices for SaaS in the agent era

8 minute read