Skip to content

Return Retry-After header when rate limit is hit

What does this MR do and why?

This MR improves the API rate limiting behavior by returning a proper Retry-After header when rate limits are hit. Previously, the API::Helpers::RateLimiter#check_rate_limit! method was calling render_api_error! directly, missing the opportunity to provide clients with timing information about when they can retry their requests.

Changes made:

  • Replace render_api_error! with too_many_requests! helper method in rate limiter
  • Add Retry-After header with the configured rate limit interval
  • Make Gitlab::ApplicationRateLimiter.interval method public to support this functionality
  • Add comprehensive test coverage for the new behavior

Why this matters:

  • Clients like GitLab Runner can now know exactly how long to wait before retrying
  • Reduces unnecessary retry attempts that would immediately hit the rate limit again
  • Follows HTTP standards for rate limiting (RFC 6585)
  • Improves overall API user experience

Changelog: fixed

References

Screenshots or screen recordings

This is a backend API change with no UI impact. The improvement is visible in HTTP response headers:

Before:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json

{"message": "This endpoint has been requested too many times. Try again later."}

After:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 600

{"message": "This endpoint has been requested too many times. Try again later."}

How to set up and validate locally

  1. Set up a rate limit for testing in Rails console:

    # Enable a low rate limit for testing
    stub_application_setting(throttle_authenticated_api_enabled: true)
    stub_application_setting(throttle_authenticated_api_requests_per_period: 1)
    stub_application_setting(throttle_authenticated_api_period_in_seconds: 60)
  2. Make API requests to trigger the rate limit:

    # First request should succeed
    curl -H "Authorization: Bearer YOUR_TOKEN" "http://localhost:3000/api/v4/projects"
    
    # Second request should return 429 with Retry-After header
    curl -I -H "Authorization: Bearer YOUR_TOKEN" "http://localhost:3000/api/v4/projects"
  3. Verify the Retry-After header is present in the 429 response

  4. Run the new integration tests:

    bundle exec rspec spec/lib/api/helpers/rate_limiter_integration_spec.rb
    bundle exec rspec spec/lib/api/helpers/rate_limiter_spec.rb

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

  • Functionality: Improves API rate limiting by providing proper Retry-After headers
  • Performance: No performance impact - only changes response headers
  • Reliability: Comprehensive test coverage added for new functionality
  • Security: No security implications - maintains existing rate limiting behavior
  • Maintainability: Code is cleaner by reusing existing too_many_requests! helper
  • Documentation: No documentation changes needed - this is an HTTP standard header
  • Testing: Added integration tests and updated unit tests
Edited by Pedro Pombeiro

Merge request reports

Loading