Skip to content

Rate Limiting & Validation

Two defenses round out API security: limiting how often a caller may hit you, and never trusting what they send.

Rate limiting caps requests per client per window, protecting against abuse, runaway clients, and accidental loops. When a caller exceeds the limit, respond 429 Too Many Requests with a Retry-After header telling them when to try again. It is good practice to advertise the budget with RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers.

A common algorithm is the token bucket: each client has a bucket that refills at a steady rate; each request spends a token; an empty bucket means 429.

JavaScript

Every value from a client is untrusted until validated. The main risks:

  • Injection — never build SQL/commands by string concatenation; use parameterized queries.
  • Mass assignment — do not blindly spread a request body onto a model; accept only an allowlist of fields, or a user could set isAdmin: true.
  • Oversized payloads — cap request body size and array lengths to avoid resource exhaustion.
  • Type and range — validate types, lengths, and ranges with a schema, and reject with 422 (see Validation Errors).
Advantages (Rate Limiting)Trade-offs
Protects against abuse and DDoSLegitimate traffic spikes can get rejected too
Protects against credential stuffing and brute forceSetting the right limit is hard — too strict or too loose
Cuts the cost of downstream services like an LLM APISharing a limit across a distributed system needs something like Redis
Tells the client via headers how much quota is leftA poor error message hurts the user experience

Rate limiting without telling the client Symptom:

  • 429 Too Many Requests with no header saying how much quota is left or when it resets
  • The client doesn’t know how long to wait — a retry loop makes it worse
  • Always send Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (or the IETF RateLimit-* equivalents)

Input validation without sanitizing Symptom:

  • Accepting name: "<script>alert(1)</script>" and storing/rendering it unsanitized
  • XSS when the API response is rendered in a web app
  • Validate type/format and sanitize string input before storing it

💡 Real-world examples

GitHub API:

  • Rate limit: 5,000 requests/hour for authenticated requests
  • Headers: X-RateLimit-Limit: 5000, X-RateLimit-Remaining: 4999, X-RateLimit-Reset: 1609459200

Stripe API:

  • Rate limits vary by endpoint — /charges has a different limit than /events
  • 429 comes with a Retry-After header telling the client how many seconds to wait

Sources: GitHub REST API rate limits, Stripe rate limits

Which status and header signal that a client is rate limited?
What is mass assignment, and how do you prevent it?
How should you prevent SQL injection?