[ADP-138] Rate Limit Headers
reviewing phase 1
Response to rate limiting should be unified.
Why Use Rate Limiting Headers?
- Ensure that no single user or application can monopolize API resources, providing fair access opportunities for all users.
- Helps protect backend infrastructure from being overwhelmed by too many requests in a short period, which could lead to performance degradation or downtime.
- Communicates the current usage status and limits to API consumers, helping them manage request rates and avoid unexpected service interruptions.
List of Rate Limiting Headers
According to the RFC draft, the following headers should be implemented to effectively communicate rate limits:
- X-RateLimit-Limit: Indicates the maximum number of requests allowed in a given time period.
- X-RateLimit-Remaining: Displays the number of remaining requests in the current time period.
- X-RateLimit-Reset: Specifies the time when the rate limit counter resets, usually provided as a Unix timestamp.
- Retry-After: Indicates how long the client should wait before making another request after exceeding the rate limit. See ADP-139: Retry-After for more.
Guidance
- Rate limiting headers SHOULD be consistently applied across all rate-limited API endpoints.
- SHOULD clearly document the rate limiting policy in the API documentation, including limits and the meaning of each header.
- When the rate limit is exceeded, the server SHOULD respond with a 429 (Too Many Requests) status code and include the Retry-After header to indicate when the client can retry the request.
- Rate limits SHOULD be defined based on specific needs and usage patterns for different endpoints or user tiers.
- SHOULD regularly monitor usage patterns and adjust rate limits as necessary to ensure optimal performance and user experience.
Error Response Format (RFC 9457)
When the rate limit is exceeded, in addition to providing the X-Rate-Limit-*
series of headers, the server should respond according to the standardized error format defined in RFC 9457. Here's an example of how to format such a response:
Error Response Example
http
HTTP/1.1 429 Too Many Requests
Content-Type: application/problem+json
Retry-After: 3600
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1625247600
{
"type": "/problems/rate-limit-exceeded",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "You have exceeded your rate limit. Please wait until the specified time before making further requests.",
"instance": "/users/12345",
"rateLimit": {
"limit": 1000,
"remaining": 0,
"reset": 1625247600,
"retryAfter": 3600
}
}
Response Field Descriptions
- type: URI reference identifying the problem type.
- title: A short, human-readable summary of the problem type.
- status: The HTTP status code generated by the origin server for this problem.
- detail: A human-readable explanation specific to this occurrence of the problem.
- instance: URI reference identifying the specific occurrence of the problem.
- rateLimit: An object containing details about the rate limit, including:
- limit: The maximum number of allowed requests.
- remaining: The number of remaining requests in the current period.
- reset: The time when the rate limit counter resets, usually provided as a Unix timestamp.
- retryAfter: The time (in seconds) the client should wait after exceeding the rate limit before making another request.
OpenAPI Specification Example
Here's how to document rate limiting in an OpenAPI 3.1.0 specification:
yaml
openapi: 3.1.0
info:
title: Example API
version: 1.0.0
paths:
/users:
get:
summary: List users
responses:
'200':
description: Successful response
'429':
description: Too Many Requests
headers:
X-RateLimit-Limit:
schema:
type: integer
description: The number of allowed requests in the current period
X-RateLimit-Remaining:
schema:
type: integer
description: The number of remaining requests in the current period
X-RateLimit-Reset:
schema:
type: integer
description: The number of seconds left in the current period
Retry-After:
schema:
type: integer
description: The number of seconds to wait before retrying the request
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
components:
schemas:
Problem:
type: object
properties:
type:
type: string
format: uri
title:
type: string
status:
type: integer
detail:
type: string
instance:
type: string
rateLimit:
type: object
properties:
limit:
type: integer
remaining:
type: integer
reset:
type: integer
retryAfter:
type: integer
Refrain from redefining known headers
As per ADP-767, you SHOULD utilize the external headers definition YAML or reference #/components/headers
using $refs
, thereby avoiding the redefinition of known headers across all operations.