WORK IN PROGRESS.
WILL TAKE 3 MONTHS TO COMPLETE
TARGET COMPLETION DATE - Dec 2026
I've been trying to write this handbook for a year now. Today (2026-09-18) I finally decided to start publishing it. Based on my rough notes so far, this is going to be a rather large article.
This note exists for:
- Teaching newcomers or my junior teammates the components things that go into an API's design
- Helping me remember stuff
- Being a standards guide for feeding LLMs (before they starts auditing existing APIs or creating new ones).
This note is for you if you're - writing a web framework, building an API gateway, developing an API, consuming an API.
Over the years, I've learnt to judge the quality of an org's engineering through:
- Clarity of API endpoints
- Accuracy of HTTP response codes
- Details in Response Headers
This note is supposed to help in these 3 areas.
Rate limiting
When rate limit is exceeded, return a HTTP 429 - Too many requests.
And to inform consumers about the rate limit status, set 3 headers:
RateLimit-PolicyRateLimitRetry-After
You cannot impose rate limits without informing consumers about what quotas have been imposed, and how much of it has been used.
Headers to set
RateLimit-Policy
Tells the rate limit policies that are being applied.
EXAMPLE
Suppose there are 2 policies:
- daily policy allows 10,000 requests per day
- burst policy allows 100 requests per minute
The value of RateLimit-Policy will be:
RateLimit-Policy: "burst";q=100;t=60, "daily";q=10000;t=86400;pk=oBJER4zNn2XZERtPXTBh3YzRNp707L6u
Identifiers and their meanings:
- q - quota of the policy
- qu - unit that the quota is measured in (values could be -
requests,content-bytes,concurrent-requests) - w - time window of the quota
- pk - (optional) - partition key used to scope usage (could be API key, account, ip address etc)
RateLimit
This is my favourite header!
Tells the current quota remaining in each policy.
Suppose the default rate limit policy that allows 100 requests in a 60 second window. It will have a header value:
RateLimit: "default";r=100;t=30
Identifiers and their meanings:
- r - API hits remaining in policy (+ve integer).
0means all the API hits of the quota have been used up. - t - time window window length in seconds, in which the quota can be used
- pk - (optional) - partition key used to scope usage (could be API key, account, ip address etc)
RateLimit can return the limits of multiple policies at once. Separate each policy by comma, and order the policies by time-window length in ascending order.
EXAMPLE
Consider 2 policies
- daily policy allows 10,000 requests per day
- burst policy allows 100 requests per minute
- The usage is measured against an API key
Suppose 10 requests have been used up in a minute. The returned header will have value:
RateLimit: "burst";r=90;t=60, "daily";r=9990;t=86400;pk=oBJER4zNn2XZERtPXTBh3YzRNp707L6u
These 2 headers are enough, and I recommend using these 2 only.
Retry-After
Tells how long the client needs to wait before retrying the API request. Its value is in seconds.
There are 2 ways of reporting this value:
(1) Delta-seconds
Retry-After: 60
(2) HTTP-date
Gives a date in format Day, DD Mon YYYY HH:MM:SS GMT.
Retry-After: Sat, 18 Sep 2026 01:00:00 GMT
Note on headers
To report usage, you'll often find existing using 3 headers RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset. That too is fine, but the 2 header combo defined above is simply superior. (RateLimit-Limit & RateLimit-Remaining got combined into a single header - RateLimit)
Rate limiting algorithms to use - WORK IN PROGRESS
This is a very well known problem area, and infra components like redis, language-specific libraries etc have the building blocks required to implement each of them.
A list of algos for controlling API requests within a time window are:
- Token bucket - good algo to default to
- Fixed window counter - very stupid algo, inexpensive to implement at scale, will lead to 'bursty' usage
- Sliding window log - very expensive (cache wise) to implement
- Sliding window counter - inexpensive (I personally like this one the most)
- Leaky bucket - leads to very smooth processing, perfect for pull based architectures
There's also a concurrency limiting algo.
Conveying scheduled downtime
Suppose your API is currently down for planned maintenance. In this case:
- Return HTTP 503 - Service Unavailable
- Set response header
Retry-After
Retry-After header tells how long the client needs to wait before retrying the API request. Its value is in seconds.
Can return value in 2 formats:
(1) HTTP-date (recommended, easier to use) (uses format Day, DD Mon YYYY HH:MM:SS GMT)
Retry-After: Sat, 18 Sep 2026 01:00:00 GMT
(2) Delta-seconds - tells time to wait in seconds
Retry-After: 3600
This is extremely useful for scrapers, because this clearly tells them when to resume operation, thus removing the need for polling your API.
Resources:
- http.dev/headers - EVERYTHING that I know about headers has come from here
Thoughts & opinions
- Brain itch - python - how much faster are list comprehensions than for-loops 2026 Sep 18 | ~1 pages
- My subconscious doesn't like LLMs 2025 Jul 16 | ~7 pages
- Computers understanding humans makes codebases irrelevant 2023 Apr 08 | ~5 pages
- Own your email's domain 2023 Feb 12 | ~4 pages
- Isolates + storage over http + orchestrators is the future that has arrived 2023 Jan 03 | ~4 pages
Articles
I learn through writing, so I write a lot. Most of these are ever evolving pieces.
API
- HTTP API design handbook 2026 Oct 18 | ~3 pages
Data Engineering
- The Spark Field Manual 2025 Oct 16 | ~23 pages
Python
- Unicode string normalization schemes in Python 2024 May 06 | ~5 pages
Resources
- Bookmarks 2025 Jun 02 | ~4 pages
- PVLDB - links only 2026 Sep 10 | ~162 pages
- PVLDB - links with abstracts (large document) 2026 Sep 10 | ~3377 pages
Work
- Lecture - You and your research by Dr. Richard Hamming 2024 Oct 14 | ~49 pages