Skip to content
ADP
API Design PrincipleBETA

[ADP-138] Rate Limit Headers

reviewing phase 1

Response to rate limiting should be unified.

Why Use Rate Limiting Headers?

  1. Ensure that no single user or application can monopolize API resources, providing fair access opportunities for all users.
  2. Helps protect backend infrastructure from being overwhelmed by too many requests in a short period, which could lead to performance degradation or downtime.
  3. 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:

  1. X-RateLimit-Limit: Indicates the maximum number of requests allowed in a given time period.
  2. X-RateLimit-Remaining: Displays the number of remaining requests in the current time period.
  3. X-RateLimit-Reset: Specifies the time when the rate limit counter resets, usually provided as a Unix timestamp.
  4. 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.

References