01Use nouns, not verbs
The URL identifies what you are acting on; the HTTP method describes the action. Use POST /orders, not /createOrder. Use GET /customers/123, not /getCustomerById?id=123. Verbs in paths add noise because the method already carries the meaning — DELETE /users/123 is unambiguous, while /deleteUser/123 duplicates information and breaks the moment you add another way to delete. When a genuine action does not map to CRUD (approving, publishing, archiving), model it as a sub-resource or a state transition rather than smuggling a verb into the path.
02Plural nouns for collections
/customers, not /customer. The same path with an identifier addresses a single item: /customers/123. Plural-only is the dominant convention in modern APIs and keeps URL grammar consistent — a developer who sees /orders can guess /orders/456 without reading docs. Mixing singular and plural across an API (/user here, /orders there) is one of the fastest ways to make an API feel amateur.
03Lowercase and kebab-case
Use /order-items, not /OrderItems or /order_items. URLs are technically case-sensitive but treated inconsistently across stacks, so mixed case creates phantom duplicates. Hyphens beat underscores for readability in browser bars and are the web's native word separator. Pick one casing and enforce it with a linter — casing drift across fifty endpoints is a support burden.
04Hierarchy for relationships
Use the path to express containment: /customers/123/orders for the orders belonging to customer 123. This makes URLs self-documenting — the relationship is visible without opening the docs. But hierarchy is for ownership, not for every association: if orders can exist independently and are merely filtered by customer, a top-level /orders?customer=123 is cleaner.
05Limit nesting depth
One level of nesting is normal; two levels is a smell; three levels is almost always wrong. /customers/123/orders/456/items/789/notes couples your URL structure to your data model and breaks every time relationships change. Deep paths are also painful to document, test and cache. Our nesting guide covers when to nest and when to flatten.
06No file extensions, no special characters
Do not end paths with .json or .xml — the URL identifies the resource, and format is negotiated with the Accept header or a query parameter. Stick to letters, numbers and hyphens otherwise: characters like ?, & and % have reserved meanings, and spaces or unicode cause encoding bugs that surface at the worst possible time.
07Version in the URL
Include the version where clients cannot miss it: /v1/users or /api/v1/users. Header-based versioning is elegant and invisible — which is exactly the problem when debugging someone else's integration at midnight. Start at v1 explicitly (not "unversioned, we'll add it later"), and never change the meaning of a versioned endpoint without bumping the version.
08Query params for selection, not paths
Filtering, sorting and pagination belong in the query string: /users?status=active&sort=created&page=2&pageSize=20. The path identifies the resource; the query selects the view. Path segments like /users/active or /users/latest multiply endpoints and collide with IDs. Keep the path stable and let the query do the selecting.
Consistency beats cleverness. An API where every endpoint follows the same eight rules is an API developers can guess — and guessed-right APIs get adopted.