Naming Your API Endpoints Is Harder Than It Looks
by Eric Hanson, Backend Developer at Clean Systems Consulting
Bad names don’t fail immediately—they fail over time
You can ship a poorly named endpoint and nothing breaks.
Requests still go through. Data still comes back. No alarms fire.
The problem shows up later:
- new developers misunderstand what an endpoint does
- similar concepts get named differently across services
- edge cases force awkward exceptions in naming
- refactoring becomes risky because names are already embedded in clients
Endpoint names are part of your public contract. Once they’re in use, changing them is expensive.
Most teams treat naming as a quick decision. It’s not. It’s one of the few parts of your API that every consumer interacts with directly.
The root issue: naming what the backend does, not what the client needs
A common mistake is naming endpoints based on internal actions:
POST /processPayment
POST /syncUserData
POST /handleCheckout
These names reflect what the server does, not what the resource represents.
They also age badly. What happens when “processPayment” includes fraud checks, retries, and partial captures? The name stops matching reality.
Better approach: name resources, not actions.
POST /payments
GET /users/{id}
POST /checkouts
You’re describing what exists, not what happens internally.
The HTTP method carries the action. The path carries the resource.
Pluralization is a consistency trap
One of the fastest ways to introduce inconsistency:
GET /user/123
GET /orders
GET /product/456
Singular here, plural there.
Pick a rule and enforce it. Most APIs use plural nouns:
GET /users/{id}
GET /orders
GET /products/{id}
Why plural?
- collections are first-class concepts
- it avoids special casing between list and single-resource endpoints
The real problem isn’t which you pick—it’s mixing them.
Avoid “misc” and “utility” endpoints at all costs
When naming gets hard, teams fall back to buckets:
POST /utils/doSomething
POST /misc/action
POST /internal/process
These are red flags.
They signal that the resource model is unclear, so everything gets dumped into a catch-all.
This leads to:
- unclear ownership
- inconsistent behavior
- endpoints that grow uncontrollably
If you can’t name it cleanly, step back and rethink the resource. The problem is usually upstream in the domain model.
Nested resources: useful, but easy to overdo
Nesting helps express relationships:
GET /customers/{id}/orders
This is clear and useful.
Where it goes wrong:
GET /customers/{id}/orders/{orderId}/items/{itemId}/details
Deep nesting creates brittle URLs and tight coupling between resources.
A good rule:
- use nesting for clear parent-child relationships
- avoid more than 2–3 levels deep
Alternative:
GET /order-items/{id}
Flat structures are often easier to evolve.
Consistency matters more than perfection
You will not find the perfect name every time.
What matters is alignment with what already exists.
Example:
If you already have:
GET /users/{id}
GET /users/{id}/orders
Don’t introduce:
GET /purchases/{id}
Even if “purchases” might be more accurate, you’ve now created two terms for the same concept.
Consistency reduces cognitive load. Every new deviation increases it.
Versioning amplifies naming mistakes
Once you introduce versioning:
/v1/users
/v2/customers
you’ve locked in confusion.
Renaming resources across versions might seem like a cleanup, but it forces clients to relearn the API.
If you need to rename something, consider:
- aliasing (support both temporarily)
- clear deprecation timelines
- documentation that maps old to new
But avoid renaming unless the original name is actively harmful.
Don’t encode behavior into query parameters either
Naming problems don’t stop at paths.
This is just as bad:
GET /orders?action=cancel
You’ve reintroduced RPC semantics through query parameters.
Better:
DELETE /orders/{id}
or:
PATCH /orders/{id}
{
"status": "cancelled"
}
Keep behavior aligned with HTTP methods and resource state.
The tradeoff: clarity vs domain precision
Sometimes your domain language is messy.
Example:
- “accounts” vs “users”
- “orders” vs “transactions” vs “purchases”
You can optimize for:
- domain accuracy (matches internal models)
- external clarity (makes sense to API consumers)
These are not always the same.
In most cases, external clarity should win. Your API is a boundary, not a mirror of your internals.
A simple naming checklist that actually works
Before finalizing an endpoint, ask:
- Is this a resource or an action?
- Is the noun consistent with similar endpoints?
- Is it pluralized consistently?
- Would a new developer understand it without context?
- Will this name still make sense if the implementation changes?
If any answer is “no,” fix it before it ships.
What to do differently this week
Find one awkwardly named endpoint in your API.
Not the worst one—the one people hesitate when using.
Rename it properly in a new version or alias it alongside the old one.
Then write down the naming rule you applied and reuse it.
Good naming isn’t about getting it perfect once. It’s about making future decisions easier and more consistent.