API design best practices define principles for creating clean, consistent, and developer-friendly interfaces — establishing conventions for endpoints, methods, error handling, versioning, and documentation that make APIs intuitive to use and maintainable long-term, especially important for LLM services where good design impacts developer experience and adoption.
Why API Design Matters
- Developer Experience: Good APIs are easy to understand and use.
- Adoption: Clean APIs encourage integration and usage.
- Maintenance: Consistent patterns reduce support burden.
- Evolution: Good versioning enables growth without breaking users.
- Scale: Well-designed APIs handle traffic and feature growth.
REST API Fundamentals
Resource-Based URLs:
``
Good:
GET /users # List users
GET /users/{id} # Get specific user
POST /users # Create user
PUT /users/{id} # Update user
DELETE /users/{id} # Delete user
Bad:
GET /getUsers
POST /createUser
POST /deleteUser/{id}
`
HTTP Methods:
``
Method | Purpose | Idempotent | Safe
--------|-----------------|------------|------
GET | Read resource | Yes | Yes
POST | Create resource | No | No
PUT | Replace/update | Yes | No
PATCH | Partial update | No* | No
DELETE | Remove resource | Yes | No
Response Codes:
``
Code | Meaning | When to Use
-----|----------------------|----------------------------
200 | OK | Successful GET, PUT, PATCH
201 | Created | Successful POST (resource created)
204 | No Content | Successful DELETE
400 | Bad Request | Invalid input from client
401 | Unauthorized | Missing/invalid auth
403 | Forbidden | Auth valid, but no permission
404 | Not Found | Resource doesn't exist
429 | Too Many Requests | Rate limited
500 | Internal Server Error| Server-side failure
LLM API Design Patterns
Chat Completions Pattern (OpenAI-style):
`json
POST /v1/chat/completions
{
"model": "gpt-4o",
"messages": [
{"role": "system", "content": "You are helpful."},
{"role": "user", "content": "Hello!"}
],
"temperature": 0.7,
"max_tokens": 1000,
"stream": false
}
Response:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-4o",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21
}
}
`
Streaming Response (SSE):
`
POST /v1/chat/completions
{ "stream": true, ... }
Response (text/event-stream):
data: {"id":"abc","choices":[{"delta":{"content":"Hello"}}]}
data: {"id":"abc","choices":[{"delta":{"content":"!"}}]}
data: {"id":"abc","choices":[{"delta":{},"finish_reason":"stop"}]}
data: [DONE]
`
REST vs. gRPC
``
Aspect | REST | gRPC
-------------|-------------------|-------------------
Format | JSON (text) | Protobuf (binary)
Speed | Good | 2-10× faster
Browser | Native support | Needs proxy
Streaming | SSE/WebSocket | Native bidirectional
Tooling | Ubiquitous | Growing
Learning | Easy | Steeper curve
Best For | Public APIs | Internal services
Versioning Strategies
``
Strategy | Example | Pros/Cons
-------------|------------------------|--------------------
URL path | /v1/users, /v2/users | Clear, cacheable
Header | Accept: application/vnd.api+json;v=2 | Clean URLs, harder
Query param | /users?version=2 | Simple, less RESTful
Error Handling
Standard Error Response:
`json`
{
"error": {
"code": "invalid_request_error",
"message": "The 'model' field is required.",
"type": "invalid_request_error",
"param": "model"
}
}
Error Best Practices:
- Use consistent error format across all endpoints.
- Include actionable error messages.
- Log request ID for debugging.
- Don't expose internal details in production.
Pagination
Cursor-Based (preferred for real-time data):
`json
GET /messages?limit=20&after=cursor_abc123
Response:
{
"data": [...],
"has_more": true,
"next_cursor": "cursor_xyz789"
}
`
Offset-Based (simpler, less efficient):
`json
GET /users?limit=20&offset=40
Response:
{
"data": [...],
"total": 1000,
"limit": 20,
"offset": 40
}
`
Rate Limiting
Headers to Include:
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1677652288
Retry-After: 60
Best Practices
- Be Consistent: Same patterns across all endpoints.
- Be Predictable: Developers should guess correctly.
- Be Complete: Include all needed info in responses.
- Document Everything: OpenAPI/Swagger specs.
- Version Early: Plan for evolution from day one.
- Test Thoroughly: Automated API contract tests.
API design is the user interface for developers — well-designed APIs make integration easy and enjoyable, while poor APIs create friction that slows adoption and increases support burden, making API design a critical skill for building successful developer products.