REST naming conventions are one of the few areas of API design where there's broad, boring, near-universal agreement on what "good" looks like, and yet almost every API I've integrated against violates at least one of these conventions somewhere — usually in a corner added later by a different team than built the original endpoints. None of these rules are subtle; the value is entirely in applying them consistently across the whole surface, not in picking a clever convention nobody's seen before.
Plural nouns for collections, consistently
/invoices for the collection, /invoices/4471 for a specific member — plural throughout, never switching to singular for the collection endpoint just because a single request returns one thing conceptually. The value isn't some deep semantic correctness; it's that a consumer can predict the URL for any resource type without checking docs every time, and tooling (route generators, SDK codegen) that assumes this pattern works without special-casing your API.
GET /invoices # collection
GET /invoices/4471 # single member
POST /invoices # create
PATCH /invoices/4471 # partial update
DELETE /invoices/4471 # delete
GET /invoices/4471/payments # nested collection
GET /invoices/4471/payments/9 # nested member
Lowercase, hyphen-separated paths
URIs are case-sensitive per spec, and mixing case or using underscores creates avoidable friction: /invoiceItems versus /invoice_items versus /invoice-items are three different URLs to a strict server, and inconsistency within one API forces every consumer to memorize which convention applies to which endpoint. Lowercase with hyphens (/invoice-items) is the most widely adopted convention and matches URL conventions used elsewhere on the web, which is a real, if soft, argument in its favor — a consumer's existing intuition transfers.
Nest only as deep as the relationship is genuinely owned
/invoices/4471/payments is a reasonable nesting: payments belong to and are scoped by a specific invoice. /customers/88/invoices/4471/payments/9/refunds/2 is not reasonable — by three or four levels deep, the URL has become an ownership chain nobody can type from memory, and it's usually a sign the nested resource actually deserves its own top-level collection with a filter, rather than living at the bottom of an increasingly deep path. A practical rule: nest one level for a genuinely dependent sub-resource, and use a top-level collection with a query filter (/refunds?payment_id=9) for anything beyond that.
/invoices/4471/cancel as a POST endpoint is a common and defensible exception — not every state transition maps cleanly to a CRUD verb — but it should be the exception, reserved for actions with no natural resource mapping, not the default pattern for every operation.
Field casing inside the JSON body needs the same discipline
The URL naming convention debate gets most of the attention, but inconsistent field casing inside response bodies — invoice_total next to createdAt next to Status in the same object — is just as much friction for a consumer, and more common because it's easy for different endpoints, built by different people, to drift. Pick one casing convention for the whole API (snake_case or camelCase, either is fine) and enforce it in code review or with an automated schema linter, because it's the kind of inconsistency that's individually trivial and collectively annoying across a whole SDK.
An ID like inv_4471 mixed with plain integer IDs elsewhere in the same API (or worse, IDs that change format between versions) breaks any client code that assumes IDs are opaque, uniformly-typed values. If you want typed-prefix IDs (a legitimate pattern, popularized by Stripe), apply it consistently across every resource type from day one.
Wrapping up
None of these conventions are individually clever, and that's exactly why they work: plural nouns, lowercase hyphenated paths, shallow nesting, and one consistent field-casing scheme remove an entire category of "wait, is it this or that" friction from integration, at zero runtime cost. The discipline is in applying them everywhere, including the endpoint added six months after the original design doc was forgotten.
Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.