ChatGPT Token Counting — API Billing and Context Window Math

(Updated: July 16, 2026 ) ChatGPT tokens API

Added RAG, pasted the whole wiki into system prompt, enabled JSON mode with a 400-line schema — invoice doubled. r/OpenAI thread says “tokens are scam” while another comment correctly notes you sent 40k tokens per call. Both happen because token math is invisible until billing arrives.

  • Words × 1.3 = tokens always — English prose ≈ 0.75 tokens/word; code, JSON, and CJK are different. Measure, don’t guess.
  • Only user message counts — System prompt, few-shot examples, tool definitions, and chat history all bill as input.
  • Output tokens are cheap so who cares — At scale, long completions (chain-of-thought leaking into output) dominate cost.
  • Context window = free space — Filling 128k context still costs input token rate for every token you send.

What counts as input

ComponentBills as input?
System messageYes
Prior turns in chatYes (unless truncated server-side)
User messageYes
Image tokens (vision)Yes, tile-based
Function/tool schemasYes
Retrieved RAG chunksYes — every request

Truncation strategies (keep last N turns, summarize old context) exist because unused history still costs money.

Rough English estimates

ContentApprox tokens
1 word~1.3 tokens (inverse: ~0.75 words/token)
1 page (~500 words)~650–750 tokens
10k word doc~13k–15k tokens

Use token counter tool or official tiktoken for exact counts on your model.

import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
print(len(enc.encode("Your prompt here")))

Context window vs billing

Model advertises 128k context — that’s capacity, not price. Sending 100k tokens input at $/1M input rate is the real constraint. Design for:

  • Smaller retrieved chunks (top-k, MMR)
  • Summarized memory instead of full transcript
  • Structured outputs to cap completion length (max_tokens)

Cost reduction that actually works

  1. Count before ship — Wire tokenizer into CI for prompt templates.
  2. Shrink system prompt — Remove duplicate instructions buried in user messages too.
  3. Cache (where supported) — Prompt caching reduces repeated prefix cost on supported models.
  4. Cheaper model for routing — Classify intent with small model; escalate only when needed.
  5. Don’t log full prompts to Slack — Also a security issue.

ChatGPT UI vs API

Plus subscription ≠ API credits. Playground and ChatGPT app token usage doesn’t map 1:1 to API billing rates. Production apps need API dashboard metrics.

Vision token intuition

Images aren’t “one token” — they’re tiled and encoded. A 1920×1080 screenshot can be thousands of tokens. Resize before send if detail isn’t needed.

Streaming and partial output still bill

Streaming completions (stream: true) charge for tokens actually generated, not the max_tokens ceiling. Stopping early saves output cost — but input was already sent and billed in full when the request started. Client-side abort does not refund input tokens.

Model-specific tokenizer drift

gpt-4, gpt-4o, and open-weight models each use different tokenizers. A prompt that fits 8k on one model may overflow another. Never copy max_tokens settings across model migrations without re-measuring — migration posts on r/LocalLLaMA are full of “same prompt, different context usage” surprises.

Batch API vs realtime

OpenAI Batch (and similar queue products) discounts latency for offline jobs. Token counts are identical; only price and SLA differ. If you’re embedding 2M product descriptions overnight, batch + pre-counted chunks beats hammering realtime endpoints and wondering why rate limits fired at 2am.

Tool: count before you call

The token-counter tool estimates tokens for prompts locally — sanity-check RAG payloads before they hit production.

Try the token-counter tool

TL;DR

Billed tokens = everything you send + everything the model returns. English ≈ ¾ word per token is a rough guide; code/RAG/system prompts blow estimates. Measure with tiktoken, trim history, size RAG chunks deliberately.