Topic 13.1
Tokens and Cost in the UI
In one line
A model charges for tokens in and tokens out, and every request resends the whole conversation. The screen can estimate the cost before sending, show the real usage after, warn about long pastes, and say when an answer was cut off by a length limit.
Think of it like this
A taxi with a meter. The meter runs while you ride, you can see roughly what the fare will be before you get in, and you get an exact bill at the end. A taxi that never showed a fare would make people nervous, and one that charged you for the whole route again at every red light would empty your wallet. An AI feature has a meter too, and it does charge for the whole route again at every turn.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Token
- A small piece of text that a model reads and writes. Providers charge by the token.
- Tokens in / tokens out
- Input tokens are everything you send. Output tokens are what the model writes back. They usually have different prices.
- Usage
- The token counts a provider reports after a request: input, output and total, with some details.
- Context window
- The most tokens a model can handle in one request, input and output together. Look up the real number in your provider's documentation.
- Finish reason
- Why the model stopped:
stop(it finished),length(it hit the output cap),content-filter(it was blocked), and others. - Estimate
- A guess made before sending, such as characters divided by 4. Useful for warnings, never for billing.
- Message metadata
- Extra data attached to a chat message, such as the usage, sent on the final event of the stream.
- Unit (made up)
- The pretend money in this course's examples. Real prices come from your provider's price page.
Step by step
01Where tokens are counted
Three moments matter. Before sending, the page can only estimate. During the stream, nothing is known. At the end, the provider reports the real usage in the last event, and that is the number to store and show.
02Estimate and warn
The first live example counts tokens as you type, with a made-up limit of 50 tokens. It shows "N of 50 tokens", a gentle warning at 80%, and at 100% it disables Send and says by how much the text is too long. The scripted visitor then pastes a very long message.
Read the console first: an empty box is 0, "a" is 1, the Hindi sentence has 19 characters but is estimated at 16 tokens because each non-plain character counts as one, and 2,000 plain characters are 500 tokens.
03Read the usage after
In the second example the route puts part.totalUsage into the metadata of the finish event. The page shows "19 in, 8 out, about 0.043 units" under the answer. The made-up price is (19 x 1 + 8 x 3) / 1,000 = 0.043 units.
The second question is an essay request that the stand-in cuts off with finish reason length. The page adds "stopped at the length limit". Look at the input count: 65 in for the second question against 19 for the first, because the first question and answer were sent again.
04Watch the history grow
The third example sends five short questions twice. With the whole history the input tokens go 19, 63, 108, 154, 196 (total 540). Keeping only the last 3 messages gives 19, 63, 64, 66, 63 (total 275). A 2,000-character paste costs 514 tokens on turn 1 and 554 tokens on a one-word follow-up, because it travels again.
The stand-in estimates the input from the size of the prompt, so the exact numbers are the stand-in's. A real provider reports its own counts, but the shape (growing every turn) is the same.
05Words for people
Notice the page never says "tokens" to a shopper in the final design: a counter like "1,200 characters left" or a bar is enough. The developer view shows the numbers. And the server repeats the count: the page check only helps honest users.
Try it yourself
- 1
Change the price
In the second example change the made-up price to
5per 1,000 tokens out. Run it and compare the two cost lines. Only the cost changes, never the token counts. Prices are data: keep them in one place, because your provider will change them. - 2
Count Hindi honestly
In the first example add the word
chaiand a Hindi word of your choice to the list at the top. Compare the plain English estimate with the Hindi one. Characters are a poor guide for non-Latin text, so the estimate is pessimistic on purpose. - 3
Trim to 2 messages
In the third example change
run("last 3 messages ", 3)to keep 2. The cost per turn becomes flat, and the model now sees only your last question and its last answer. Ask yourself which follow-up questions would break.
Code & diagrams
The estimate here is a safe-side rule made up for the example. A real tokenizer (your provider's) gives different numbers, especially for Hindi and emoji.
Preview
Console
Nothing logged yet.Expected console output
"" → tokens: 0 | cost to send: 0.000 units
"a" → tokens: 1 | cost to send: 0.001 units
"Where is my order ORD-1042?" → tokens: 7 | cost to send: 0.007 units
"मेरा ऑर्डर कहाँ है?" → tokens: 16 | cost to send: 0.016 units
"☕☕☕☕" → tokens: 4 | cost to send: 0.004 units
"xxxxxxxx…(2000 characters)" → tokens: 500 | cost to send: 0.500 units
empty | 0 of 50 tokens | send disabled: true | warning: none
short | 7 of 50 tokens | send disabled: false | warning: none
close | 49 of 50 tokens | send disabled: false | warning: Getting close to the limit.
pasted | 520 of 50 tokens | send disabled: true | warning: Too long by about 470 tokens. Shorten it, or send the key part only.The stand-in estimates the input from the size of the prompt. A real provider reports its own counts. The second request is bigger (65 in) because the first question and answer were sent again.
Preview
Console
Nothing logged yet.Expected console output
screen: Orders ship in one to two working days.19 in, 8 out, about 0.043 units
screen: Orders ship in one to two working days.19 in, 8 out, about 0.043 units | Tea began in China and then spread along trade routes to65 in, 11 out, about 0.098 units · stopped at the length limitTrimming to the last 3 messages caps the cost, and the model forgets everything older. Real apps often summarise the old part instead of dropping it.
Preview
Console
Nothing logged yet.Expected console output
whole history → input tokens per request: 19, 63, 108, 154, 196 | total: 540
last 3 messages → input tokens per request: 19, 63, 64, 66, 63 | total: 275
big paste: turn 1 sent 514 tokens, turn 2 sent 554 for a one-word follow-upBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Count characters, not tokens
In the first example change the last line of estimateTokens to return Math.ceil((plain + other) / 4);.
Break #2
Show zeros while the answer is still arriving
In the second example replace the first line of Usage with const u = (metadata && metadata.usage) || {}; and delete the following const u = metadata.usage; line. Add console.log("usage line drawn, answer finished:", Boolean(metadata && metadata.usage)); before the if.
Myth vs fact
Myth
A token is a word.
Fact
A token is a piece of text. Short common words are one token, long or rare words are several, and a space or punctuation mark can be a token or part of one. Other scripts and emoji take more tokens per character.
Myth
I only pay for the answer.
Fact
You pay for tokens in and tokens out. Input includes the system instructions and the whole conversation so far, sent again on every turn.
Myth
If the page checks the length, the server does not need to.
Fact
A request can be sent without your page at all. The server must count and refuse too. The page check is a courtesy to honest users.
Myth
The estimate is the bill.
Fact
The estimate is a guess made before sending. Only the provider's reported usage is the real count.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Store the usage per request (user id, feature, input, output, finish reason, model id) in your own database, not only in the provider's dashboard. That table is how you find the expensive feature, the expensive user and the prompt change that doubled the bill.
- ▸
Prices differ by model, cached input is often cheaper, and reasoning tokens are billed as output. Read
inputTokenDetails.cacheReadTokensandoutputTokenDetails.reasoningTokenswhen your provider reports them, and keep prices in configuration rather than in code. - ▸
A
lengthfinish is a product decision: raise the cap, ask for shorter answers in the instructions, or offer Continue. Do not silently retry a cut-off answer in a loop, because each retry costs again. - ▸
On Stop, the browser closes the connection and the final usage event may never arrive. Treat that request's usage as unknown and let the server record whatever its own callbacks received. See Topic 3.1 for what stopping does to the stream.
Remember this
- 1
The problem this solves: A model is paid for by the token, and a user does not know what a token is. Without any signal, one person pastes a whole book into the box, and a team finds out at the end of the month. This topic shows how to estimate before sending, read the real count afterwards, warn about long pastes, and explain a cut-off answer.
- 2
A token is a small piece of text: a short word, part of a long word, a space with a word, a punctuation mark. Providers charge per token, with one price for tokens in (everything you send: the question, the instructions, the earlier conversation) and usually a different price for tokens out (the answer). This course uses made-up prices only, like "1 unit per 1,000 tokens in and 3 units per 1,000 tokens out". Your provider's price page has the real, current numbers, and they change.
- 3
Estimating before you send. The real count comes only from the provider, after the request. Before sending you can only estimate. A common rule of thumb for plain English is about 4 characters per token. It is a rough guide, and it under-counts other scripts: Hindi letters, accented letters and emoji often take one token or more *each*. The first live example uses a deliberately pessimistic rule (plain characters count 1/4 token, every other character counts 1) so the estimate errs on the safe side. Use it to warn and to block absurd input. Never use it for billing.
- 4
Usage after the request. Every result carries a
usageobject. In the AI SDK it hasinputTokens,outputTokensandtotalTokens, plus details such asinputTokenDetails.cacheReadTokens(input read from the provider's cache, usually cheaper) andoutputTokenDetails.reasoningTokens(hidden thinking, billed as output). Any of these can beundefinedif the provider did not report it. The count is known only when the model has finished, so on a streamed answer it arrives in the last event: send it to the page in message metadata (messageMetadataontoUIMessageStreamResponse, thefinishpart carriestotalUsage). While the answer is still arriving, show nothing, not zeros. - 5
The conversation is resent every turn. A model has no memory between requests. To "remember" turn 1, the app sends turn 1 again inside turn 2. So input tokens grow with every turn, and the total over a long chat grows much faster than the number of questions. The third live example shows 19, 63, 108, 154, 196 tokens for five short questions. Options: send only the last few messages, summarise old ones, or start a new chat (the trade-off: the model forgets what you cut). A single long paste is paid for again on *every* later turn.
- 6
Two different limits. The context window is the most tokens a model can handle in one request, input and output together. Your provider's documentation states it for each model. The maximum output is a cap you set on the answer. When the answer hits the cap, the model stops mid-sentence and reports the finish reason
length. Treat that as its own state on screen ("This answer was cut off because it reached the length limit"), offer a shorter question or a Continue button, and never present a cut sentence as a finished fact. - 7
Show cost in words people understand. Most users should not see units of money or the word "token". Show a character or word count, "about 3 pages", or a simple bar that fills toward a limit, and keep exact token counts for a developer view. Whatever the screen says, the server counts again: the browser can be edited, and a limit checked only in the page is a suggestion.
Explain it without notes
Why can a page only estimate the cost of a request before sending it, and what does it use the estimate for?
Where does the real usage of a streamed answer come from, and why can't the page show it while the answer is still arriving?
Why does the cost of a long chat grow faster than the number of questions, and how can you control it?
What does the finish reason length mean, and what should the screen do about it?
Practice
Add a "Developer view" checkbox to the second example. Unchecked, the usage line shows "About 1 page" (output tokens divided by 400, rounded up) and never the word token. Checked, it shows the exact counts.
Change the third example so that each turn also logs the made-up cost (1 unit per 1,000 tokens) of the input, and prints the running total for both strategies.
Trade-offs
- ↔
Trimming history cuts cost and latency and makes the model forget. Summarising keeps more meaning at the price of an extra model call. Choose by how much your users rely on earlier turns.
- ↔
A strict input limit protects the budget and annoys people who really have a long document. A common compromise is a generous limit for the box and a clear message that suggests sending the key part.
Done when you can
I can explain tokens in, tokens out and why the price is per token.
I can estimate tokens in the page and say what the estimate is allowed to decide.
I can read the usage from a finished answer and send it to the page as metadata.
I can explain why history makes each turn cost more and name two ways to control it.
I can show a length cut-off as its own state on the screen.