Today, from mobile apps to microservices, almost all modern software communicates through APIs (Application Programming Interfaces). Writing an API that works is easy. Designing a scalable, secure and predictable API that other developers enjoy using requires following a set of proven standards.

In this guide we cover the REST API design best practices that matter most in real projects, and then go through a practical checklist for securing a REST API.

REST API Design Best Practices

1. Use HTTP Methods Properly

The foundation of REST is using the correct HTTP method for each operation on a resource. Using POST for everything is a common anti-pattern.

  • GET: Reads or lists data. It must not change anything on the server.
  • POST: Creates a new resource.
  • PUT: Replaces an existing resource entirely.
  • PATCH: Partially updates an existing resource.
  • DELETE: Removes a resource.

GET, PUT and DELETE are idempotent: sending the same request twice has the same effect as sending it once. This makes retries safe. For POST requests that must not run twice (such as payments), accept an Idempotency-Key header and ignore duplicates with the same key.

2. Use Nouns, Not Verbs, in Endpoints

Use resource names (preferably plural) in your URLs instead of verbs like get, create or update. The action is already described by the HTTP method.

// Bad practice ❌
POST /create-user
GET  /get-user-by-id/5

// Good practice ✅
POST /users
GET  /users/5
GET  /users/5/orders      // nested resource: orders of user 5

Keep URLs lowercase, use hyphens for multi-word names (/order-items) and avoid nesting deeper than one or two levels.

3. Return Meaningful HTTP Status Codes

Returning 200 OK for everything and describing errors only inside the JSON body is poor design. Clients, proxies and monitoring tools all understand standard status codes:

  • 200 OK: Request successful (GET, PUT, PATCH).
  • 201 Created: New resource created (POST). Return its URL in the Location header.
  • 204 No Content: Success with no response body (often DELETE).
  • 400 Bad Request: The request is malformed or invalid.
  • 401 Unauthorized: Authentication is missing or invalid.
  • 403 Forbidden: Authenticated, but not allowed to do this.
  • 404 Not Found: The resource does not exist.
  • 409 Conflict: The request conflicts with the current state (for example a duplicate email).
  • 422 Unprocessable Content: Validation errors in otherwise well-formed data.
  • 429 Too Many Requests: The client hit the rate limit.
  • 500 Internal Server Error: An unexpected server-side error.

4. Use a Consistent Error Format

Every error should have the same shape so clients can handle it with one piece of code. A good option is the standard Problem Details format (RFC 9457, application/problem+json):

{
  "type": "https://api.example.com/errors/validation",
  "title": "Validation failed",
  "status": 422,
  "detail": "The request contains invalid fields.",
  "errors": {
    "email": "The email field must contain a valid email address."
  }
}

5. Support Pagination, Filtering and Sorting

Never return an unbounded list. Large collections should be paginated, with a sensible default and a maximum page size:

GET /products?status=active&sort=-created_at&limit=20&cursor=eyJpZCI6MTIzfQ

Cursor-based pagination stays fast on large tables and does not skip or repeat items when new records are added. Return the next cursor in the response so the client does not need to build it.

6. Version Your API From Day One

Once other applications depend on your API, you will eventually need breaking changes. Include a version from the start so old clients keep working while new ones move to the next version:

GET https://api.example.com/v1/users

Only bump the major version for breaking changes. Adding a new optional field is not a breaking change; removing or renaming one is.

7. Document the API

An API is only as good as its documentation. Describe endpoints, parameters, example requests and error responses in an OpenAPI specification. It can generate interactive docs, client SDKs and contract tests from a single file.

How to Secure a REST API

Whether an API is public or internal, security must be designed in from the start. These are the practices that prevent the most common API vulnerabilities, many of which appear in the OWASP API Security Top 10.

1. Use HTTPS Everywhere

Serve the API only over HTTPS (TLS). Without it, tokens, passwords and personal data travel in plain text and can be intercepted. Redirecting HTTP to HTTPS is not enough for API clients; reject plain HTTP requests and enable HSTS.

2. Authenticate With Standard Protocols

Do not invent your own authentication scheme. Use OAuth 2.0 (with OpenID Connect for user login) or signed tokens such as JWT. Keep access tokens short-lived, use refresh tokens to renew them, and send them in the Authorization: Bearer header, never in the URL, where they end up in logs and browser history.

When you validate a JWT, always verify the signature, the expected algorithm, the expiry (exp), the issuer and the audience. For server-to-server integrations, API keys can be enough, but store them hashed and allow them to be rotated.

3. Check Authorization on Every Object

The most common API vulnerability is not broken login; it is missing authorization checks. If GET /invoices/1042 returns the invoice without checking that it belongs to the current user, anyone can change the number and read other users' data. This is known as Broken Object Level Authorization (BOLA).

// ❌ Trusts the ID from the URL
$invoice = $invoiceModel->find($id);

// ✅ Scopes the query to the authenticated user
$invoice = $invoiceModel->where('id', $id)
                        ->where('user_id', $currentUser->id)
                        ->first();

if ($invoice === null) {
    return $this->failNotFound();
}

Apply the same idea to fields: never let a client set properties such as role or is_admin just because they appear in the request body. Accept only an explicit list of allowed fields.

4. Validate All Input

Validate type, length, format and allowed values for every parameter, header and body field on the server, even if the client already validates them. Use parameterized queries or a query builder for database access to prevent SQL injection, and set a maximum request body size.

5. Apply Rate Limiting

Rate limiting protects the API from brute-force attacks, scraping and accidental overload. Limit requests per API key, user or IP (for example 60 requests per minute), apply stricter limits to login and password reset endpoints, and return 429 Too Many Requests with a Retry-After header when the limit is reached.

6. Configure CORS Carefully

If browsers call your API from other domains, allow only the origins you trust. Avoid Access-Control-Allow-Origin: * on endpoints that use cookies or return private data.

7. Do Not Leak Information in Errors

Error responses should help the client fix the request, not help an attacker. Never return stack traces, SQL errors, file paths or framework versions in production. Log the details on the server and return a generic message with a request ID the client can report.

8. Log, Monitor and Protect Secrets

Log authentication failures, permission errors and unusual traffic, and set alerts for spikes. Keep secrets such as database passwords and signing keys in environment variables or a secret manager, never in the repository, and rotate them regularly.

REST API Security Checklist

  • All traffic is served over HTTPS with HSTS enabled.
  • Authentication uses OAuth 2.0 / OpenID Connect or properly validated, short-lived tokens.
  • Every endpoint checks that the user may access the specific object and fields.
  • All input is validated on the server and database access is parameterized.
  • Rate limits are in place, with stricter limits on authentication endpoints.
  • CORS allows only trusted origins.
  • Errors never expose stack traces or internal details.
  • Secrets are stored outside the code and can be rotated.
  • Security events are logged and monitored.

Conclusion

High-quality software is measured not only by its database or backend logic but also by the quality of the interface it exposes to the outside world. Consistent URLs, correct status codes, predictable errors, pagination and versioning make an API pleasant to use; HTTPS, standard authentication, object-level authorization, validation and rate limiting keep it safe. At CodeBlanks we build our own projects, including WantAPI, on these same standards. Keep coding clean!