Claude API rate limit headers, explained
Every anthropic-ratelimit header the Claude API returns, what retry-after means on a 429, and what the unified 5h and 7d headers Claude Code reads report.
What the headers tell you
The Claude API returns two different families of rate limit headers, and which family you see depends on how the request was authenticated. A request made with an API key gets the anthropic-ratelimit-requests-*, anthropic-ratelimit-tokens-*, anthropic-ratelimit-input-tokens-* and anthropic-ratelimit-output-tokens-* headers. They describe your organization's per-minute limits for the model you called, and a retry-after header on a 429 tells you how many seconds to wait. A request Claude Code makes while logged in with a claude.ai subscription gets the anthropic-ratelimit-unified-* headers instead. Those describe the subscription's rolling 5-hour and 7-day windows as a fraction used plus an epoch reset time, and they have nothing to do with API usage tiers.
Every header name in the API sections below is copied from the Anthropic rate limits page as it read on 2026-09-20. The subscription section is built from the Claude Code documentation and from raw headers Claude Code users have posted in public, and it says so wherever a header is not documented.
Which Claude API rate limit headers exist
RPM is requests per minute. ITPM is input tokens per minute. OTPM is output tokens per minute. The Messages API measures each one separately for each model, so calling Sonnet and Opus in the same minute draws from two separate pools. Each limit reports itself with a triplet of headers: the size of the limit, how much of it is left, and when it will be full again.
anthropic-ratelimit-requests-limit: the maximum number of requests allowed within any rate limit period.anthropic-ratelimit-requests-remaining: the number of requests remaining before being rate limited.anthropic-ratelimit-requests-reset: the time when the request rate limit will be fully replenished, in RFC 3339 format.anthropic-ratelimit-input-tokens-limit,anthropic-ratelimit-input-tokens-remainingandanthropic-ratelimit-input-tokens-reset: the same triplet for input tokens. The remaining count is rounded to the nearest thousand.anthropic-ratelimit-output-tokens-limit,anthropic-ratelimit-output-tokens-remainingandanthropic-ratelimit-output-tokens-reset: the same triplet for output tokens, again rounded to the nearest thousand.anthropic-ratelimit-tokens-limit,anthropic-ratelimit-tokens-remainingandanthropic-ratelimit-tokens-reset: a combined token figure. The documentation says these show the most restrictive limit currently in effect. If a workspace token limit is the one you exceeded, they carry the workspace values. If no workspace limit applies, they carry the total, which is input plus output.retry-after: sent on a 429. The number of seconds to wait until you can retry the request. Earlier retries will fail.
RFC 3339 is the timestamp format that looks like 2026-09-20T09:14:03Z. The Z means UTC (coordinated universal time), so compare it against date -u, not against your local clock.
Two more families exist, and you will only see them in specific situations. Organizations on Priority Tier get anthropic-priority-input-tokens-limit, anthropic-priority-input-tokens-remaining and anthropic-priority-input-tokens-reset, plus the same three for anthropic-priority-output-tokens. Requests that set speed: "fast" on Opus 5 or Opus 4.8 get anthropic-fast-* headers for the separate fast mode pool. Neither appears on an ordinary request.
Two headers that are not rate limit headers matter when you debug one. request-id is the value to quote to support. anthropic-workspace-id names the workspace the key resolved to, which matters because a workspace can be given a lower limit than the organization.
Read them with curl -D -
-D - writes the response headers to standard output. -o sends the body somewhere else so the two do not mix. Set ANTHROPIC_API_KEY in your shell first. If you are not sure which request header carries the key, how Claude API keys and bearer tokens authenticate a request covers both x-api-key and Authorization: Bearer.
curl -sS -D - -o /tmp/body.json https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":16,"messages":[{"role":"user","content":"Reply with one word."}]}'The output has the shape below. The figures are the documented Start tier limits for Claude Sonnet 5, and the request is the first one this minute, so almost nothing has been drawn down. Your numbers will differ.
HTTP/2 200
content-type: application/json
request-id: req_011CTxxxxxxxxxxxxxxxxxxx
anthropic-organization-id: 5f1c0d2e-xxxx-xxxx-xxxx-xxxxxxxxxxxx
anthropic-workspace-id: wrkspc_01xxxxxxxxxxxxxxxxxxxxxx
anthropic-ratelimit-requests-limit: 1000
anthropic-ratelimit-requests-remaining: 999
anthropic-ratelimit-requests-reset: 2026-09-20T09:14:03Z
anthropic-ratelimit-input-tokens-limit: 2000000
anthropic-ratelimit-input-tokens-remaining: 2000000
anthropic-ratelimit-input-tokens-reset: 2026-09-20T09:14:03Z
anthropic-ratelimit-output-tokens-limit: 400000
anthropic-ratelimit-output-tokens-remaining: 400000
anthropic-ratelimit-output-tokens-reset: 2026-09-20T09:14:03Z
anthropic-ratelimit-tokens-limit: 2400000
anthropic-ratelimit-tokens-remaining: 2400000
anthropic-ratelimit-tokens-reset: 2026-09-20T09:14:03ZThree things to notice. The values are for the model in the request, because limits are per model. anthropic-ratelimit-tokens-limit here is the input and output limits added together, which is what the documentation describes when no workspace limit applies. And remaining on the token headers moves in steps of a thousand, so a sixteen-token test request does not visibly change it.
To feed one header to a script, save the headers to a file and filter them instead of reading by eye:
curl -sS -D /tmp/headers.txt -o /tmp/body.json https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" -H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":16,"messages":[{"role":"user","content":"Reply with one word."}]}'
grep -i '^anthropic-ratelimit-' /tmp/headers.txt | tr -d '\r'HTTP header names are case-insensitive, so match with grep -i. curl writes the line endings the server sent, which end in a carriage return, and tr -d '\r' removes it so a value does not carry an invisible character into your next comparison.
What a 429 looks like
When any per-minute limit is exceeded, the API returns HTTP 429 with a JSON body whose error.type is rate_limit_error, a message that names which limit was exceeded, and a retry-after header. That is the ordinary case, and the fix is to wait retry-after seconds.
Two other responses look almost the same and need different handling.
The first is the monthly spend cap. Each usage tier carries a maximum monthly spend, and when your organization reaches it, every request returns 429 until 00:00 UTC on the first day of the next month. The documented body is:
{
"type": "error",
"error": {
"type": "rate_limit_error",
"message": "You have reached your API usage limits: your organization has crossed its monthly API usage threshold, set based on your organization's API tier. You will regain access on 2026-09-01 at 00:00 UTC.",
"details": { "error_code": "enforced_spend_limit_reached" }
},
"request_id": "req_018EeWyXxfu5pfWkrYcMdjWG"
}error.type is still rate_limit_error, but there is no retry-after header, and error.details.error_code is enforced_spend_limit_reached. A retry loop that only reads retry-after will either fail on the missing header or keep retrying until next month. Check error_code first. The official SDKs retry automatically and fail the same way here.
The second is an acceleration limit. The documentation says a sharp increase in usage can trigger a 429 on its own, and the advice is to ramp traffic up gradually rather than to wait for a header to change.
One more response is often mistaken for a 429. A spend limit you set yourself on the Billing page returns HTTP 400 with invalid_request_error, and the message begins You have reached your specified API usage limits. Raise or remove that limit and it clears at once.
Back off from retry-after, not from reset
The API uses a token bucket. Your capacity refills continuously up to the limit rather than resetting on a fixed clock. That changes what reset means: it is the moment the bucket will be completely full, not the earliest moment your next request will succeed. If you have used half of your requests, you can keep sending now. If you received a 429, retry-after is the shortest correct wait, and it is usually far shorter than the time to reset.
A minimal shell loop that does the right thing:
#!/usr/bin/env bash
hdr=$(mktemp); body=$(mktemp)
header() { grep -i "^$1:" "$hdr" | tail -n1 | cut -d' ' -f2- | tr -d '\r'; }
for attempt in 1 2 3 4 5; do
code=$(curl -sS -o "$body" -D "$hdr" -w '%{http_code}' https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" -H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-sonnet-5","max_tokens":16,"messages":[{"role":"user","content":"Reply with one word."}]}')
[ "$code" != "429" ] && break
wait=$(header retry-after)
if [ -z "$wait" ]; then
echo "429 without retry-after, error_code=$(jq -r '.error.details.error_code // \"none\"' "$body")"
exit 1
fi
echo "attempt $attempt: rate limited, waiting ${wait}s"
sleep "$wait"
done
echo "status $code, requests remaining $(header anthropic-ratelimit-requests-remaining)"The header function takes the last match in case a proxy added a duplicate, drops the carriage return, and returns only the value. -w '%{http_code}' prints the status code alone on standard output, so the script can branch on it without parsing the HTTP/2 429 line.
If you want the seconds until a bucket is full, GNU date parses RFC 3339 directly:
reset=$(header anthropic-ratelimit-input-tokens-reset)
echo $(( $(date -d "$reset" +%s) - $(date +%s) ))A negative number means the bucket was already full by the time you did the arithmetic. It has been refilling since the response left the server.
What a usage tier changes
Your organization is placed on a usage tier automatically from its usage history and account standing, and the tier sets the numbers in the -limit headers. New organizations may start in an Evaluation tier with limits below the standard ones. The standard per-minute figures for Claude Sonnet 5, Opus 5 and Haiku 4.5 are identical across the three self-serve tiers, so one set of charts covers them. These are published figures from the rate limits page on 2026-09-20, not measurements.
The data behind this chart
[
{
"label": "Start",
"itpm": "2,000,000",
"otpm": "400,000"
},
{
"label": "Build",
"itpm": "5,000,000",
"otpm": "1,000,000"
},
{
"label": "Scale",
"itpm": "10,000,000",
"otpm": "2,000,000"
}
]The data behind this chart
[
{
"label": "Start",
"rpm": "1,000"
},
{
"label": "Build",
"rpm": "5,000"
},
{
"label": "Scale",
"rpm": "10,000"
}
]On the Start tier, anthropic-ratelimit-input-tokens-limit reads 2,000,000, anthropic-ratelimit-output-tokens-limit reads 400,000 and anthropic-ratelimit-requests-limit reads 1,000. On the Scale tier the same three headers read 10,000,000, 2,000,000 and 10,000. The monthly spend caps that produce the no-retry-after 429 above are, as of September 2026, $500 on Start, $1,000 on Build and $200,000 on Scale. Custom tier organizations have no cap and arrange limits with their account team. The Fable 5 models and the older Opus 4.x and Sonnet 4.x families have their own rows and, in some cases, shared buckets across versions, so read the page for those.
Three details about the token limits change how you read the headers.
Cached input does not count. For every model on the current tables except the retired Claude Haiku 3.5, only input_tokens and cache_creation_input_tokens draw from ITPM. cache_read_input_tokens do not. A request that reads a 100,000-token cached prefix and adds 50 new tokens draws about 50 tokens from the bucket, not 100,050, which after rounding may not move anthropic-ratelimit-input-tokens-remaining at all. Prompt caching raises your effective throughput without changing a single -limit header.
max_tokens does not count. OTPM is drawn down as output tokens are actually generated, so a generous max_tokens costs nothing until the model uses it.
The pools are per model and can be narrowed per workspace. Exhausting Opus leaves the Sonnet headers untouched, and the numbers in a response are always for the model named in that request. A workspace can be given a lower limit than the organization, in which case the anthropic-ratelimit-tokens-* headers report the workspace figure when that is the binding one. To read the configured limits without spending a request, the Rate Limits API returns them, and the Console's Rate limits page shows the same numbers with the tier you are on.
The anthropic-ratelimit-unified headers on a subscription login
Log Claude Code in with a claude.ai Pro or Max subscription instead of an API key, and the response headers change family. This is the part a search for anthropic-ratelimit-unified-5h-utilization is really about, and it needs a clear line between what is documented and what is observed. Logging Claude Code in with a subscription or an API key is the choice that decides which family you get.
What Anthropic documents, as of 2026-09-20, is this. The Claude Code status line receives a rate_limits object with a five_hour window and a seven_day window. Each window has used_percentage, from 0 to 100, and resets_at, in Unix epoch seconds. The object appears only for claude.ai Pro and Max subscribers, or behind a Claude apps gateway with a spend limit, and only after the first API response of the session. The Claude apps gateway page names the header family: each successful /v1/messages response for a developer with a cap carries their own cap utilization and reset time in the anthropic-ratelimit-unified-* headers, and Claude Code reads them to warn at 75% and 95% of that cap. Neither page lists the individual header names.
The names below come from raw response headers that Claude Code users posted in public bug reports on the Claude Code GitHub repository between December 2025 and July 2026. They are what Claude Code's own requests receive.
anthropic-ratelimit-unified-5h-utilization: a fraction from 0 to 1, for example0.018416969696969696. Multiplied by 100 it matches the scale of the documentedfive_hour.used_percentage.anthropic-ratelimit-unified-5h-reset: Unix epoch seconds, for example1764554400. This is the same unit the documentedresets_atuses.anthropic-ratelimit-unified-5h-status: the valueallowedin every dump. Other values are not documented.anthropic-ratelimit-unified-7d-utilization,anthropic-ratelimit-unified-7d-resetandanthropic-ratelimit-unified-7d-status: the same three values for the 7-day window.anthropic-ratelimit-unified-statusandanthropic-ratelimit-unified-reset: in the dumps they repeat the 5-hour status and reset. Which window they summarize, and when, is not documented.anthropic-ratelimit-unified-representative-claim: the valuefive_hourin the dumps. The name and value suggest it points at one of the two windows. Anthropic has not documented it, so do not build logic on it.anthropic-ratelimit-unified-fallback-percentageandanthropic-ratelimit-unified-overage-disabled-reason: seen with the values0.2andorg_level_disabled. Undocumented, and their meaning is not something this guide will guess at.
Three differences from the API family stand out, and they are why a parser written for one family drops the other. The unified headers report a fraction spent, not a count remaining, and no -limit or -remaining variant appears in any dump. The reset is epoch seconds, not RFC 3339. And the third value per window is a -status string rather than a -limit number. A regular expression that expects a -limit, -remaining or -reset suffix keeps 5h-reset and silently discards 5h-utilization and 5h-status, which is a bug more than one third-party client has had to fix.
What these windows are is the key point. They describe the subscription's rolling 5-hour window and its weekly window, the same limits that produce You've hit your session limit and You've hit your weekly limit inside Claude Code. They are not API usage tiers. A subscription has no RPM, ITPM or OTPM, and an API key request never receives a unified header. For how large the windows are, how the weekly window is shared across models and what happens at 100%, read how the subscription's 5-hour and 7-day windows work. If you are deciding whether to keep the subscription or move the same work to metered API billing, what the API costs compared with a subscription does that arithmetic.
Reading the unified values without scraping headers
You do not need to capture Claude Code's traffic. Run /usage inside Claude Code and the plan usage bars show the same two windows with a percentage and a reset time. For a script, the status line is the supported channel. Claude Code pipes a JSON document to your status line command on every update, and the rate_limits object is inside it. The documented way to handle its absence is jq's // empty:
#!/bin/bash
input=$(cat)
FIVE_H=$(echo "$input" | jq -r '.rate_limits.five_hour.used_percentage // empty')
WEEK=$(echo "$input" | jq -r '.rate_limits.seven_day.used_percentage // empty')
RESET=$(echo "$input" | jq -r '.rate_limits.five_hour.resets_at // empty')
if [ -n "$FIVE_H" ]; then
printf '5h: %.0f%% 7d: %.0f%% 5h resets %s UTC\n' "$FIVE_H" "${WEEK:-0}" "$(date -u -d @"$RESET" +%H:%M)"
else
echo "no plan limits yet"
fiSave it as ~/.claude/statusline.sh, make it executable, and point Claude Code at it in ~/.claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "~/.claude/statusline.sh"
}
}resets_at is epoch seconds, so date -u -d @"$RESET" turns it into a clock time. The two windows can be absent independently, and Claude Code drops a window once its resets_at has passed, so test every field before printing it.
Test the script without a live session by feeding it a sample document:
echo '{"model":{"display_name":"Sonnet"},"rate_limits":{"five_hour":{"used_percentage":23.5,"resets_at":1738425600},"seven_day":{"used_percentage":41.2,"resets_at":1738857600}}}' | bash ~/.claude/statusline.shYou should see 5h: 24% 7d: 41% 5h resets 16:00 UTC. Feed it {"model":{"display_name":"Sonnet"}} and you should see no plan limits yet, which is what an API key session prints, because that session never receives a rate_limits object.
What used_percentage cannot tell you is where the tokens went. A long session re-sends its whole context on every turn, so a one-line question late in the day can move the 5-hour bar more than an hour of work did in the morning. Why Claude Code's token usage climbs during a long session explains the mechanism and the habits that slow it down.
One caution about the gateway case. Behind a Claude apps gateway with spend limits, the same anthropic-ratelimit-unified-* names carry the developer's spend cap rather than the subscription windows, and the gateway strips the upstream provider's rate limit headers instead of forwarding them. On that setup the status line shows rate_limits.spend_limit, and its used_percentage can go above 100.
FAQ
Why does my 429 have no retry-after header?
Because it is not a per-minute rate limit. The monthly spend cap returns 429 with error.type set to rate_limit_error, no retry-after, and error.details.error_code set to enforced_spend_limit_reached. Nothing clears it until 00:00 UTC on the first day of next month or a move to a higher tier, so a retry loop must read error_code and stop rather than sleep and try again.
Should I wait until anthropic-ratelimit-requests-reset before retrying?
No. The API uses a token bucket that refills continuously, and reset is when the bucket will be full, not when your next request can succeed. After a 429, sleep for the retry-after seconds and retry. Before a 429, keep sending while remaining is above zero.
Does anthropic-ratelimit-input-tokens-remaining count cached tokens?
For every model on the current tables except the retired Claude Haiku 3.5, no. Only input_tokens and cache_creation_input_tokens draw from the ITPM bucket, and cache_read_input_tokens do not. The value is also rounded to the nearest thousand, so a small request can leave it unchanged.
Is anthropic-ratelimit-unified-5h-utilization documented?
Partly. As of 2026-09-20 Anthropic names the header family on the Claude apps gateway page and documents the values Claude Code derives from it, rate_limits.five_hour.used_percentage and resets_at, on the status line page. It does not publish a per-header table. The individual names and their formats come from headers Claude Code users have posted publicly, and the ones beyond utilization, reset and status have no documented meaning.
Do the unified headers appear on an API key request?
No. An API key request receives the anthropic-ratelimit-requests-*, anthropic-ratelimit-tokens-*, anthropic-ratelimit-input-tokens-* and anthropic-ratelimit-output-tokens-* headers, which describe per-minute, per-model limits set by your usage tier. The anthropic-ratelimit-unified-* headers describe a claude.ai subscription's rolling 5-hour and 7-day windows, or a Claude apps gateway spend cap, and arrive only on those logins.