APIs Are Products
When we build APIs at StrikingWeb, we treat them as products with their own users — other developers. Just as a well-designed user interface makes an application pleasant to use, a well-designed API makes integration straightforward and enjoyable. A poorly designed API, on the other hand, generates support tickets, causes integration bugs, and frustrates everyone involved.
This guide covers the principles and conventions we follow when designing REST APIs for our clients, whether they are building internal microservices, public developer platforms, or mobile application backends.
Use Nouns for Resource URLs
REST is built around the concept of resources, and your URLs should reflect this. Use nouns to name resources, not verbs. The HTTP method (GET, POST, PUT, DELETE) already specifies the action being performed.
// Good - nouns representing resources
GET /api/users // List users
GET /api/users/42 // Get specific user
POST /api/users // Create a user
PUT /api/users/42 // Update a user
DELETE /api/users/42 // Delete a user
// Bad - verbs in URLs
GET /api/getUsers
POST /api/createUser
POST /api/deleteUser/42
Use plural nouns for collections (/users, not /user) and lowercase with hyphens for multi-word resources (/order-items, not /orderItems or /order_items).
Use HTTP Methods Correctly
Each HTTP method has a specific semantic meaning. Using them correctly makes your API predictable:
- GET: Retrieve data. Must be safe (no side effects) and idempotent (same result when called multiple times).
- POST: Create a new resource. Not idempotent — calling it twice creates two resources.
- PUT: Replace an entire resource. Idempotent — calling it multiple times with the same data produces the same result.
- PATCH: Partially update a resource. Use when you only want to modify specific fields rather than replacing the entire resource.
- DELETE: Remove a resource. Idempotent — deleting a resource that is already deleted should not return an error.
Return Proper HTTP Status Codes
Status codes communicate the result of an API request. Using them properly eliminates ambiguity:
- 200 OK: Successful GET, PUT, or PATCH request
- 201 Created: Successful POST request that created a resource. Include a Location header with the URL of the new resource.
- 204 No Content: Successful DELETE request. No body needed.
- 400 Bad Request: The request is malformed or contains invalid data. Include a clear error message explaining what is wrong.
- 401 Unauthorized: Authentication is required but was not provided or is invalid.
- 403 Forbidden: The authenticated user does not have permission to perform this action.
- 404 Not Found: The requested resource does not exist.
- 422 Unprocessable Entity: The request is well-formed but contains validation errors.
- 429 Too Many Requests: Rate limit exceeded. Include a Retry-After header.
- 500 Internal Server Error: Something went wrong on the server. Never expose stack traces or internal details.
Design Meaningful Error Responses
Error responses should help developers fix the problem quickly. A status code alone is not enough. Include a structured error body:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"message": "Must be a valid email address"
},
{
"field": "age",
"message": "Must be a positive integer"
}
]
}
}
Use a consistent error format across all endpoints. Include a machine-readable error code that clients can programmatically handle, a human-readable message for debugging, and field-level details for validation errors.
Implement Pagination
Any endpoint that returns a collection of resources should support pagination. Returning unbounded lists is a performance risk and can crash clients that attempt to load thousands of records into memory.
We recommend cursor-based pagination for real-time data and offset-based pagination for simpler use cases:
// Offset-based pagination
GET /api/users?page=2&per_page=25
// Response includes pagination metadata
{
"data": [...],
"meta": {
"current_page": 2,
"per_page": 25,
"total": 150,
"total_pages": 6
}
}
Set sensible defaults (e.g., 25 items per page) and enforce maximum limits (e.g., 100 items per page) to prevent clients from requesting excessively large pages.
Version Your API
APIs evolve over time, and breaking changes are sometimes unavoidable. Versioning allows you to introduce changes without breaking existing integrations. The most common approaches:
- URL versioning:
/api/v1/users,/api/v2/users. The simplest and most visible approach. Easy for developers to understand and switch between versions. - Header versioning:
Accept: application/vnd.myapi.v2+json. Keeps URLs clean but is less discoverable. - Query parameter:
/api/users?version=2. Simple but feels less RESTful.
We prefer URL versioning for most projects because it is explicit, easy to test in a browser, and clearly communicates which version is being used. Whichever approach you choose, commit to supporting old versions for a reasonable deprecation period (typically 6-12 months) and communicate upcoming changes through changelogs and deprecation warnings in response headers.
Use Filtering, Sorting, and Searching
Collection endpoints should support query parameters for filtering, sorting, and searching to give clients flexibility without requiring new endpoints for every data variation:
// Filtering
GET /api/users?status=active&role=admin
// Sorting
GET /api/users?sort=-created_at,name
// Prefix with - for descending order
// Searching
GET /api/users?search=john
// Combining
GET /api/users?status=active&sort=-created_at&page=1&per_page=25
Authentication and Security
API security is non-negotiable. Here are the essentials:
- Always use HTTPS. Never serve an API over unencrypted HTTP. All data, including authentication tokens, is visible in plaintext over HTTP.
- Use token-based authentication. JWT (JSON Web Tokens) or API keys transmitted in the Authorization header are the standard approaches. Avoid session-based authentication for APIs.
- Implement rate limiting. Protect your API from abuse by limiting the number of requests per time window. Return 429 status codes with clear retry information when limits are exceeded.
- Validate all input. Never trust client-provided data. Validate data types, lengths, formats, and ranges on every request. Use parameterized queries to prevent SQL injection.
- Use CORS headers carefully. Only allow origins that should have access to your API. Avoid wildcard CORS in production.
Document Your API
An undocumented API is unusable. Good documentation should include:
- Authentication instructions with working code examples
- Complete endpoint listing with request and response examples
- Error code reference with explanations of each error
- Rate limit information
- SDKs or client libraries for popular languages
- A changelog tracking all modifications
Tools like Swagger (OpenAPI), Postman, and Insomnia can help generate interactive documentation from your API specification. At StrikingWeb, we use OpenAPI specifications to maintain documentation that stays synchronized with the actual implementation.
A well-designed API reduces integration friction, lowers support costs, and makes your platform more attractive to developers. If you are building an API — whether for internal consumption or as a public developer platform — investing in good design upfront pays dividends throughout the API's lifetime.