An API is a product for developers. When it is consistent and predictable, integrations are quick and support requests are rare. When it is not, every client pays the price. These REST API design best practices apply whether you build with ASP.NET Core, Node.js or anything else.

1. Model resources with clear names

  • Use nouns, plural: /customers, /customers/42, /customers/42/orders.
  • Let HTTP methods express actions; avoid /getCustomer or /createOrder.
  • Keep nesting shallow — one level is usually enough.
  • For operations that are not simple CRUD, use a sub-resource: POST /orders/42/cancellation.
  • Pick one casing convention for JSON fields (camelCase is common) and use it everywhere.

2. Use HTTP methods and status codes correctly

MethodUseTypical success status
GETRead a resource or list200 OK
POSTCreate a resource or trigger an action201 Created (with Location header) or 202 Accepted
PUTReplace a resource200 OK or 204 No Content
PATCHPartially update a resource200 OK or 204 No Content
DELETERemove a resource204 No Content

For errors: 400 invalid input, 401 not authenticated, 403 not allowed, 404 not found, 409 conflict, 422 validation or business-rule failure, 429 rate limited, 5xx server errors.

3. One error format everywhere

Adopt Problem Details for HTTP APIs (RFC 9457):

{
  "type": "https://api.example.com/errors/validation",
  "title": "One or more fields are invalid.",
  "status": 422,
  "errors": { "email": ["Email is required."] },
  "traceId": "00-4bf92f..."
}

Include a trace ID so support can find the request in your logs, and never expose stack traces or internal details.

4. Paginate, filter and sort list endpoints

  • Every list endpoint should be paginated, with a maximum page size.
  • Cursor pagination (?cursor=abc&limit=50) is robust for large or changing data; offset pagination (?page=2&pageSize=50) is simpler for small lists.
  • Use clear query parameters for filtering and sorting: ?status=open&sort=-createdAt.
  • Return pagination metadata (next cursor, or total where it is cheap to compute).

5. Version without breaking clients

  • Put the major version in the path: /v1/.
  • Within a version, only make additive changes: new optional fields, new endpoints.
  • Removing or renaming fields, or changing their meaning, requires a new version.
  • Announce deprecations with dates, and use response headers to warn clients.

6. Make writes safe

  • Support idempotency keys on POST requests that create payments or orders, so retries do not create duplicates.
  • Use ETags and If-Match for optimistic concurrency on updates.
  • For long operations, return 202 Accepted with a status URL instead of holding the connection open.

7. Secure every endpoint

  • HTTPS only; OAuth 2.0 / OpenID Connect or API keys for server-to-server integrations.
  • Authorize at the resource level: check that the caller owns or may access this record.
  • Validate and size-limit all input; rate limit by client.
  • Log security-relevant events without logging secrets.

8. Document with OpenAPI

Publish an OpenAPI description, generated from code or written first, with examples for requests, responses and errors. Treat changes to it as reviewed changes to a contract, and check it for breaking changes in CI. Microsoft's API design guidance is a useful additional reference.

9. Webhooks for events

If clients need to know when something happens, offer webhooks: sign payloads, retry with backoff, include an event ID for deduplication, and provide a way to replay missed events.

Checklist: plural noun resources · correct methods and status codes · Problem Details errors with trace IDs · paginated lists · path versioning with additive changes · idempotency keys · resource-level authorization · OpenAPI documentation · signed webhooks.

Building in .NET? Read ASP.NET Core Web API best practices. Need an API designed or built? See our API and backend development services.