What This Template Is For
An API design specification defines the contract between your service and its consumers before any code is written. It captures endpoints, data shapes, authentication, error formats, versioning rules, and pagination patterns in a single document that frontend engineers, partner teams, and third-party developers can all build against.
Skipping the design phase leads to inconsistent naming, undocumented error codes, and breaking changes that force consumers to rewrite their integrations. A written spec prevents these problems by making every design decision reviewable and reversible before it becomes cemented in production code.
This template works for both REST APIs and GraphQL services. It is scoped to the API surface, not the underlying architecture. If you need to document database schemas, service dependencies, and rollout plans alongside your API, use the Technical Specification Template instead. For the broader context of how API design fits into the technical PM workflow, see the Technical PM Handbook.
If you are deciding whether to build an API feature, score it with the RICE Calculator first.
How to Use This Template
- Start after the product requirements are finalized but before backend implementation begins. The author should be the API owner or lead backend engineer.
- Define the resource model first. List every resource (noun) your API exposes and map the relationships between them. This becomes the foundation for endpoint design.
- Write endpoint contracts with enough detail for consumers to mock their integrations. Include request/response schemas, field types, required/optional annotations, and all status codes.
- Choose a versioning strategy and document it explicitly. Consumers need to know how you will handle breaking changes before they commit to integrating.
- Define the authentication model. Specify token types, scopes, and how API keys differ from user tokens.
- Document error responses with a consistent format. Every error should include a machine-readable code, a human-readable message, and a documentation link.
- Circulate the draft to frontend engineers, partner teams, and anyone who will consume the API. Use the Open Questions section for unresolved design decisions.
The Template
API Overview
| Field | Details |
|---|---|
| API Name | [Name] |
| Version | [v1, v2, etc.] |
| Base URL | [https://api.example.com/v1] |
| Author | [Engineer name] |
| Reviewers | [Names] |
| Date | [Date] |
| Status | Draft / In Review / Approved / Implemented |
Summary. [1-2 sentences describing what this API does and who its primary consumers are.]
Design Principles.
- ☐ RESTful resource naming (nouns, not verbs)
- ☐ Consistent error response format across all endpoints
- ☐ Pagination on all list endpoints
- ☐ Idempotent write operations where possible
- ☐ Backward-compatible changes by default
Versioning Strategy
| Decision | Value |
|---|---|
| Strategy | URL path (/v1/) / Header (Accept-Version) / Query param (?version=1) |
| Breaking Change Policy | [How long old versions are supported after a new version ships] |
| Deprecation Notice Period | [Minimum days/weeks before removal] |
| Sunset Header | Yes / No |
What constitutes a breaking change:
- ☐ Removing an endpoint
- ☐ Removing or renaming a response field
- ☐ Changing the type of an existing field
- ☐ Adding a required request parameter
- ☐ Changing authentication requirements
What is NOT a breaking change:
- ☐ Adding a new optional request parameter
- ☐ Adding a new response field
- ☐ Adding a new endpoint
- ☐ Adding a new error code
Authentication
| Property | Value |
|---|---|
| Auth Type | OAuth 2.0 / API Key / JWT / Bearer Token |
| Token Location | Authorization header / Query param / Cookie |
| Token Lifetime | [Duration] |
| Refresh Mechanism | [How tokens are refreshed] |
| Scopes | [List of scopes and what each permits] |
Rate limits by auth type:
| Auth Type | Requests/Minute | Burst Limit |
|---|---|---|
| API Key (free) | [Limit] | [Burst] |
| API Key (paid) | [Limit] | [Burst] |
| OAuth token | [Limit] | [Burst] |
Resource Model
| Resource | Description | Relationships |
|---|---|---|
| [Resource 1] | [What it represents] | [Has many X, belongs to Y] |
| [Resource 2] | [What it represents] | [Has many X, belongs to Y] |
Endpoints
[Resource]: [Method] [Path]
| Property | Value |
|---|---|
| Method | GET / POST / PUT / PATCH / DELETE |
| Path | /v1/[resource] |
| Auth | Required / Optional / None |
| Scopes | [Required scopes] |
| Rate Limit | [Requests per minute] |
| Idempotent | Yes / No |
Request body:
{
"field_1": "string (required)",
"field_2": 123,
"field_3": true
}
Response (200/201):
{
"id": "uuid",
"field_1": "string",
"created_at": "2026-03-04T00:00:00Z",
"updated_at": "2026-03-04T00:00:00Z"
}
Error responses:
| Status | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR | [When this occurs] |
| 401 | UNAUTHORIZED | [When this occurs] |
| 403 | FORBIDDEN | [When this occurs] |
| 404 | NOT_FOUND | [When this occurs] |
| 409 | CONFLICT | [When this occurs] |
| 429 | RATE_LIMITED | [When this occurs] |
[Repeat for each endpoint]
Pagination
| Property | Value |
|---|---|
| Strategy | Cursor-based / Offset-based |
| Default Page Size | [Number] |
| Max Page Size | [Number] |
| Cursor Field | [Field name] |
Example response:
{
"data": [],
"pagination": {
"cursor": "eyJpZCI6...",
"has_more": true,
"total_count": 142
}
}
Error Format
All errors follow this structure:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable description",
"details": [
{ "field": "email", "issue": "Invalid email format" }
],
"doc_url": "https://docs.example.com/errors/VALIDATION_ERROR"
}
}
Open Questions
| # | Question | Owner | Status | Decision |
|---|---|---|---|---|
| 1 | [Question] | [Name] | Open | |
| 2 | [Question] | [Name] | Open |
Filled Example: Project Management API v1
API Overview
| Field | Details |
|---|---|
| API Name | TaskFlow API |
| Version | v1 |
| Base URL | https://api.taskflow.io/v1 |
| Author | Jordan Park, Senior Backend Engineer |
| Reviewers | Lisa Tran (PM), Dev Patel (Frontend), Maya Santos (Partner Eng) |
| Date | March 2026 |
| Status | Approved |
Summary. The TaskFlow API enables third-party integrations and internal clients to create, read, update, and manage projects, tasks, and team members. Primary consumers are the web/mobile frontend, Zapier integration, and 3 strategic partner applications.
Endpoints (excerpt)
Tasks: POST /v1/projects/:project_id/tasks
| Property | Value |
|---|---|
| Method | POST |
| Path | /v1/projects/:project_id/tasks |
| Auth | Required (Bearer token) |
| Scopes | tasks:write |
| Rate Limit | 120/min |
| Idempotent | Yes (via Idempotency-Key header) |
Request body:
{
"title": "Update onboarding flow (required)",
"description": "Redesign the first-run experience based on user research findings",
"assignee_id": "usr_7k2m9n",
"priority": "high",
"due_date": "2026-04-15",
"labels": ["ux", "onboarding"]
}
Response (201):
{
"id": "tsk_3a8b2c",
"project_id": "prj_1x4y7z",
"title": "Update onboarding flow",
"description": "Redesign the first-run experience based on user research findings",
"assignee": { "id": "usr_7k2m9n", "name": "Alex Rivera" },
"priority": "high",
"status": "todo",
"due_date": "2026-04-15",
"labels": ["ux", "onboarding"],
"created_at": "2026-03-04T10:30:00Z",
"updated_at": "2026-03-04T10:30:00Z"
}
Versioning Strategy
URL path versioning (/v1/). Breaking changes ship in a new version (/v2/). Previous versions are supported for 12 months after the new version launches. A Sunset response header is added to deprecated version responses 90 days before end-of-life. Non-breaking additions (new fields, new endpoints) ship within the current version without a version bump.
Key Takeaways
- Define the resource model and relationships before writing any endpoint contracts
- Choose a versioning strategy and document what constitutes a breaking change
- Use a consistent error format with machine-readable codes and documentation links
- Design pagination into every list endpoint from the start
- Circulate the spec to consumers before implementation begins
About This Template
Created by: Tim Adair
Last Updated: 3/4/2026
Version: 1.0.0
License: Free for personal and commercial use
