AI coding

Token Math for Long Files: Counting a Codebase Before You Load It

Antonio Evans11 min read

A developer's desk seen from above: a laptop showing a terminal that lists file paths with line and token counts, a printed directory tree with some folders circled and others crossed out, a calculator, and a coffee cup.
Key points

Key points

  1. 1Lines and bytes per file are exact and cheap to compute, so start there before you reach for a tokenizer.
  2. 2Token counts per file can be estimated within a few percent by sampling whole files and applying a lines-to-tokens ratio.
  3. 3The number you actually need is the total tokens the agent will read, not the length of the answer it will write.
  4. 4Once that total is known, the choice between summarizing, retrieving on demand and skipping files follows from one ratio: total tokens divided by usable context.

Why the input side is the hard number

Most token budgeting conversations focus on output length, because output tokens are the ones that feel expensive and unpredictable. For an agent working against a codebase, that framing is backwards. The input dominates. A single agentic task in production can consume 15,000 to 1,000,000 or more tokens per request depending on how many steps it takes, while a single-turn chatbot reply sits in the 200 to 2,000 token band and a retrieval-augmented question with fetched documents lands in the 2,000 to 12,000 range per this token usage reference. The spread between those categories is roughly an order of magnitude at every boundary, and it is driven almost entirely by how much material gets pulled into the prompt.

There is a second reason to count before loading. Agents degrade with context length well before they hit the nominal window limit. After roughly 25 to 30 tool calls, even 200K-token windows show coherence problems, with models forgetting early results and re-executing completed steps as described in this survey of long-horizon agent planning. The same analysis notes that accuracy can drop 20 to 30 percentage points when relevant information sits in the middle of a long window rather than at its edges in this analysis of agentic task complexity. A codebase that technically fits is not automatically a codebase the agent can use well.

That is the practical argument for measuring input first: fitting is a necessary condition, not a sufficient one, and the gap between "fits" and "works" is where most of the wasted spend lives.

Level one: lines and bytes, exact and free

Before any tokenizer runs, you can get exact, reproducible counts for every file. Lines and bytes have no model dependency, no encoding question and no sampling error. They cost a directory walk.

On a Unix-like system, three tools cover most of it:

# Total lines and bytes per tracked file, sorted by size
git ls-files -z \
  | xargs -0 -I{} sh -c 'wc -lc "{}"' \
  | sort -k2 -n -r \
  | head -40

That gives you a ranked list of the files that will dominate any token estimate. In most repositories the top 40 files account for a large share of the total, which means the expensive part of the calculation is a short list, not the whole tree.

Two refinements matter. First, exclude what the agent will never read: vendored dependencies, generated code, lockfiles, binary assets, minified bundles. If those are in your repository, git ls-files already excludes ignored files, but generated directories that are committed need an explicit filter. Second, exclude files by extension. A dependency lockfile can be tens of thousands of lines of near-useless tokens; an image file has bytes but almost no meaningful text tokens.

The output of level one is a table: path, lines, bytes. That table is your inventory. Everything downstream refines it.

Level two: tokens per file

Token counts are model-specific, and the same text can produce different counts under different encodings and providers as OpenAI's own documentation notes. That means there is no single "correct" token number for a file, only a number for a specific tokenizer. This is the first place where precision has diminishing returns.

For a first pass, the standard heuristics are good enough: one token is roughly four characters or about three-quarters of a word for English text per the same OpenAI guidance. Those heuristics are calibrated on prose. Source code tokenizes differently: the reference table puts ordinary prose at about 1.33 tokens per word and source code at 1.5 to 2.0 tokens per word in the token usage guide. Using the prose ratio on a codebase systematically undercounts, sometimes by a third.

A worked example makes the gap concrete. Take a repository with 400,000 lines of code at an average of 40 characters per line. That is 16 million characters. At four characters per token, the prose heuristic predicts 4 million tokens. At the code-calibrated ratio of 1.75 tokens per word, with roughly six words per line at 40 characters per line, the estimate lands closer to 4.2 million. Those are not catastrophic differences, but they compound: if your agent makes 30 tool calls and re-reads context each turn, a 5 percent underestimate becomes a 5 percent underestimate of a very large number.

When you need a real count, use the tokenizer that belongs to the model you will call. The reference guide to counting tokens lists the exact route per provider: tiktoken for OpenAI, the count-tokens endpoint for Anthropic, countTokens for Gemini, and the published Hugging Face tokenizer for open models per this OpenReplay guide to token counting. It also flags an important trap: Anthropic's counting endpoint returns an estimate, and the billed figure comes from the response's usage fields. Anthropic's tokenizer has also changed across model generations, with later Claude models turning the same text into roughly 30 percent more tokens than earlier ones per the same guide. A count taken against the wrong generation is not a conservative estimate; it is a different number.

If you want a command-line tool that walks a directory and reports token counts, several exist. token-count wraps tiktoken and can count a string, file or directory as documented in its repository. The count-tokens package adds directory patterns, streaming for large files and a --max-tokens flag that warns when a file exceeds a limit per its PyPI page. Either one converts level one's inventory into level two's token table.

Sampling versus counting everything

Exhaustive tokenization of a large repository is slow, and it produces a number more precise than you need for a decision. Sampling gives you the same decision at a fraction of the cost, provided you sample correctly.

The wrong way to sample is to grab the first 20 files returned by the directory walk. That sample is biased by whatever the filesystem returns first, which in practice means the root-level configuration files, not the code that dominates the byte count. The right way is to sample by size. Sort files by line count, then take a stratified sample: a handful of the largest files, a handful from the middle of the distribution, and a handful of the smallest. Tokenize each, compute tokens per line for each stratum, and weight back to the full population.

The sampling error is small because lines-to-tokens is a stable ratio within a file type. The predictable failure is mixing file types into one pool: a Markdown documentation file and a minified JavaScript bundle have very different tokens-per-line ratios, and pooling them produces a ratio that describes neither. Stratify by extension.

ApproachCostAccuracyWhen to use it
Lines and bytes onlySecondsExact for size, no token informationAlways, as the first pass
Characters divided by fourSecondsUndercounts code by 25 to 40 percentRough sizing of prose, never for billing
Stratified sample, 5 to 10 percent of filesSeconds to a minuteWithin a few percent of exhaustiveAny codebase over a few thousand files
Exhaustive tokenizationMinutes, scales with sizeExact for the chosen tokenizerFinal check before a large run, or when files are few

The rule that falls out of this table: sample by size and extension, then spend the exhaustive tokenizer run only on the files that survived the cut. If your top 40 files are 60 percent of the total, tokenizing those 40 exactly and estimating the tail gives you a number within a couple of percent for the price of 40 tokenizer calls.

One package worth knowing about exposes a simple programmatic interface for this: TokenCount takes a model name at construction and then counts strings, files or directories per the token-count repository. That makes the sample-and-weight loop a short script rather than a project. If you would rather press a button than write the loop, browser token counters accept a paste and report token, word and character counts locally, with the caveat that they run a reference tokenizer rather than every provider's per token-calculator.net.

The gap between your estimate and the bill

Even an exact tokenizer count of every file is not the billed number. Several things sit between your count and the invoice.

The billed prompt is the whole request body: system prompt, role framing, tool schemas, and the full re-sent conversation history, not just the file contents you counted per the OpenReplay guide. Tool definitions alone can be substantial. A coding agent with a dozen tools and their JSON schemas adds tokens before it has read a single line of your repository.

Then there is the multiplier from multi-turn work. Context doubles with each turn because history accumulates, and a ten-cycle reasoning loop consumes fifty times the tokens of a single pass per the agentic complexity analysis. Unconstrained coding agents on real software tasks have been measured at $5 to $8 per task per the same analysis. A single file read is not the unit of cost; a file read repeated across every turn of a thirty-turn loop is.

The reference budget guidance is to multiply your raw estimate by 1.7 to 2.0 to account for retries, system prompts and context overhead per the token usage guide. That multiplier is doing real work: on a four-million-token codebase estimate, the difference between raw and budgeted is another four million tokens of headroom.

The structural fix is to treat the token budget as first-class agent state rather than an afterthought. Budgets need to be checkpointed and enforced during execution, not checked at the end per the same analysis. If you are routing work across several models or providers, centralizing that accounting saves a lot of duplicated bookkeeping, which is one reason a single API surface like Quanta Inference is worth having in the stack.

The decision rule: summarize, retrieve, or skip

With a total token count in hand, the question becomes what to do with each file. The decision rule is a single ratio.

Let T be the total tokens the agent would need to read for a complete answer, and let W be the usable context window, meaning the window minus the room you reserve for the system prompt, tool schemas, the growing history and the output. Call the ratio R = T / W.

RWhat it meansWhat to do
R under 0.3Everything fits with room to spare, and coherence is not at riskLoad the files directly. Do not add machinery.
0.3 to 1.0Everything technically fits, but the middle of the window is where accuracy dropsLoad the most relevant files, summarize the rest, keep the total near 0.5
1.0 to 5.0Does not fit. The agent cannot see everything even once, let alone across turnsRetrieve per step. Give the agent search and file-read tools, not a preloaded context
Above 5.0Retrieval at file granularity is too coarse; each turn pulls in more than it needsSummarize into an index first, then retrieve against the index

Two thresholds in that table need justification. The 0.3 figure is a hedge against degradation in the middle of the window; a context that is one third full leaves the model room to attend across its whole history. The 0.5 target inside the second band comes from the same logic: you want headroom for the tool outputs that will arrive during execution, which you did not count when you measured the codebase.

The summarize-versus-retrieve choice inside that rule is not arbitrary. Summarizing loses information permanently and costs a pass over the whole corpus to produce. Retrieving keeps information intact but pays a search cost per step and risks pulling the wrong files. Summarize when the corpus is large relative to the window and the same subset is needed repeatedly; retrieve when the corpus is large and each step needs a different, narrow slice. A codebase larger than five times the usable window almost never needs to be read in full for a single task, so the default should be retrieval with a good index, not a wholesale summary.

The rule also explains why an estimate that is 20 percent off does not change the decision. If R is 3.0, being wrong by 20 percent moves it to 3.6 or 2.4, and both are still in the retrieve band. Estimation precision matters at the boundaries, not in the middle.

Chunking without breaking the code

Retrieval granularity matters as much as the decision to retrieve. Splitting a file at a fixed token count produces chunks that begin mid-function and reference variables that were defined in a previous chunk, which is the code equivalent of cutting a sentence in half.

Chunk on structural boundaries instead. In most languages those are functions, classes, methods and top-level blocks. A chunk that contains one complete function with its signature, body and docstring is intelligible on its own; a chunk that contains the second half of one function is not. Language-aware parsers exist for this, and the tooling discussed in this Stack Overflow thread on counting code tokens is a reminder that "token" means two different things in this context: lexical tokens in a programming language and model tokens. The structural parsers use the former; you are budgeting the latter. Do not confuse the two counts.

For each chunk, record the file path, the enclosing symbol name, the start and end line, and the token count. That metadata is what makes retrieval work: the agent can ask for "the definition of the function it just saw called" rather than searching by text similarity.

There is a parallel here worth noticing. Breaking work into small, independently meaningful pieces is the same principle that agile teams apply when they split large stories into vertical slices that each deliver something demonstrable, rather than horizontal slices across architectural layers as described in this guide to story splitting. The failure modes are similar too: a horizontal split produces pieces that cannot stand alone, and so does a chunk boundary that cuts through a function per this comparison of vertical and horizontal slicing. Keep chunks vertical: whole units of behavior, not layers of syntax.

Putting it in a script

The whole workflow collapses into a short pipeline. This is deliberately minimal; the point is the sequence, not the implementation.

import subprocess
from pathlib import Path
from token_count import TokenCount

SKIP = {".lock", ".min.js", ".map", ".png", ".jpg", ".svg", ".woff"}

files = subprocess.run(
    ["git", "ls-files"], capture_output=True, text=True
).stdout.splitlines()

tc = TokenCount(model_name="gpt-4")  # tokenizer matching your target model
inventory = []

for name in files:
    if Path(name).suffix in SKIP:
        continue
    p = Path(name)
    data = p.read_text(errors="ignore")
    lines = data.count("\n") + 1
    inventory.append({
        "path": name,
        "lines": lines,
        "bytes": p.stat().st_size,
        "tokens": tc.num_tokens_from_string(data),  # replace with sampling
    })

inventory.sort(key=lambda r: r["tokens"], reverse=True)
total = sum(r["tokens"] for r in inventory)
print(f"files={len(inventory)} tokens={total:,}")

Three edits make this production-grade. Replace the full tokenization with stratified sampling plus weighting, as described above. Add a per-extension breakdown so a single anomalous file type does not skew the total. And swap TokenCount for the tokenizer of the model you will actually call, since the same repository will produce different totals under different encodings per the OpenAI token guidance.

Once the total is printed, compute R and read the decision off the table in the previous section. That is the whole method: exact sizes, sampled token ratios, one ratio, one band, one decision.

If the agent work that follows is long-running, the same discipline applies inside the run. Agents that hold a stable goal structure while adapting the path, with explicit stop conditions and persisted progress, stay coherent far longer than agents that simply keep going per this guide to agent planning. Token budgets are the same idea applied to context: checkpoints, limits, and a clear rule for what happens when the limit is reached. Teams that would rather hand the whole pipeline to a specialist can do so through Quanta Experts, and the counting logic itself is worth packaging as a reusable skill so every agent in the fleet measures input the same way.

FAQ

FAQ

Why not just let the model handle a large codebase and see what happens?

Because the failure is expensive and hard to detect. Agents that underestimate scope in their first decisions commit to a shallow plan that produces a confident but incomplete answer, and the analysis of 3,100 trajectories describes recovery from that as effectively impossible without restarting per this complexity analysis. By the time the context limit is hit, you have paid for most of the run.

How accurate does my token estimate need to be?

Accurate enough to place the ratio in the right band. A 20 percent error does not change a decision to retrieve rather than preload, unless the total sits right at a threshold. Stratified sampling gets you within a few percent for a fraction of the cost of exhaustive tokenization, which is well inside that tolerance.

Should I count tokens with the tokenizer of the model I am calling?

Yes, if the count will influence a billing or context-window decision. The same text produces different counts under different encodings, and providers document that explicitly per OpenAI's token guidance. The characters-divided-by-four heuristic is fine for early sizing but breaks on code, JSON and emoji per this counting guide.

What about files I exclude from the count but the agent might still need?

Treat them as retrieval targets rather than preloaded context. Lockfiles, generated code and binary assets are usually needed only for narrow, specific questions, and pulling them into every turn is the most common way a modest repository turns into a large bill. Index them, expose a search tool, and let the agent fetch on demand.

How do I budget for a multi-turn agent rather than a single read?

Apply a multiplier to the raw estimate. The reference figure is 1.7 to 2.0 times raw token usage to cover retries, system prompts and context overhead per the token usage guide. For agents that reuse the same large context across many turns, prompt caching on the input side can change the arithmetic substantially, so measure the cached share before you set a hard per-task budget.

Sources

Sources

  1. Token Usage Guide 2026: How Many Tokens AI Really Uses
  2. Long-Horizon Planning and Goal Decomposition in AI Agents
  3. Agentic Task Complexity Estimation: Budget Tokens Before ...
  4. Understanding and counting tokens | OpenAI Help Center
  5. How to Count Tokens and Estimate LLM API Costs
  6. felvin-search/token-count
  7. count-tokens
  8. Token Counter & Calculator for LLMs, OpenAI, Claude, Gemini
  9. How can I count words or tokens in my code?
  10. The Humanizing Work Guide to Splitting User Stories
  11. User Story Splitting - Vertical Slice vs Horizontal Slice - Visual Paradigm
  12. AI Agent Planning: Task Decomposition, Execution & ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles