Understanding Billing, Credits, and What a Request Costs

Ma Mahalakshmi V Updated 13 Sep 2026
20 min read ·Lesson 5 of 10

The API Is Prepaid, and Separate from ChatGPT

The first thing to get straight, because it produces a steady stream of confused first billing errors: a ChatGPT subscription gives you no API credit.

ChatGPT Plus, Pro and Team are consumer products with monthly subscriptions. The API is a developer product billed on prepaid credits. They are separate products with separate balances on the same account. Paying for one does nothing for the other.

The API model works like a prepaid phone: you buy credit in advance, each request deducts from the balance, and when the balance reaches zero the requests stop. There is no monthly invoice for what you used, and there is no unlimited tier.

Why prepaid rather than postpaid? Because a bug in your code can generate an enormous bill in minutes. A loop that accidentally re-sends a 100,000-token document a thousand times is a plausible mistake and, on an expensive model, an expensive one. A prepaid balance is a hard ceiling on how badly that can go — you can only lose what you put in. Treat that as a feature, not an inconvenience.

Buying and Managing Credit

The mechanics, as they currently stand:

SettingValue
Minimum purchase$5 (the interface defaults to $10)
Credit expiry1 year from purchase, non-refundable
Auto-recharge minimum$5
Maximum balanceSet by your trust tier, which grows with usage and payment history

Auto-recharge is enabled by default during setup. When your balance falls below a threshold you choose, it automatically purchases enough credit to bring you back to a target amount. This is convenient for production, where running out means downtime, and it is a genuine risk while learning, where running out is a useful signal that something is wrong.

There is a guard: an optional monthly recharge limit caps how much can be bought automatically per month. Manual purchases do not count toward it. If an automatic recharge would exceed the remaining allowance, only the available amount is added — provided that amount still meets the $5 minimum.

Recommendation for this course: buy $5 to $10, and either turn auto-recharge off or set a low monthly limit. The entire course, run generously on gpt-5.6-luna, costs a small fraction of that. With auto-recharge off, a runaway loop stops when the balance empties instead of quietly buying more credit to keep going.

When credit runs out, requests begin failing with a billing error whose code is credit_balance_exhausted, surfaced through the SDK as a RateLimitError (HTTP 429). This is worth remembering because it looks identical in type to a genuine rate limit while requiring the opposite response: rate limits resolve if you wait and retry, an empty balance never does. Retrying an exhausted balance just burns your retry budget.

One more thing the help documentation makes explicit and people miss: having credit does not guarantee your request succeeds. Rate limits, token-per-minute limits, and organisation spend caps are enforced separately. A full balance and a 429 are entirely compatible.

Tokens: The Unit You Are Billed In

Everything is priced per token. A token is a chunk of text produced by the tokeniser — typically a common word, a word fragment, or a piece of punctuation.

Working approximations for English prose:

  • 1 token ≈ 4 characters ≈ 0.75 words
  • 1,000 tokens ≈ 750 words
  • 100,000 tokens ≈ a 250-page book

These ratios shift with content, sometimes dramatically:

ContentTokens per 1,000 characters (approx.)
English prose~250
Source code~330 (punctuation and indentation tokenise poorly)
JSON~400 (every brace, quote and colon costs)
Non-Latin scriptsOften 2–4× more than the equivalent English

That last row has a real consequence: an application serving users in a language the tokeniser handles less efficiently costs meaningfully more per user for the same amount of meaning. If you are budgeting for a multilingual product, measure with real text in each language rather than extrapolating from English.

The Price Table, and the Asymmetry That Matters

Current published rates, per million tokens:

ModelInputCached inputOutput
gpt-6-astra$10.00$1.00$50.00
gpt-5.6-sol$4.00$0.40$20.00
gpt-5.6-terra$2.00$0.20$12.00
gpt-5.6-luna$0.20$0.02$1.20

Prices change. Always verify against the current pricing page before doing budget arithmetic that matters.

Two patterns in that table drive almost every cost decision you will make.

Output costs five to six times what input costs. Across every model in the line-up, generating is dramatically more expensive than reading. This inverts the intuition that a long prompt is the expensive part. A 5,000-token document summarised into 200 tokens costs, on gpt-5.6-terra, $0.010 for the input and $0.0024 for the output — the input dominates. But an instruction that produces 2,000 tokens of rambling from a 100-token question costs $0.0002 in and $0.024 out, and the output dominates by more than a hundredfold. Verbosity is the expensive failure mode, and "be concise" in your instructions is a cost control, not just a style preference.

Cached input is ten times cheaper than fresh input. This is the single largest lever available to most applications, and it is covered below.

The spread between models is fifty-fold. gpt-6-astra costs fifty times gpt-5.6-luna on input and roughly forty times on output. This is why "which model" is a budget decision. A classification task that a small model handles correctly does not become more correct on a flagship model — it becomes fifty times more expensive at the same accuracy.

Reading the usage Object Properly

Every response reports exactly what it consumed:

usage = response.usage

print("Input tokens: ", usage.input_tokens)
print("  of which cached:", usage.input_tokens_details.cached_tokens)
print("Output tokens:", usage.output_tokens)
print("  of which reasoning:", usage.output_tokens_details.reasoning_tokens)
print("Total:        ", usage.total_tokens)

The two nested detail fields are the ones people overlook, and both change your bill:

cached_tokens is the portion of your input that was served from OpenAI's prompt cache and therefore billed at the cheaper cached rate. If this is zero on a call where you expected caching, your prompt prefix is not stable — see below.

reasoning_tokens counts tokens a reasoning model generated while working through the problem. These are billed as output tokens and they are not included in output_text. This is a genuine surprise: a reasoning model can return a three-sentence answer while having been billed for thousands of tokens you never see. If your cost estimates are built from the visible answer length, they will be badly wrong for reasoning models. Unit 3, Lesson 4 covers reasoning_effort, which is the control for how much of this the model does.

Prompt Caching: The Biggest Lever

When you send a request whose beginning matches a recent request, the shared prefix can be served from cache at roughly one tenth the input price.

The mechanism matters because it determines how you write prompts. Caching works on exact prefix matches from the start of the input. The cache is checked from the first token forward, and the match ends at the first difference. So:

# Cacheable: the long stable part comes first
instructions = LONG_SYSTEM_PROMPT + STYLE_GUIDE + FEW_SHOT_EXAMPLES
input = user_question              # varies per request
# Not cacheable: the varying part is at the front
input = f"User {user_id} at {timestamp} asks: {question}\n\n{LONG_SYSTEM_PROMPT}"

In the second version, user_id and timestamp differ on every call, so the prefix diverges at token one and nothing is cached — including the long system prompt sitting behind it. The fix is purely structural: put everything stable at the front and everything variable at the back.

The economics are worth spelling out. Suppose you have a 4,000-token system prompt and 100 tokens of user question, on gpt-5.6-terra:

  • Uncached: 4,100 tokens × $2.00/M = $0.0082 per request
  • Cached prefix: (4,000 × $0.20/M) + (100 × $2.00/M) = $0.0010 per request

That is an 88% reduction on the input side, achieved by ordering your prompt correctly. At a million requests a month, it is the difference between $8,200 and $1,000. Unit 12, Lesson 4 covers caching in depth, including how long entries persist and what breaks a match.

Worked Examples

A single simple call

Your Lesson 4 request: 15 input tokens, 31 output tokens, on gpt-5.6-luna.

Input:  15 / 1,000,000 × $0.20 = $0.000003
Output: 31 / 1,000,000 × $1.20 = $0.0000372
Total:  $0.0000402

About four thousandths of a cent. You could run 25,000 of these for one dollar. This is why a small credit balance is genuinely plenty for learning — and why the numbers stop feeling irrelevant only when you multiply by traffic or by model price.

The quadratic cost of a conversation

This one surprises everyone, and it is the most important cost dynamic in the course.

Because the model is stateless, a chatbot resends the entire history on every turn. So turn 10 does not cost the same as turn 1 — it costs roughly ten times as much on the input side.

Assume each turn adds 100 tokens of user message and 200 tokens of reply, on gpt-5.6-terra:

TurnInput tokens sentCumulative input tokens
1100100
2400500
37001,200
51,3003,500
102,80014,500
205,80059,000

Input cost grows with the square of the number of turns, not linearly. A 20-turn conversation sends 59,000 input tokens in total, not the 2,000 a naive count suggests. On gpt-5.6-terra that is $0.118 of input for one conversation — thirty times the $0.004 you would estimate by counting only the new messages.

Three consequences follow, and all three have units devoted to them:

  • Long conversations must be trimmed or summarised. Unit 4, Lesson 6 covers compaction.
  • Caching helps enormously here, because the history prefix is stable and grows only at the end — exactly the shape the cache rewards.
  • The context window is a hard wall. At some point the accumulated history exceeds the model's limit and requests start failing with a 400.

Bulk processing

Ten thousand support tickets, averaging 500 input tokens each, classified into a 20-token label:

ModelInput costOutput costTotal
gpt-5.6-luna$1.00$0.24$1.24
gpt-5.6-terra$10.00$2.40$12.40
gpt-6-astra$50.00$10.00$60.00

Same work, fifty times the price. For a classification task with a fixed set of labels, the small model is very often as accurate as the large one — which is exactly what Unit 13 teaches you to measure rather than guess. The right sequence is: build an eval, run the cheap model, and only move up if the numbers say you must.

A Cost Tracker You Can Reuse

Printing cost during development turns an abstract number into feedback. This module is small enough to drop into any project:

# cost.py
from dataclasses import dataclass, field

# USD per 1,000,000 tokens. Verify against the current pricing page.
PRICES = {
    "gpt-6-astra":   {"input": 10.00, "cached": 1.00, "output": 50.00},
    "gpt-5.6-sol":   {"input":  4.00, "cached": 0.40, "output": 20.00},
    "gpt-5.6-terra": {"input":  2.00, "cached": 0.20, "output": 12.00},
    "gpt-5.6-luna":  {"input":  0.20, "cached": 0.02, "output":  1.20},
}


def call_cost(response) -> float:
    """Cost in USD of a single Responses API call."""
    model = response.model
    prices = PRICES.get(model)
    if prices is None:
        # Match a family prefix, e.g. "gpt-5.6-luna-2026-08-01"
        prices = next(
            (p for name, p in PRICES.items() if model.startswith(name)), None
        )
    if prices is None:
        raise KeyError(f"No price recorded for model {model!r}")

    u = response.usage
    cached = u.input_tokens_details.cached_tokens
    fresh = u.input_tokens - cached

    return (
        fresh / 1_000_000 * prices["input"]
        + cached / 1_000_000 * prices["cached"]
        + u.output_tokens / 1_000_000 * prices["output"]
    )


@dataclass
class CostTracker:
    """Accumulates cost across many calls in one run."""
    total: float = 0.0
    calls: int = 0
    by_model: dict = field(default_factory=dict)

    def record(self, response) -> float:
        cost = call_cost(response)
        self.total += cost
        self.calls += 1
        self.by_model[response.model] = self.by_model.get(response.model, 0.0) + cost
        return cost

    def report(self) -> str:
        lines = [f"{self.calls} calls, ${self.total:.6f} total"]
        for model, cost in sorted(self.by_model.items()):
            lines.append(f"  {model}: ${cost:.6f}")
        return "\n".join(lines)

Used like this:

from cost import CostTracker

tracker = CostTracker()

for question in questions:
    response = client.responses.create(model="gpt-5.6-luna", input=question)
    tracker.record(response)

print(tracker.report())

Several design choices here are worth copying:

  • It reads response.model, not the model you requested. The served model can be more specific than the alias you asked for — gpt-5.6-luna may resolve to a dated snapshot — and pricing follows the served model.
  • It handles cached tokens separately. Ignoring cached_tokens overstates cost by up to ten times on a cache-heavy workload, which makes your optimisation efforts look like they achieved nothing.
  • It raises on an unknown model rather than returning zero. A silent zero is worse than an error: it makes an expensive model look free, which is precisely the failure you were trying to prevent.
  • It aggregates per model. In a pipeline that uses a cheap model for routing and an expensive one for the hard cases, the per-model breakdown is what tells you whether your routing is actually working.

Print tracker.report() at the end of every script you write in this course. It takes one line and it makes cost a number you watch rather than a number you discover.

Spend Limits Are Your Real Safety Net

Cost awareness in code is useful. Limits enforced by the platform are what actually protect you, because they work even when your code is the thing that is broken.

In your dashboard you can set:

  • A project spend limit — a hard cap. Requests from that project fail once it is reached.
  • An organisation usage limit — the same at the billing-entity level.
  • A notification threshold — an email when spend crosses a level, without blocking anything.

Set a project spend limit before you write another line of code. For this course, $5 is generous. The cost of setting it is thirty seconds. The cost of not setting it is bounded only by your credit balance and your worst bug.

The classic scenario this prevents: a retry loop with no backoff, wrapped around an expensive model, left running overnight. Each iteration is a few cents, the loop runs for eight hours, and nobody is watching. A spend limit turns that from an expensive lesson into a 429 at 2am.

Spend limits and rate limits are different mechanisms and people conflate them:

Spend limitRate limit
MeasuresDollars over a periodRequests and tokens per minute
Set byYouOpenAI, based on your usage tier
PurposeProtect your budgetProtect shared infrastructure
When hitRequests fail until the period resets or you raise itRequests fail until the window rolls forward
Correct responseInvestigate why you spent that muchBack off and retry

Both surface as 429s, which is why the error message matters. Unit 12, Lesson 3 covers rate limits and how tiers raise them.

Where to See What You Spent

The usage page in your dashboard breaks spend down by day, by model, and by project. Three habits make it useful rather than decorative:

  • Check it after your first day of real usage. Compare the number against what your cost tracker predicted. A large discrepancy usually means reasoning tokens, retries, or calls you forgot were in a loop.
  • Use separate projects per application. Usage is reported per project, so this is the only way to attribute spend without instrumenting everything yourself.
  • Look at the model breakdown, not just the total. A total that doubled tells you nothing; a breakdown showing the expensive model taking 90% of a workload it was not supposed to touch tells you exactly what to fix.

Reducing Cost, in Order of Impact

1. Use the cheapest model that passes your evals. Fifty-fold spread. Nothing else on this list comes close. Start with the small model, measure, and move up only when the numbers require it (Unit 13).

2. Structure prompts for caching. Stable content first, variable content last, for a roughly 90% reduction on the cached portion of input.

3. Cap and shape output. Ask for brevity in instructions, set max_output_tokens as a ceiling, and use structured outputs (Unit 6) so the model returns data rather than data wrapped in explanation. Output is the expensive direction.

4. Trim conversation history. Because cost grows quadratically with turns, summarising or windowing history is the difference between a chatbot that is cheap and one that gets steadily more expensive the longer someone talks to it (Unit 4, Lesson 6).

5. Use the Batch API for work that is not time-sensitive. Submitting a large job to run asynchronously trades latency for a substantial discount. Verify the current discount on the pricing page; it has historically been significant enough to change the economics of bulk processing outright (Unit 12, Lesson 5).

6. Do not send what the model does not need. Retrieving five relevant paragraphs beats sending an entire document. This is the practical argument for embeddings and retrieval (Unit 10) — relevance is a cost strategy as much as a quality one.

7. Cache your own results. If ten users ask the same question, answering it once and storing the result costs one request. This is ordinary application caching, and it applies here exactly as it does to any expensive backend call.

Common Mistakes

Assuming ChatGPT Plus includes API credit. It does not. Separate products, separate balances.

Estimating cost from visible output length on reasoning models. Reasoning tokens are billed as output and never appear in output_text. Read output_tokens_details.reasoning_tokens.

Ignoring the quadratic growth of conversations. Estimating a 20-turn chat by counting only new messages understates the input cost by roughly thirty times.

Leaving auto-recharge on with no monthly limit while learning. A runaway loop that would have stopped at an empty balance instead keeps buying credit and keeps running.

Retrying an exhausted credit balance. It presents as a 429, so generic retry logic hammers it. Read the error body — credit_balance_exhausted is not a condition that resolves by waiting.

Putting variable data at the front of a prompt. Timestamps, user IDs and request IDs at position one defeat prompt caching entirely, including for the large stable block behind them.

Testing on the flagship model. Development involves far more calls than production traffic per unit of value. Develop on the cheap model; validate on the expensive one only when you are nearly done.

Forgetting a request is inside a loop. Notebook cells re-run, retry wrappers repeat, and for loops over a list of a thousand items are all easy to write and easy to forget. A spend limit is the backstop; a printed running total is the early warning.

Not setting a spend limit at all. Everything above is judgement. A spend limit is enforcement, and it is the only item on this list that works while you are asleep.

Estimating a Project's Cost Before You Build It

Reading usage tells you what a call cost after the fact. Budgeting requires an estimate beforehand, and the arithmetic is simple enough to do on paper.

For any feature, you need four numbers:

  1. Requests per period — how many calls per day or month.
  2. Input tokens per request — instructions + history + retrieved context + user message.
  3. Output tokens per request — how long the answer typically is.
  4. The cached fraction of input — how much of your input is a stable prefix.

Then:

monthly cost =
    requests
    × [ (input_tokens × (1 - cached_fraction) × input_price
       +  input_tokens × cached_fraction × cached_price
       +  output_tokens × output_price) / 1,000,000 ]

A worked example. A support assistant on gpt-5.6-terra: 2,000 conversations a month, averaging 6 turns each (so 12,000 requests), with a 3,000-token stable system prompt, an average 1,500 tokens of accumulated history and user message, and 250 tokens of reply. The system prompt caches; the history does not.

Cached input:  3,000 × $0.20/M  = $0.0006
Fresh input:   1,500 × $2.00/M  = $0.0030
Output:          250 × $12.00/M = $0.0030
                                  ---------
Per request:                      $0.0066
× 12,000 requests                 $79.20 / month

Now vary one assumption at a time and watch what moves:

ChangeNew monthly cost
Baseline$79.20
Caching not working (prefix unstable)$144.00
Switch to gpt-5.6-luna$7.92
Replies average 600 tokens instead of 250$129.60
History trimmed to 800 tokens$62.40

Three lessons fall straight out of that table. Model choice dominates everything. Broken caching nearly doubles the bill invisibly, because nothing errors — you simply pay more. And output length matters more than you would guess from its token count, because of the price asymmetry.

Do this arithmetic before you build, with rough numbers. It takes five minutes and it routinely changes the design — most often by revealing that the cheap model plus a good eval is the right architecture, rather than the flagship model plus hope.

Measuring your actual token counts. For the input side, you do not have to guess. Send one representative request and read usage.input_tokens. That single measurement replaces all the character-ratio estimating above, and it accounts for whatever your instructions and formatting actually cost. For a stricter local count before sending — useful when you must reject oversized input rather than discover the limit at the API — tokenise locally with a tokeniser library. Unit 4, Lesson 6 covers this alongside context-limit management.

Usage Tiers

Your account sits in a usage tier determined by cumulative spend and account history. Tiers govern two things that matter as you grow:

  • Rate limits — requests per minute and tokens per minute rise as you move up. A new account is limited enough that a modest concurrent workload can hit 429s that a more established account would not.
  • Maximum credit balance — the trust tier caps how much credit you can hold at once.

Progression is automatic: spend accumulates, time passes, and limits rise. There is nothing to apply for and nothing to configure.

The practical implication is one of scheduling rather than budgeting. If you are planning a bulk job — embedding a large corpus, processing a backlog — check your current rate limits first. A new account attempting to fan out a hundred concurrent requests will spend most of its time being rate-limited and retried, which is slow and, because retries can bill partially-generated tokens, not free. Either throttle your concurrency to fit the limit, or use the Batch API, which is designed for exactly this shape of work. Unit 12 covers both approaches.

Data Retention and What It Costs You in a Different Currency

One billing-adjacent decision belongs here because it interacts with the store parameter from Lesson 4.

Storing responses is free — there is no per-byte charge for retention. What it costs is exposure: content sits on OpenAI's servers, retrievable by anyone with your key, until you delete it. For a personal learning project that is irrelevant. For an application handling customer data, medical records, or anything covered by a data-processing agreement, it is the difference between a compliant system and a non-compliant one.

The trade-off is concrete rather than philosophical. With store=True you get retrieval by ID, chaining via previous_response_id, and the ability to inspect a bad output after a user reports it. With store=False you get none of those and must resend history yourself on every turn — which, note, costs more money, because the history is billed as fresh input rather than being reconstructed server-side.

So the decision is: pay slightly more in tokens and hold no data, or pay less and accept retention with a deletion policy you actually implement. Decide it deliberately per application rather than inheriting whatever the default happens to be, and if you choose retention, write the deletion job at the same time — a retention policy nobody implemented is the most common gap between what a privacy page claims and what a system does.

Before Moving to Unit 2

Three things should be true:

  • A spend limit is set on the project you are using for this course.
  • Auto-recharge is off, or capped at a monthly amount you are comfortable losing entirely.
  • Your scripts print token counts and cost, so a prompt that quietly grew is visible on the next run rather than on the next invoice.

With those in place, the rest of the course is a very small line item, and you can experiment freely — which is the point. Every unit from here on adds capability, and almost every capability has a cost profile attached to it: streaming changes latency but not price, structured outputs reduce wasted output tokens, tools add a round trip per call, and retrieval trades a cheap embedding lookup for a much smaller prompt. Knowing how to read usage is what lets you evaluate each of those trade-offs with a number instead of an intuition.

0 Comments

Reviewed before they appear

No comments yet.

OpenAI SDK
Ask about this post
AI Ask about this post

Ask questions about Understanding Billing, Credits, and What a Request Costs and get answers drawn from it.

Signed-in readers only.