REST API Endpoint Naming: Conventions That Scale

A practical guide to naming REST API endpoints: principles, patterns, examples, and a checklist for clear, consistent, and evolvable URLs.

ASOasis
6 min read
REST API Endpoint Naming: Conventions That Scale

Image used for representation purposes only.

Introduction

Clear, predictable API endpoint names make RESTful services easier to learn, safer to evolve, and faster to integrate. While there’s no single universal standard, strong conventions help clients guess URLs, reduce documentation overhead, and avoid breaking changes. This guide distills practical, battle‑tested naming rules and patterns you can adopt today.

What good names achieve

  • Discoverability: clients can infer endpoints from resource names.
  • Consistency: fewer exceptions and special rules to remember.
  • Evolvability: stable structures make versioning and deprecations smoother.
  • Safety: unambiguous meanings prevent destructive mistakes.

Core REST naming principles

  1. Model resources, not actions. Endpoints represent nouns; HTTP methods represent actions.
  2. Prefer plural resource names. Collections and members are predictable.
  3. Keep names lowercase, human‑readable, and consistent.
  4. Encode hierarchy and relationships in the path. Use query parameters for filtering.
  5. Keep URLs stable; evolve behavior through headers, representations, and new sub‑resources.

Resource modeling first

Name endpoints after domain concepts, not database tables or UI screens. Use product language users already understand.

  • Good: /customers, /invoices, /shipments
  • Weak: /tbl_customer, /invoiceScreen, /processShipment

If a concept is a first‑class entity, it likely deserves its own resource.

Use plural nouns for collections

  • Collection: /users
  • Member: /users/{user_id}

Plural nouns align with how collections behave: POST to create new resources in the collection; GET to list; DELETE on a member to remove; PATCH on a member to update.

Avoid verbs in paths; use HTTP methods instead

  • Bad: POST /createUser, GET /getUser, DELETE /deleteUser
  • Good: POST /users, GET /users/{id}, DELETE /users/{id}

HTTP methods carry the action semantics:

  • GET: retrieve
  • POST: create (or execute a non‑idempotent subordinate action)
  • PUT: full replace (idempotent)
  • PATCH: partial update (idempotent by design of your operation)
  • DELETE: remove

Hierarchies and relationships

Represent containment and ownership with nested resources when the parent is required to locate the child.

  • /accounts/{account_id}/projects
  • /accounts/{account_id}/projects/{project_id}

Use top‑level endpoints for entities that can be addressed independently, even if they reference others.

  • /users/{id}/addresses (addresses scoped to a user)
  • /addresses/{id} (if addresses are globally addressable)

Avoid deep nesting beyond two levels; it complicates routing and discoverability. Consider linking via IDs or using query filters instead of paths like /orgs/{org}/teams/{team}/members/{member}/devices/{device}.

Identifier patterns

  • Prefer opaque, stable IDs: UUIDs or numeric IDs.
  • Support slugs for readability only if uniqueness and mutability are well‑managed.
  • Be explicit in parameter names: {user_id}, {invoice_id}. Avoid ambiguous {id} in multi‑segment paths.

Examples:

  • /products/{product_id}
  • /stores/{store_id}/products/{product_id}

Path vs. query string

  • Path segments identify resources and structure: /orders/{order_id}/items
  • Query parameters filter, sort, paginate, and shape representations: /orders?status=shipped&sort=-created_at

Avoid encoding filters in the path, e.g., /orders/status/shipped. Use queries instead.

Filtering, sorting, and searching

Choose consistent parameter names and operators.

  • Equality: /tickets?assignee_id=123
  • Sets: /tickets?status=in:open,queued (comma‑separated or repeat the param)
  • Ranges: /events?start_at>=2026-06-01&start_at<2026-07-01 (or use a structured convention like start_at[gte]=…)
  • Sorting: /invoices?sort=-due_date,amount (minus for descending)
  • Full‑text search: /articles?query=observability

Document how to URL‑encode operators and multiple values.

Pagination conventions

Pick one style and use it everywhere.

Offset/limit (simple, but can be inconsistent with large/insert‑heavy datasets):

  • GET /users?limit=50&offset=100

Page/per_page (human‑friendly):

  • GET /users?page=3&per_page=25

Cursor‑based (stable under mutation, best for real‑time lists):

  • GET /users?cursor=eyJpZCI6IjEyMyJ9&limit=50

Expose pagination metadata in response headers or body (e.g., next, prev cursors or HATEOAS links), but keep names consistent.

Non‑CRUD actions and state transitions

Some operations are actions on a resource, not new resources. Prefer sub‑resources for these domain actions.

  • POST /orders/{id}/cancel
  • POST /invoices/{id}/pay
  • POST /sessions (to create a login session)

Use POST for non‑idempotent actions; consider PUT for idempotent toggles when the target state is known.

Avoid verb‑heavy top‑level endpoints like /cancelOrder or /runReport.

Bulk operations

Support batch semantics with clear resource shapes.

  • Create many: POST /users (array payload)
  • Update many: PATCH /users (array of patch docs)
  • Specialized: POST /users/bulk-delete

Name batch sub‑resources explicitly when behavior deviates from normal POST/PUT/PATCH semantics.

Sparse fieldsets and embedding

Let clients control payload size with consistent parameters.

  • Field selection: GET /orders/{id}?fields=order_number,total,customer
  • Embedding related resources: GET /orders/{id}?include=items,customer

Keep names predictable: fields, expand/include, exclude.

File formats and content negotiation

Prefer headers for format negotiation over file extensions.

  • Good: GET /reports with Accept: application/pdf
  • Avoid: GET /reports.pdf

If you must support extensions for legacy reasons, keep them optional and consistent.

Case, separators, and reserved characters

  • Use lowercase in paths.
  • Prefer hyphens for multi‑word path segments: /purchase-orders
  • Use snake_case for query parameter names for readability: created_at, per_page
  • Avoid spaces and special characters in path segments; URL‑encode when necessary.

Pick a style guide and enforce it everywhere.

Trailing slashes and slugs

Choose one policy and stick to it. Common choice: no trailing slash for both collections and members.

  • Preferred: /users and /users/{id}

If supporting both, 301 redirect to the canonical form.

Versioning strategy and naming

Avoid embedding versions in every path if you can negotiate via headers (e.g., Accept: application/vnd.company.resource+json;version=2). If path versioning is required, keep it simple and stable.

  • Path versioning: /v1/users, /v2/users
  • Don’t version individual resources differently; version the whole API surface per base path.
  • Avoid date‑stamped versions in URLs unless your domain requires time‑boxed contracts.

Multitenancy and localization

Avoid tenant IDs and locales in the path unless they are first‑class resources.

  • Good: /tenants/{tenant_id}/users (if tenant scoping is fundamental)
  • Prefer headers or claims for locale (Accept‑Language) over /en-US/products

Error “naming” via problem types

While not endpoint names, standardizing error identifiers improves debuggability.

Consistency checklist

  • Plural nouns for collections; singular members addressed by ID.
  • Lowercase; hyphens in paths, snake_case in query parameters.
  • Use paths for identity and hierarchy; queries for filters and shaping.
  • No verbs in paths; use HTTP methods and action sub‑resources.
  • Predictable pagination and sorting names across all endpoints.
  • Canonical trailing‑slash policy and redirects.
  • Clear versioning strategy, preferably via headers or a single base path prefix.

Common anti‑patterns

  • RPC disguised as REST: /doPayment, /calculateTax
  • Deep nesting: /a/b/c/d/e causing brittle URLs
  • Overloading GET with action semantics: GET /users/123/activate
  • Mixing case and separators: /UserProfile, /user-profile, /user_profile in the same API
  • Encoding filters in path segments: /orders/status/shipped

Worked examples

E‑commerce domain, applying the conventions:

# Collections and members
GET    /products
POST   /products
GET    /products/{product_id}
PATCH  /products/{product_id}
DELETE /products/{product_id}

# Relationships
GET    /products/{product_id}/reviews
POST   /products/{product_id}/reviews

# Filtering, sorting, pagination
GET    /products?category_id=42&in_stock=true&sort=-created_at,price&limit=20&cursor=eyJpZCI6IjEwMCJ9

# Non-CRUD actions via sub-resources
POST   /orders/{order_id}/cancel
POST   /orders/{order_id}/pay

# Bulk operations
POST   /products/bulk-import
POST   /orders/bulk-cancel

# Sparse fieldsets and includes
GET    /orders/{order_id}?fields=order_number,total,customer&include=items,customer

# Versioning (path-based example)
GET    /v1/products

Document and enforce

Codify your naming rules in a short style guide and enforce them with:

  • API linting (e.g., Spectral rules for OpenAPI specs)
  • Contract tests that assert canonical URLs and redirect behavior
  • Review checklists for new endpoints

Migration and deprecation

When you must rename:

  • Add new endpoints alongside old ones.
  • Return deprecation headers and dates.
  • Provide 301 redirects for safe GETs where applicable.
  • Publish a changelog with examples and timelines.

Quick reference template

Use this template as a starting point for new resources:

Base:            /v1 (or header-based versioning)
Collection:      /{resources}
Member:          /{resources}/{resource_id}
Sub-collection:  /{resources}/{resource_id}/{subresources}
Action:          POST /{resources}/{resource_id}/{action}
Filters:         ?field=value&filter[in]=a,b&sort=-created_at
Pagination:      ?limit=50&cursor=...
Fields:          ?fields=a,b,c
Includes:        ?include=x,y

Conclusion

Great endpoint names are the byproduct of clear domain modeling and disciplined consistency. Establish a small set of rules—plural nouns, verb‑free paths, predictable filters and pagination, careful hierarchy—and enforce them across your surface. Your API will be easier to use today and far easier to evolve tomorrow.

Related Posts