Principles and practices for designing clear, consistent, and evolvable programming interfaces that facilitate integration between systems.
API design is the discipline of creating programmatic interfaces that are clear, consistent, and evolvable. A well-designed API acts as a contract between systems, defining how consumers can interact with a service in a predictable and reliable manner.
Effective API design goes beyond simply exposing functionality. It requires considering developer experience, long-term system evolution, and integration with architectural patterns like microservices and API Gateway. A well-designed API reduces adoption friction, minimizes integration errors, and enables teams to work independently.
The design process should be iterative and user-centered, starting with contract definition before implementation — an approach known as API-first that aligns with spec-driven development.
| Style | Characteristics | Ideal use cases | Complexity |
|---|---|---|---|
| REST | Resources, HTTP verbs, stateless | CRUD, public APIs, simple integrations | Low |
| GraphQL | Query language, typed schema, single endpoint | Complex frontends, data aggregation | Medium |
| gRPC | Protocol Buffers, streaming, type-safe | Internal microservices, high performance | High |
| WebSocket | Bidirectional, real-time, persistent connection | Chat, notifications, gaming | Medium |
An OpenAPI specification defines a REST API contract declaratively. Example for a users endpoint:
openapi: 3.0.3
info:
title: User Management API
version: 1.0.0
paths:
/users:
get:
summary: List users
parameters:
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: cursor
in: query
schema:
type: string
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/CursorPagination'
components:
schemas:
User:
type: object
required: [id, email, created_at]
properties:
id:
type: string
format: uuid
email:
type: string
format: email
created_at:
type: string
format: date-time
CursorPagination:
type: object
properties:
next_cursor:
type: string
nullable: true
has_more:
type: boolean| Strategy | Implementation | Advantages | Disadvantages |
|---|---|---|---|
| URL Path | /v1/users, /v2/users | Explicit, easy routing | Multiple URLs, cache splitting |
| Query Parameter | /users?version=1 | Flexible, same endpoint | Easy to omit, less explicit |
| Header | Accept: application/vnd.api+json;version=1 | Clean, HTTP standard | Less visible, complex debugging |
| Content Negotiation | Accept: application/vnd.myapi.v1+json | HTTP standard, granular | Complex configuration |
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTIzfQ==",
"has_more": true,
"limit": 20
}
}Advantages: Consistent with changing data, efficient for large datasets Disadvantages: Cannot jump to specific pages
{
"data": [...],
"pagination": {
"offset": 40,
"limit": 20,
"total": 1250,
"has_more": true
}
}Advantages: Familiar, allows direct navigation Disadvantages: Inconsistent with insertions/deletions, inefficient on large datasets
Following RFC 9457 (Problem Details for HTTP APIs), which replaces RFC 7807:
{
"type": "https://api.example.com/problems/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "The request body contains invalid data",
"instance": "/users/create",
"errors": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email must be a valid email address"
}
],
"trace_id": "abc123def456"
}| Algorithm | Description | Use cases |
|---|---|---|
| Token Bucket | Tokens regenerate at fixed rate | APIs with allowed bursts |
| Fixed Window | Fixed limit per time window | Simple implementation |
| Sliding Window | More precise sliding window | Balance between precision and performance |
| Leaky Bucket | Processes requests at constant rate | Smooth irregular traffic |
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 742
X-RateLimit-Reset: 1640995200
Retry-After: 3600
Integration with OAuth/OIDC is fundamental for modern APIs. Key considerations:
APIs should integrate natively with observability:
/health, /ready)A poorly designed API becomes permanent technical debt. Every design decision — resource naming, response structure, error handling — solidifies once there are consumers in production. Changing a public API requires versioning, client migration, and coordination between teams.
The cost of redesigning an API after launch is exponentially higher than investing in initial design. A well-designed API reduces integration time from weeks to days, minimizes support tickets, and enables product teams to move faster. In organizations with multiple teams, consistent APIs reduce cognitive load and enable reuse of tools and patterns.
Development methodology where the specification is written before the code, serving as a contract between teams and as the source of truth for implementation.
Architectural style structuring an application as a collection of small, independent, deployable services, each with its own business logic and data.
Pattern providing a single entry point for multiple microservices, handling routing, authentication, rate limiting, and response aggregation.
Industry standards for delegated authorization (OAuth 2.0) and federated authentication (OpenID Connect), enabling third-party login and secure API access.
Ability to understand a system's internal state from its external outputs: logs, metrics, and traces, enabling problem diagnosis without direct system access.
Principles for designing development kits that are intuitive, consistent, and facilitate service integration across multiple programming languages.
Software design approach centering development on the business domain, using a ubiquitous language shared between developers and domain experts.
Principles for designing intuitive, consistent, and productive command-line interfaces that developers enjoy using.
Architectural pattern where each client type has its own dedicated backend adapting microservice APIs to that client's specific needs.
AWS managed service for creating, publishing, and managing REST, HTTP, and WebSocket APIs that act as entry points to Lambda functions and other backend services.
Practices and tools for documenting APIs clearly, interactively, and maintainably, from OpenAPI specifications to documentation portals.