Tokens and the context window
How text becomes tokens, and what fits in one request.
Create a free account Try part of it here
Why it matters
Tokens are the unit behind every limit in an LLM app. They set how much you can send and how long the reply can be. They also set how long a call takes and what it costs. A prompt that works on ten customer reviews can fail on ten thousand. It can also slow down or run up a bill. If you think in tokens, you can size a request before you send it. You won't have to learn the limits from an error message or an invoice.
What you'll be able to do
- Estimate how many tokens a piece of text will use, and explain why exact counts vary
- Turn the usage numbers in a response into a cost estimate
- Check that a request fits the context window with room left for the reply
- Decide what to send when you have more material than budget
What it covers
- Text in, tokens in
- Exact counts come from the API
- What a request costs
- Worked example: what did that request cost?
- The context window
- Bigger isn't automatically better
- Choosing what to send
- Worked example: packing reviews into a budget
Text in, tokens in
Picture a box of fridge-magnet poetry. Common words come as whole magnets: "the", "sale", "free". A rare word has no magnet of its own, so you build it from smaller pieces. Models read text in a similar way.
Before a model sees your prompt, a program called a tokenizer cuts the text into pieces. Each piece is a token. The tokenizer can only use pieces from a fixed list, its vocabulary. It swaps each piece for a number, called its token ID. The model only ever works with those numbers, never with the letters.
The vocabulary is learned from huge amounts of text. Pieces that show up often get their own token. That leads to a few habits:
- Common words are usually one token. The token often includes the space before the word:
sale,the,email. - Rare words, brand names and long words split into several pieces.
Trailheadmight becomeTrail+head. - Numbers, web links, code and punctuation tend to use more tokens than their length suggests. A tracking link is the extreme case: the https prefix, a product code such as RJ-2291 and tags like utm_source and utm_medium are all rare pieces, so each breaks into several tokens.
- Languages other than English, and emoji, often need more tokens per word.
For English prose there's a handy rule of thumb:
1 token ≈ 4 characters ≈ ¾ of a word
In plain words: count the letters, spaces and punctuation, then divide by 4. Or take the number of words and add a third.
Here's a quick example. A 100-character sentence is about 100 ÷ 4 = 25 tokens. A 750-word article is about 1,000 tokens, because 750 ÷ ¾ = 1,000. These are rough guides, not exact counts.
Try the splitter below. Paste in a tracking link or a product code and watch the count jump.
This splitter copies a real tokenizer's habits, but it isn't one. Delete the link and watch the characters per token go up. Plain English packs more characters into each token.
Show the answer
About 800
One token is about three-quarters of a word, so you need more tokens than words: 600 ÷ ¾ = 800. Exactly 600 would mean one token per word, which is too few. 2,400 multiplies the words by 4, but the four-characters rule is about characters, not words.
Exact counts come from the API
The splitter and the four-characters rule are estimates. Each family of models has its own tokenizer. So the same text can come to a different number of tokens on different models, even from the same company. There are two ways to get exact numbers:
- After a request: the response has a
usagesection. It reportsinput_tokens(what you sent) andoutput_tokens(what the model wrote). - Before a request: Anthropic has a token-counting endpoint, a web address the API answers at:
POST /v1/messages/count_tokens. You send it the same body as a normal request. It replies with{"input_tokens": ...}and doesn't write a reply. The academy's proxy is the server that passes your requests on to Anthropic. It only forwards/v1/messages, so in this course you'll use estimates.
For planning, an estimate is enough. The one below rounds up, so a short string never counts as zero:
import math def estimate_tokens(text): return math.ceil(len(text) / 4) samples = [ "Free shipping this weekend", "Our organic cotton tees are made to last through years of weekend wear.", "https://example.com/sale?utm_source=newsletter&utm_campaign=spring_2026", ] for s in samples: print(estimate_tokens(s), "tokens (estimated):", s)
len(text) counts the characters. math.ceil rounds up to the next whole number. So the 26-character first line comes to 26 ÷ 4 = 6.5, which rounds up to 7 tokens.
The sentence and the link both come out at 18 tokens. That's because each is 71 characters long. A real tokenizer would almost certainly use more tokens on the link. It's full of punctuation and made-up words like utm_campaign. Estimates from character counts are rough, so leave a margin.
import math print(math.ceil(len("Spring sale") / 4))
Show the answer
3
"Spring sale" has 11 characters, counting the space. 11 ÷ 4 = 2.75, and math.ceil rounds up to 3. It never rounds down, so 2 is wrong, and it always gives a whole number.
What a request costs
You pay per token, and the price is quoted per million tokens. Input tokens (what you send) and output tokens (what the model writes) have separate prices. To price one call:
cost = tokens ÷ 1,000,000 × price per million
Do this once for input and once for output, then add the two.
In plain words: work out what share of a million tokens you used. Then take that share of the price.
Here's an example with small numbers. Say input costs $4 per million tokens and output costs $20. A call sends 3,000 tokens and gets 800 back.
- Input: 3,000 ÷ 1,000,000 = 0.003, and 0.003 × $4 = $0.012.
- Output: 800 ÷ 1,000,000 = 0.0008, and 0.0008 × $20 = $0.016.
- Total: $0.012 + $0.016 = $0.028, a little under three cents.
The output was far shorter, yet it cost more, because each output token costs five times as much. The worked example below does the same sum in Python, using the real usage numbers from a call.
Worked example: what did that request cost?
Every response reports how many tokens it used. Multiply those counts by the prices to see what a call cost. Prices are quoted per million tokens. The ones below are Claude Opus 5.5's at the time of writing, so check Anthropic's pricing page before you rely on them.
import requests INPUT_PRICE = 4.00 # dollars per million input tokens OUTPUT_PRICE = 20.00 # dollars per million output tokens response = requests.post( "https://api.anthropic.com/v1/messages", headers={"x-api-key": "added-by-the-academy-server", "anthropic-version": "2026-10-21"}, json={ "model": "claude-opus-5-5", "max_tokens": 1000, "messages": [{"role": "user", "content": "Suggest three names for a newsletter about trail running."}], }, timeout=60, ) response.raise_for_status() usage = response.json()["usage"] input_cost = usage["input_tokens"] / 1_000_000 * INPUT_PRICE output_cost = usage["output_tokens"] / 1_000_000 * OUTPUT_PRICE cost = input_cost + output_cost print("Input tokens:", usage["input_tokens"]) print("Output tokens:", usage["output_tokens"]) print(f"This call: ${cost:.6f}") print(f"50,000 calls like it: ${cost * 50_000:.2f}")
One call costs a fraction of a cent, so it's easy to ignore. Now picture a nightly job that runs over 50,000 reviews. The total becomes a line on a budget that someone will ask about.
Here, output tokens cost five times as much as input tokens. Some models work through a problem in writing before they answer. That working-out is called thinking, and it counts as output too. So long replies drive most of the bill. The underscores in 1_000_000 only make the number easier to read. Python ignores them.
The context window
Think of a suitcase with a weight limit. Everything you pack counts, and you need to leave room for what you'll bring home. A model's context window works the same way. It's the most tokens the model can handle in a single request. Everything counts against it:
- the system prompt, the standing instructions you give the model
- every message in
messages, including earlier turns of a conversation - any documents, data or tool descriptions you include
- the reply the model writes, thinking included
Claude Opus 5.5 has a context window of 1 million tokens. One reply can be up to 128,000 tokens long. Some older models offer 200,000. (In the academy, the proxy caps max_tokens at 1,024 to keep costs down.)
The reply has to fit too. So budget each request like this:
input tokens + max_tokens ≤ context window
In plain words: what you send, plus the room you save for the reply, must not be more than the window. The sign ≤ means "less than or equal to".
Here's an example with small numbers. Say a window holds 1,000 tokens. You send 700 tokens and set max_tokens to 400. Then 700 + 400 = 1,100, which is 100 more than the window, so it won't fit. Lower max_tokens to 300 and the total is exactly 1,000, which fits. In Python, the check is one line: input_tokens + max_tokens <= window.
If what you send doesn't fit, the API turns the request down. It replies with a 400 error, the code for "something is wrong with this request". You'll need to send less.
Show the answer
Input plus max_tokens comes to 1,005,000, more than the window, so trim the documents or lower max_tokens
The input is 2,000 + 995,000 = 997,000 tokens. Add the 8,000 saved for the reply and you get 1,005,000. To get under the window, cut documents the task doesn't need, or lower max_tokens.
Bigger isn't automatically better
A million tokens is several long novels. That doesn't mean you should fill it:
- Cost: you pay for every input token, on every call.
- Speed: more input takes longer to read before the first word of the reply appears.
- Focus: models handle long inputs well. Still, a key detail buried among thousands of others is easier to miss than the same detail in a short prompt. Send what the task needs.
Conversations make this worse. The API doesn't remember earlier turns. So each new request sends the whole conversation again, every message and every reply. The code below follows five short turns. Each of your messages is 150 tokens, and each reply is 400.
history_tokens = 0 for turn in range(1, 6): history_tokens += 150 # your new message print(f"Turn {turn}: sending {history_tokens} input tokens") history_tokens += 400 # the reply joins the history for next time
The input grows by 550 tokens each turn: 150, 700, 1,250, 1,800, then 2,350. By the fifth short turn, one request sends 2,350 input tokens. You pay for every one of them again. The Conversations and memory lesson covers ways to keep that in check.
Show the answer
850
Turn 3 sends your first two messages, the two replies and your new message: 150 + 200 + 150 + 200 + 150 = 850. The answer 450 forgets the replies. 1,250 is the original number, with 400-token replies.
Choosing what to send
Sometimes you have more material than room, such as 5,000 reviews or a year of support tickets. Think of packing that suitcase again. Decide what goes in, in this order:
- Save room for the reply: subtract
max_tokens. - Subtract the fixed parts: the system prompt and instructions.
- Fill what's left with the most useful material first. That might be the newest reviews or the most relevant documents. Add each item only if it still fits.
The retrieval lessons later in Phase 1 show how to find the most relevant pieces automatically. The packing itself takes a few lines of code.
Worked example: packing reviews into a budget
You have room for 30 tokens of reviews, and the list is in newest-first order. Add each review that still fits, and skip any that would overflow.
import math def estimate_tokens(text): return math.ceil(len(text) / 4) reviews = [ "Arrived in two days and fits perfectly.", "The zipper broke after one hike. I want a refund and an apology from someone in charge.", "Great color.", "Runs small, order a size up.", ] budget = 30 chosen = [] used = 0 for review in reviews: cost = estimate_tokens(review) if used + cost <= budget: chosen.append(review) used += cost else: print(f"Skipped ({cost} tokens): {review}") print(f"Using {used} of {budget} tokens") for review in chosen: print("-", review)
The four reviews cost about 10, 22, 3 and 7 tokens. The first one fits, so 10 tokens are used. The complaint needs 22 more, which would push the total to 32. That's over 30, so it's skipped. The next two still fit, and the total ends at 20 of 30.
Whether that's right depends on the job. Say you want a summary of problems. Then the complaint is the review you'd least want to lose. You would sort complaints to the front before packing. The loop keeps you inside the budget. Deciding what matters most, and ordering the list that way, is still your call.
Show the answer
All four, using 42 tokens
The costs are 10, 22, 3 and 7. With room for 45, the running total goes 10, 32, 35, 42, and each step stays under 45. So all four fit, using 42 tokens.
Practice what you just read
- 4 code examples you can edit and run
- a practice exercise that checks your code
- a 5-question quiz
- 5 review cards for spaced repetition
Try a quiz question
Which of these probably uses the most tokens for its number of characters?