The definitive guide to API resource naming conventions
Clear rules for naming API resources, fields, and events across REST, GraphQL, and gRPC—with examples, pitfalls, and a practical checklist.
Image used for representation purposes only.
Why naming conventions matter
Good names reduce cognitive load, prevent breaking changes, and make APIs self-documenting. Consistent resource names help clients guess endpoints, discover capabilities, and avoid subtle bugs that come from case, pluralization, or action verbs used inconsistently. This guide distills practical conventions you can adopt across REST, GraphQL, gRPC, and evented APIs.
Core principles
- Prefer nouns over verbs for resources; use HTTP methods (or RPC methods) for actions.
- Be consistent: pick a style (case, separators, versioning) and stick to it everywhere.
- Optimize for readability over keystrokes; avoid cryptic abbreviations.
- Stability beats cleverness; changing names is a breaking change.
- Names are part of your contract—document them, lint them, and test them.
REST resource paths
Collections and items
- Collections: plural, kebab-case, lowercase.
- Items: append an opaque identifier segment.
Examples:
GET /users
GET /users/{user_id}
POST /users
GET /orders/{order_id}/items
Bad patterns to avoid:
GET /getUsers # verbs in paths
GET /user/123 # singular collection name
GET /Orders # inconsistent case
Hierarchies and relationships
- Use hierarchical paths for strong ownership:
orders/{order_id}/items. - Use top-level collections with filter params for weak relationships:
GET /items?order_id=.... - Keep path parameter names singular and explicit:
{user_id},{order_id}. - Don’t repeat the parent name in child segments: prefer
/users/{user_id}/sessions/{session_id}over/users/{user_id}/user-sessions/{session_id}.
Case and separators
- Paths: kebab-case (hyphens) for readability:
/credit-cards. - Query parameters: choose snake_case or camelCase; snake_case works well in URLs and many backends.
- JSON fields: camelCase is common (interops with JS), but snake_case is fine—just be consistent.
Trailing slashes and file extensions
- No trailing slash on canonical URLs:
/usersnot/users/. - No file extensions: prefer
Accept/Content-Typeheaders over/users.json.
Identifiers
- Use opaque, stable IDs (UUIDs, ULIDs, snowflakes). Don’t encode meaning in IDs.
- Field names:
idfor the resource’s primary key; foreign keys use suffixed names (e.g.,userIdoruser_id). - Timestamp fields:
createdAt,updatedAt(orcreated_at,updated_at), RFC 3339 strings.
Actions and operations
- Use HTTP methods for CRUD:
- GET (safe, idempotent) read
- POST (not idempotent) create or trigger a server-side action that changes state
- PUT/PATCH (idempotent/partial) update
- DELETE (idempotent) delete
- For non-CRUD domain actions, model them as sub-resources or action endpoints:
POST /payments/{payment_id}/capture
POST /payments/{payment_id}/refunds
If you need to emphasize action semantics, a colon action is a clean, explicit option inspired by Google APIs:
POST /payments/{payment_id}:capture
Prefer idempotency keys for actions that could be retried:
curl -X POST \
-H 'Idempotency-Key: 2ca9a4e7-...' \
'https://api.example.com/payments/{payment_id}/refunds'
Examples: a cohesive resource tree
/v1
/users
GET /users
POST /users
GET /users/{user_id}
PATCH /users/{user_id}
/users/{user_id}/sessions
/users/{user_id}/roles
/orders
GET /orders?status=pending&customer_id=...
GET /orders/{order_id}
/orders/{order_id}/items
POST /orders/{order_id}/cancel
Query parameters
Establish a small, predictable vocabulary.
- Filtering: explicit field names
GET /orders?status=pending&customer_id=abc123- For multi-value:
?status=pending,confirmedor repeated:?status=pending&status=confirmed(pick one convention).
- Search:
qfor free-text search;queryfor structured queries. - Pagination:
- Offset:
?limit=50&offset=100 - Page:
?page=3&per_page=50 - Cursor:
?limit=50&cursor=eyJv...with response fieldsnext_cursor,prev_cursor.
- Offset:
- Sorting:
?sort=created_atascending; prefix with-for descending:?sort=-created_at. - Sparse fieldsets:
?fields=id,name,created_at. - Embedding/expansion: pick one term—
includeorexpand—e.g.,?include=customer,items.product.
JSON payloads and schemas
- Case: choose camelCase or snake_case; use it everywhere (requests, responses, errors, webhooks).
- Booleans: prefix with
is/hasif it clarifies intent (e.g.,isArchived,hasTrial). - Money: store as integer minor units (
amount: 1299,currency: “USD”). - Enums: use stable, lowercase strings with hyphens or snake_case (e.g.,
status: "pending" | "in-transit" | "delivered"). - Time: RFC 3339 UTC strings (e.g.,
"2026-04-04T16:20:00Z"). - Nullability: prefer omitting absent optional fields over null, unless null is semantically meaningful.
Example response:
{
"id": "ord_7WQqfQfV",
"status": "pending",
"createdAt": "2026-04-04T16:20:00Z",
"customerId": "cus_kT52z1",
"amount": 1299,
"currency": "USD",
"items": [
{ "productId": "prod_123", "quantity": 2 }
],
"links": { "self": "/v1/orders/ord_7WQqfQfV" }
}
Error representation
- Top-level
errororerrorsarray. - Machine-readable
codeand human-readablemessage. - Include
fieldfor validation issues and arequestIdfor support.
{
"error": {
"code": "invalid_argument",
"message": "amount must be >= 0",
"field": "amount",
"requestId": "req_9a5..."
}
}
Name error codes in snake_case or kebab-case and keep them stable: not_found, permission_denied, rate_limited.
Versioning and names
- URI versioning:
/v1/...keeps names stable within a version. - Header/media-type versioning:
Accept: application/vnd.acme.v2+jsonif you need many variants. - Avoid embedding version numbers in field names (
createdAtV2); add fields with new names and deprecate old ones.
GraphQL naming conventions
- Types, Inputs, Enums: PascalCase (e.g.,
User,CreateOrderInput). - Fields and arguments: camelCase (e.g.,
createdAt,orderId). - Enum values: UPPER_CASE_SNAKE (e.g.,
PENDING,IN_TRANSIT). - Queries use nouns; mutations are verbs that return changed resources.
Example:
type Order {
id: ID!
status: OrderStatus!
createdAt: DateTime!
customer: Customer!
items: [OrderItem!]!
}
enum OrderStatus { PENDING IN_TRANSIT DELIVERED CANCELED }
type Query {
order(id: ID!): Order
orders(status: OrderStatus, after: String, first: Int = 50): OrderConnection!
}
type Mutation {
createOrder(input: CreateOrderInput!): Order!
cancelOrder(id: ID!): Order!
}
gRPC/Protobuf naming conventions
- Service and message names: PascalCase (
OrderService,CreateOrderRequest). - Field names: snake_case by Protobuf convention.
- RPC methods: PascalCase verbs (
CreateOrder,GetOrder,ListOrders). - If you expose HTTP via transcoding, keep RESTful path naming consistent with your REST guidelines.
service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (Order) {}
rpc GetOrder(GetOrderRequest) returns (Order) {}
rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse) {}
}
message Order {
string id = 1;
string status = 2; // "pending", "in_transit", ...
string created_at = 3;
}
Event and webhook names
Evented systems benefit enormously from clear, stable names.
- Use dotted, namespaced, past-tense event names:
acme.order.created,acme.order.canceled. - Keep payloads aligned with your REST/JSON naming style.
- Include a
type,id,occurredAt, anddataenvelope.
{
"id": "evt_1G...",
"type": "acme.order.created",
"occurredAt": "2026-04-04T16:20:00Z",
"data": {
"id": "ord_7WQqfQfV",
"status": "pending",
"customerId": "cus_kT52z1"
}
}
Topic naming for streams (Kafka, SNS, NATS):
- Use product and domain namespaces:
acme.orders.v1.events. - Partition key name: match your business key (e.g.,
order_id).
Internationalization, domains, and tenants
- Resource names are part of the API grammar—keep them in English.
- Localize data, not endpoints; respect
Accept-Languagefor fields that carry human text. - For multi-tenant APIs, make tenancy explicit via headers or a stable path segment:
/v1/{tenant_id}/usersorX-Tenant-Id: tnt_123(pick one and standardize).
Consistency across SDKs
- Mirror server naming in SDKs, but follow language idioms for casing:
- Java/Kotlin: camelCase methods/fields, PascalCase types.
- Python/Ruby: snake_case methods/fields, PascalCase classes.
- JS/TS: camelCase methods/fields, PascalCase classes.
- Keep method verbs aligned with API actions:
client.orders.create(...),client.orders.cancel(order_id).
A minimal style guide you can adopt today
- Paths: lowercase, kebab-case, plural nouns; no trailing slashes.
- IDs: opaque strings;
idfor self,{resource}Idfor references. - Query:
limit,offsetorpage,per_page;cursorfor cursor-based. - Sorting:
sort=fieldorsort=-field. - Timestamps: RFC 3339 UTC strings named
createdAt,updatedAt. - Errors: snake_case
code, humanmessage, optionalfield,requestId. - Versions:
/v1, evolve with additive changes; avoid renaming in-place. - Events:
vendor.domain.eventin past tense.
Common pitfalls (and how to avoid them)
- Mixing cases in one API: define a single casing policy for paths, query, and JSON.
- Encoding actions as verbs in paths: let methods do the work; use sub-resources for domain actions.
- Overloading
GETfor destructive operations: never do this; respect HTTP semantics. - Leaking database structure:
tbl_userin paths or integer autoincrement IDs; prefer opaque business IDs. - Inconsistent plurals and irregular nouns: standardize exceptions (
peoplevspersons—pick one and document it). - Hidden side effects behind idempotent methods: if it changes state and isn’t idempotent, it’s POST.
- Ad hoc query parameter names: maintain a dictionary for filters, pagination, and expansion and lint PRs against it.
Design-time checklist
- Is the resource a noun and pluralized for collections?
- Are path segments lowercase kebab-case and free of trailing slashes?
- Do IDs, timestamps, and money fields follow consistent names and formats?
- Are non-CRUD operations modeled as sub-resources or explicit actions?
- Are filtering, pagination, and sorting parameters named consistently?
- Are error codes stable, documented, and machine-readable?
- Do events/webhooks use a consistent namespace and past-tense names?
- Is versioning strategy explicit and documented?
Conclusion
Naming is one of the highest-leverage choices in API design. Choose a small set of clear, consistent conventions and enforce them with linters, style guides, and review checklists. Your future users—and your future self—will thank you.
Related Posts
API Backward Compatibility Strategies: Designing Change Without Breaking Clients
Practical strategies to keep APIs backward compatible—versioning, additive changes, deprecation, rollout, and testing for REST, GraphQL, and gRPC.
The API Versioning Playbook: Best Practices, Patterns, and Pitfalls
A practical playbook for API versioning: strategies, SemVer, backward compatibility, deprecation, testing, and rollout patterns for stable, evolving APIs.
API Documentation Auto‑Generation Tools: A Practical Guide
Practical guide to auto‑generating API docs: tools, workflows, examples, CI/CD, and best practices for OpenAPI, GraphQL, and gRPC.