API Design Spec Template for PMs
A product manager's API design spec template. Define endpoints, request/response shapes, auth, rate limits, and versioning — before engineering writes a single line. Free to copy, download, and use. No signup required.
# API Design Spec Template
**API Name:** [Name]
**Version:** v1
**PM:** [Name]
**Tech Lead:** [Name]
**Date:** [Date]
**Status:** Draft / In Review / Approved
---
## 1. Purpose
> What does this API enable? Who are the consumers (mobile app, web frontend, third-party integrations)?
[Write here]
---
## 2. Base URL
| Environment | Base URL |
|---|---|
| Development | http://localhost:8000/api/v1 |
| Staging | https://staging-api.yourproduct.com/api/v1 |
| Production | https://api.yourproduct.com/api/v1 |
---
## 3. Authentication
| Method | Description |
|---|---|
| JWT Bearer Token | `Authorization: Bearer <token>` in header |
| API Key | `X-API-Key: <key>` in header (for server-to-server) |
Token expiry: 24 hours
Refresh mechanism: POST /auth/refresh with refresh_token
---
## 4. Endpoints
### [Resource Name]
#### List
```
GET /[resource]/
```
**Query Parameters:**
| Param | Type | Required | Description |
|---|---|---|---|
| page | integer | No | Default: 1 |
| page_size | integer | No | Default: 20, max: 100 |
| status | string | No | Filter by status |
**Response 200:**
```json
{
"count": 42,
"next": "/api/v1/[resource]/?page=2",
"previous": null,
"results": [
{
"id": "uuid",
"name": "Example",
"status": "active",
"created_at": "2026-04-01T00:00:00Z"
}
]
}
```
---
#### Create
```
POST /[resource]/
```
**Request Body:**
```json
{
"name": "string (required, 3–255 chars)",
"status": "active | archived (optional, default: active)"
}
```
**Response 201:**
```json
{
"id": "uuid",
"name": "Example",
"status": "active",
"created_at": "2026-04-01T00:00:00Z"
}
```
**Errors:**
| Status | Code | Message |
|---|---|---|
| 400 | VALIDATION_ERROR | name is required |
| 401 | UNAUTHORIZED | Token missing or expired |
| 403 | FORBIDDEN | User lacks permission |
---
#### Retrieve
```
GET /[resource]/{id}/
```
**Response 200:** Same as single object above.
**Response 404:** `{ "code": "NOT_FOUND", "message": "Resource not found" }`
---
#### Update
```
PATCH /[resource]/{id}/
```
Partial update — only fields provided are updated.
---
#### Delete
```
DELETE /[resource]/{id}/
```
**Response 204:** No body.
---
## 5. Error Format
All errors return a consistent structure:
```json
{
"code": "ERROR_CODE",
"message": "Human-readable description",
"details": {}
}
```
| HTTP Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Request body fails validation |
| 401 | UNAUTHORIZED | Missing or invalid token |
| 403 | FORBIDDEN | Valid token but insufficient permissions |
| 404 | NOT_FOUND | Resource doesn't exist |
| 409 | CONFLICT | Duplicate resource or state conflict |
| 429 | RATE_LIMITED | Too many requests |
| 500 | INTERNAL_ERROR | Unexpected server error |
---
## 6. Rate Limits
| Endpoint Group | Limit | Window |
|---|---|---|
| Auth endpoints | 10 requests | per minute |
| Read endpoints | 1,000 requests | per hour |
| Write endpoints | 100 requests | per hour |
Rate limit headers returned on all responses:
- `X-RateLimit-Limit`
- `X-RateLimit-Remaining`
- `X-RateLimit-Reset`
---
## 7. Versioning Strategy
- Current version: **v1**
- Breaking changes get a new version (v2)
- Non-breaking additions (new optional fields, new endpoints) can be added to v1
- v1 will be supported for minimum 12 months after v2 launches
- Deprecation announced via API response header: `Deprecation: true`
---
## 8. Pagination
All list endpoints use cursor-based or page-based pagination:
| Type | When to Use |
|---|---|
| Page-based | < 10,000 records, user-facing tables |
| Cursor-based | > 10,000 records, infinite scroll, exports |
---
## 9. Open Questions
| Question | Owner | Due |
|---|---|---|
| | | |How to use this API Design Spec template
Define consumers before defining endpoints
Who calls this API? The mobile app? The web frontend? A webhook from Slack? Different consumers have different pagination, auth, and error-handling needs. Write consumers first — endpoints follow from what each consumer needs.
Agree on the error format before writing any endpoint
Inconsistent error shapes are the #1 complaint from frontend engineers. Define the error object structure once in Section 5, then reference it in every endpoint. Never let individual endpoints invent their own error formats.
Write request and response JSON with real example values
JSON with placeholder 'string' types is better than nothing, but JSON with real values ('name': 'Bulk CSV export') lets reviewers spot semantic mistakes (wrong field names, missing fields) that type-only specs miss.
Set rate limits before shipping, not after
Rate limits defined after launch feel arbitrary and break existing integrations. Define them in this spec, communicate them to integrators on day one, and include the rate limit headers so clients can manage their own throttling.
Want a API Design Spec grounded in your actual customer data?
PMRead ingests your customer interviews, feedback, and Slack threads — and generates PRDs backed by real evidence, not guesses.
Frequently asked questions
Should PMs write API specs or leave it entirely to engineers?
PMs should define what the API needs to do: what data flows in, what flows out, what business rules apply, and what consumers need. Engineers define how it's implemented. The spec is the PM's deliverable; the implementation is engineering's. Leaving the spec entirely to engineering produces technically correct but product-incorrect APIs.
When should we version an API?
Version when a change breaks existing consumers: removing a field, renaming a field, changing a field's type, or removing an endpoint. Adding new optional fields, adding new endpoints, or adding new optional query params are non-breaking — they don't require a new version.
What's the difference between REST and GraphQL for internal APIs?
REST is simpler to reason about, cache, and document for teams where the PM is involved in the spec. GraphQL gives frontends more flexibility to query exactly what they need, reducing over-fetching — better for complex, data-intensive UIs with many different query patterns. For most product features, REST is the right default.
How do I handle API changes after the spec is approved?
Non-breaking changes (new optional fields, new endpoints) can be added without a new version — just update the spec and notify consumers. Breaking changes require a new version number, a migration guide, and a deprecation timeline for the old version. Document both in a changelog.
Other free templates