What This Template Is For
APIs are the attack surface most product teams underestimate. Your UI might enforce perfect validation, but every API endpoint is accessible to anyone with curl and your base URL. A single misconfigured endpoint can expose customer data, allow privilege escalation, or enable denial-of-service attacks that take down your entire product.
This template provides a systematic approach to API security covering five layers: authentication (who is calling), authorization (what they can access), input validation (what they can send), rate limiting (how often they can call), and monitoring (how you detect abuse). It works for REST APIs, GraphQL endpoints, and webhook receivers.
Most teams bolt on security after the API is built. That approach leads to inconsistent enforcement, where some endpoints check auth and validate input while others skip it because "it is an internal endpoint." This template helps you define security policies before implementation, so every endpoint is covered from day one.
For PMs working on API products, the Technical PM Handbook covers the architecture decisions that affect API design. If you are also designing access control for your product's UI, the access control template covers role-based permissions. For a full security posture review, see the enterprise security review template.
How to Use This Template
- Inventory your endpoints. List every API endpoint, its HTTP method, and who should be able to call it.
- Choose your auth strategy. Select from the authentication options in Part 1 based on your use case.
- Define authorization rules. Map each endpoint to the roles or scopes that can access it.
- Set rate limits. Define limits per tier, per endpoint, and per client.
- Document input validation rules. For each endpoint, specify expected input types, ranges, and sanitization requirements.
- Configure monitoring and alerting. Define what abnormal traffic looks like and how you will respond.
The Template
Part 1: Authentication Strategy
Select the authentication mechanism for each client type. Most products need more than one.
| Client Type | Auth Mechanism | Token Lifetime | Refresh Strategy | Notes |
|---|---|---|---|---|
| Web application (SPA) | OAuth 2.0 + PKCE | 15 min access token | Refresh token (7 days, rotated) | Use httpOnly cookies for token storage, not localStorage |
| Mobile application | OAuth 2.0 + PKCE | 15 min access token | Refresh token (30 days, rotated) | Pin SSL certificates to prevent MITM |
| Server-to-server | API key + HMAC signing | No expiration (rotated quarterly) | Manual rotation with overlap window | Require HMAC signature on all mutating requests |
| Third-party integrations | OAuth 2.0 client credentials | 1 hour access token | Re-authenticate on expiry | Scope tokens to specific resources |
| Webhooks (outbound) | HMAC signature verification | Per-request | N/A | Include timestamp in signature to prevent replay |
| Internal microservices | mTLS or service mesh tokens | Short-lived (5 min) | Auto-renewed by mesh | Zero-trust between services |
Authentication checklist:
- ☐ All endpoints require authentication (no accidental public endpoints)
- ☐ Tokens are transmitted only over HTTPS (HSTS enforced)
- ☐ Access tokens are short-lived (< 1 hour for most use cases)
- ☐ Refresh tokens are rotated on every use (detect token theft via reuse)
- ☐ API keys are hashed at rest in the database (never stored in plaintext)
- ☐ Failed authentication returns 401 with a generic message (no information leakage)
- ☐ Brute-force protection: lock out after 10 failed attempts per IP per 15 minutes
- ☐ All authentication events are logged (success and failure)
Part 2: Authorization and Scoping
Define what each authenticated identity can access. Use the principle of least privilege.
Endpoint authorization matrix:
| Endpoint | Method | Required Role/Scope | Resource Scoping | Notes |
|---|---|---|---|---|
/api/v1/users | GET | admin:read | Org-scoped | Returns only users in the caller's organization |
/api/v1/users | POST | admin:write | Org-scoped | Cannot create users with higher privilege than caller |
/api/v1/projects | GET | projects:read | Org-scoped + membership | Returns only projects the caller is a member of |
/api/v1/projects/:id | PUT | projects:write + owner | Resource-scoped | Only project owner or Admin can update |
/api/v1/projects/:id | DELETE | admin:write | Resource-scoped | Soft-delete with 30-day recovery window |
/api/v1/billing | GET | billing:read | Org-scoped | Separate from user data access |
/api/v1/exports | POST | data:export | Org-scoped | Rate limited to 1 per hour |
/api/v1/webhooks | POST | integrations:write | Org-scoped | Validate webhook URL is not internal |
Authorization checklist:
- ☐ Every endpoint explicitly declares its required scope or role
- ☐ Authorization is checked at the API layer, not just the UI
- ☐ Resource-level scoping prevents cross-tenant data access (IDOR prevention)
- ☐ Privilege escalation is blocked: users cannot grant roles higher than their own
- ☐ Bulk endpoints (list, export) enforce the same authorization as single-resource endpoints
- ☐ 403 responses do not reveal whether the resource exists (return 404 for unauthorized access)
Part 3: Rate Limiting
Define rate limits to prevent abuse, protect infrastructure, and ensure fair usage.
| Tier | Global Limit | Per-Endpoint Limit | Burst Allowance | Retry-After Header | Scope |
|---|---|---|---|---|---|
| Free | 100 req/min | 20 req/min | 10 req burst | Yes | Per API key |
| Standard | 500 req/min | 100 req/min | 50 req burst | Yes | Per API key |
| Enterprise | 2,000 req/min | 500 req/min | 200 req burst | Yes | Per API key |
| Internal | 10,000 req/min | 2,000 req/min | 1,000 req burst | Yes | Per service |
Endpoint-specific limits:
| Endpoint Pattern | Additional Limit | Reason |
|---|---|---|
POST /auth/* | 10 req/min per IP | Brute-force prevention |
POST /exports | 1 req/hour per org | Resource-intensive operation |
POST /uploads | 20 req/hour per user | Storage cost control |
GET /search | 60 req/min per user | Database query cost |
| Webhook deliveries | 3 retries, exponential backoff | Prevent overwhelming receivers |
Rate limiting checklist:
- ☐ Rate limits are enforced at the API gateway, not in application code
- ☐ Limits use a sliding window algorithm (not fixed windows that allow bursts at boundaries)
- ☐ Rate limit headers are included in every response (
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset) - ☐ 429 responses include
Retry-Afterheader with seconds until next allowed request - ☐ Rate limits are documented in your API reference for each endpoint
- ☐ Different limits for read vs. write operations (reads are cheaper)
Part 4: Input Validation
Define validation rules for every input your API accepts.
Validation rules by input type:
| Input Type | Validation Rules | Sanitization | Max Length |
|---|---|---|---|
| Email addresses | RFC 5322 format, lowercase normalized | HTML entity encoding on output | 254 chars |
| User names | Alphanumeric + limited special chars | Strip HTML tags, trim whitespace | 100 chars |
| Free text fields | UTF-8 only | HTML entity encoding on output, no script tags | 10,000 chars |
| URLs | Valid scheme (https only), no internal IPs | Resolve and check against SSRF blocklist | 2,048 chars |
| File uploads | Validate MIME type + magic bytes, not just extension | Virus scan, strip metadata | 50 MB |
| Numeric IDs | Positive integer, within valid range | Type coercion to integer | N/A |
| JSON payloads | Schema validation (JSON Schema or Zod) | Reject unknown properties | 1 MB |
| Query parameters | Allowlist of accepted params | Type coercion, range clamping | 500 chars per param |
| Pagination | page > 0, limit between 1-100 | Default to limit=20 if not specified | N/A |
Validation checklist:
- ☐ All inputs validated on the server side (never trust client-side validation alone)
- ☐ JSON schema validation rejects payloads with unknown properties
- ☐ SQL injection prevention: use parameterized queries exclusively (no string concatenation)
- ☐ XSS prevention: all user-generated content is escaped on output
- ☐ SSRF prevention: URL inputs are validated against an internal IP blocklist
- ☐ File upload validation checks MIME type via magic bytes, not just file extension
- ☐ Request body size limits enforced at the web server and API gateway level
- ☐ Error messages do not expose internal implementation details or stack traces
Part 5: Monitoring and Incident Response
| Signal | Threshold | Alert Channel | Response |
|---|---|---|---|
| Auth failure spike | > 50 failures/min from single IP | PagerDuty (P2) | Auto-block IP for 1 hour, investigate |
| Rate limit violations | > 100 429s/min from single key | Slack #security | Review client, consider key revocation |
| Unusual data export | > 10x normal export volume | PagerDuty (P1) | Pause export, verify with account owner |
| 500 error spike | > 5% error rate sustained for 5 min | PagerDuty (P1) | Investigate root cause, check for attack |
| New API key usage pattern | Key used from new IP/region | Slack #security | Log and monitor, no auto-action |
| Schema validation failures | > 20 rejections/min from single client | Slack #engineering | Likely integration bug, contact client |
Monitoring checklist:
- ☐ All API requests are logged with: timestamp, client ID, endpoint, method, status code, response time, request size
- ☐ Logs do not contain sensitive data (tokens, passwords, PII in request bodies)
- ☐ Anomaly detection alerts on unusual traffic patterns (volume, geography, error rates)
- ☐ Security events are streamed to SIEM for correlation with other signals
- ☐ Incident response runbook exists for API security incidents (see incident response template)
Filled Example: ProjectFlow REST API
Product: ProjectFlow, a B2B project management API serving 500 integration partners.
Authentication implementation:
- Third-party integrations use OAuth 2.0 client credentials with 1-hour access tokens
- Web application uses OAuth 2.0 + PKCE with 15-minute access tokens and 7-day refresh tokens
- All tokens are JWT with RS256 signing. Public key published at
/.well-known/jwks.json - API keys are SHA-256 hashed at rest. Only the first 8 characters are shown in the dashboard for identification.
Rate limiting results (Q1 2026):
- Implemented sliding window rate limiting at the API gateway (Cloudflare Workers)
- Average utilization: 12% of limit for Standard tier, 3% for Enterprise tier
- Blocked 47 brute-force attempts and 3 credential stuffing attacks in January
- Zero false-positive blocks reported by legitimate customers
Top security finding from last audit:
- Three internal endpoints (
/api/internal/health,/api/internal/metrics,/api/internal/config) were accessible without authentication due to a misconfigured route prefix. Remediated within 2 hours of discovery. Added route-level auth checks and automated tests to prevent recurrence.
Common Mistakes
- Trusting the Authorization header blindly. Validate JWTs on every request. Check expiration, issuer, audience, and signature. Do not just decode and trust.
- Inconsistent auth enforcement. Some endpoints check auth, some do not. Use middleware that applies authentication to all routes by default, with explicit opt-out for public endpoints.
- Logging sensitive data. Request logs should never contain access tokens, passwords, or credit card numbers. Implement structured logging with a sensitive field blocklist.
- Rate limiting by user ID only. Unauthenticated endpoints need IP-based rate limiting. Authenticated endpoints need both user-based and organization-based limits to prevent a single API key from consuming all quota.
- Ignoring SSRF in URL inputs. If your API accepts URLs (webhooks, imports, avatars), validate that they do not resolve to internal IPs or cloud metadata endpoints.
For understanding how API security fits into your overall product security strategy, the Technical PM Handbook covers the full technical decision landscape. The encryption template covers how to protect data at rest and in transit across your API infrastructure.
