The URL is the first thing another developer sees when they pick up your APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses.. A well-named URL tells them what they're looking at before they've read a single line of documentation. A poorly named one forces them to dig through your code to figure out what /api/doUserStuff actually does.
Nouns, not verbs
The most common RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. mistake is using verb-based URLs, usually because developers are thinking in terms of functions rather than resources. HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. methods are already your verbs. The URL just names the thing you're acting on.
Think of it like a filing cabinet. The drawer is /users. The method tells you whether you're reading from it, adding to it, or removing something. You wouldn't label the drawer "getUser" or "deleteUser."
// RPC-style (wrong for REST)
GET /getUser?id=123
POST /createUser
PUT /updateUser/123
DELETE /deleteUser?id=123
// RESTful (correct)
GET /users/123
POST /users
PUT /users/123
DELETE /users/123Same resource, same URL, the method tells you what you're doing to it.
Plural nouns for collections
Collections use plural nouns. Always. This makes it consistent: /users is always a collection, /users/123 is always a single item. No guessing.
// Wrong
GET /user/123
GET /product/456
// Right
GET /users/123
GET /products/456Kebab-case for multi-word names
URLs are case-insensitive by convention, and some servers treat them as case-sensitive. Avoid camelCase in URLs entirely. Use hyphens (kebab-case), they're readable and universally URL-safe.
// Wrong
GET /userProfiles
GET /order_items
GET /OrderHistory
// Right
GET /user-profiles
GET /order-items
GET /order-historyNesting for relationships
When one resource belongs to another, you can represent that relationship in the URL hierarchy. This makes the relationship immediately readable.
/users/123/orders // All orders for user 123
/users/123/orders/456 // Order 456 belonging to user 123
/orders/456/items // Items within order 456Keep nesting to a maximum of 2-3 levels. Deeper than that and your URLs become unmanageable:
// Too deep - don't do this
/users/123/orders/456/items/789/reviews/999When resources get deeply nested, consider whether the child resource can stand on its own. An order item might be accessible as both /orders/456/items/789 and /items/789, whatever makes more sense for your use case.
Query parameters for filtering, sorting, paginationWhat is pagination?Splitting a large set of results into smaller pages so the server and client only handle a manageable chunk at a time.
The URL path identifies a resource. Query parameters modify how you retrieve it, filtering, sorting, paginating, searching. These are not part of the resource identity; they're options on the request.
// Filtering
GET /users?role=admin&active=true
GET /products?category=electronics&max_price=500
// Sorting (minus prefix = descending)
GET /users?sort=name&order=asc
GET /users?sort=-created_at
// Pagination
GET /users?page=2&limit=25
GET /users?offset=50&limit=25
// Searching
GET /products?q=laptop
// Field selection
GET /users?fields=id,name,email
// Combined
GET /products?category=electronics&sort=-price&page=2&limit=20Versioning your APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses.
APIs change over time, and you can't just modify existing endpoints without breaking clients who depend on them. Versioning lets you introduce breaking changes without destroying existing integrations.
// URL versioning (recommended)
/api/v1/users
/api/v2/users
// Header versioning
GET /users
Accept: application/vnd.api+json;version=1
// Query parameter versioning
GET /users?api-version=2URL versioning is the most practical choice. It's explicit, easy to test in a browser, cache-friendly, and dead simple to understand. Header versioning is theoretically purer (the URI identifies the resource, not the version), but it's harder to work with in practice.
Common patterns
Standard CRUDWhat is crud?Create, Read, Update, Delete - the four basic operations almost every application performs on data.
GET /resources // List all (paginated)
GET /resources/:id // Get one
POST /resources // Create
PUT /resources/:id // Full update
PATCH /resources/:id // Partial update
DELETE /resources/:id // DeleteActions that don't map cleanly to CRUD
Sometimes you need to express an action on a resource, cancelling an order, activating a user, publishing a post. These don't fit neatly into CRUD. The convention is to use a sub-resource that looks like an action:
POST /orders/123/cancel
POST /users/123/activate
POST /posts/456/publishAuth endpoints
AuthenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token. is a special case where the resource model doesn't quite fit. It's conventional to break the strict "nouns only" rule here:
POST /auth/register
POST /auth/login
POST /auth/logout
POST /auth/forgot-password
POST /auth/reset-password
GET /auth/meQuick reference
| Rule | Wrong | Right |
|---|---|---|
| Use nouns | /getUser | /users |
| Use plurals | /user/123 | /users/123 |
| Use kebab-case | /userProfiles | /user-profiles |
| Use lowercase | /Users/123 | /users/123 |
| No trailing slash | /users/ | /users |
| No file extension | /users.json | /users |
| Version the API | /api/users | /api/v1/users |