This LLM cost function looks right. Can you spot the bug? A developer demonstrated a common cost-tracking bug in OpenAI's Responses API: because output_tokens already includes reasoning_tokens, adding the reasoning field to the output count double-bills reasoning, inflating estimates by 58% in a sample calculation ($38.00 versus $24.00 per 10,000 requests). The writeup notes that Google's Gemini API splits usage differently, with thoughtsTokenCount outside candidatesTokenCount, so no single formula works across providers and each API's token definitions must be read before writing cost code. OpenAI's reasoning models bill you for thinking you never see. So it seems careful to add that thinking to your cost tracking, like this: python from decimal import Decimal Made-up prices, in USD per million tokens. INPUT RATE = Decimal "0.50" OUTPUT RATE = Decimal "2.00" def estimate cost usage : """Estimated USD cost of one OpenAI Responses API call.""" reasoning = usage "output tokens details" "reasoning tokens" billed output = usage "output tokens" + reasoning return usage "input tokens" INPUT RATE + billed output OUTPUT RATE / 1 000 000 It runs, and it returns a believable number. It's also wrong for every response that used reasoning. Have a guess at why before you read on. OpenAI's Responses API reports everything the model generates as output tokens . Its token counting guide https://developers.openai.com/api/docs/guides/token-counting says: "Reported output token usage includes all tokens generated by the model, not only the text visible in a response." The reasoning tokens value under output tokens details breaks that number down: it tells you how many of the output tokens were reasoning. So estimate cost counts the reasoning twice. You can see it in the example response in OpenAI's reasoning guide https://developers.openai.com/api/docs/guides/reasoning : { "input tokens": 75, "input tokens details": { "cached tokens": 0 }, "output tokens": 1186, "output tokens details": { "reasoning tokens": 1024 }, "total tokens": 1261 } The total is 75 + 1,186 = 1,261, so the 1,024 reasoning tokens are already inside the 1,186. Add them again and your log says 2,285 tokens for a response that reported 1,261. The same guide confirms the billing: "While reasoning tokens are not visible via the API, they still occupy space in the model's context window and are billed as output tokens." Use output tokens as it is. Here's the fixed function, with a made-up response to run both versions on: python def estimate cost fixed usage : """Same estimate; output tokens already includes the reasoning tokens.""" billed output = usage "output tokens" return usage "input tokens" INPUT RATE + billed output OUTPUT RATE / 1 000 000 A made-up response in the shape of OpenAI's Responses API usage object. usage = { "input tokens": 1200, "output tokens": 900, "output tokens details": {"reasoning tokens": 700}, } for label, cost in "Counted once: ", estimate cost fixed usage , "Counted twice:", estimate cost usage : print f"{label} ${cost:.4f} per request, ${cost 10 000:.2f} per 10,000" Put both blocks in one file and run it: Counted once: $0.0024 per request, $24.00 per 10,000 Counted twice: $0.0038 per request, $38.00 per 10,000 On this made-up response, the buggy version is 58% too high. Both numbers look perfectly plausible, which is why the mistake is easy to miss. Google's Gemini API splits it differently. In the generateContent reference https://aistudio.google.com/api-reference/generate-content , thoughtsTokenCount is the "Number of tokens of thoughts for thinking models", and totalTokenCount is "prompt + tool-use prompt + thoughts + response candidates". The thinking tokens sit outside candidatesTokenCount , so if you treat candidatesTokenCount as all of the output, they go missing. No single formula covers both APIs. Adding the reasoning field counts tokens twice on OpenAI, and leaving it out drops tokens on Gemini. Read each provider's definitions before you write the formula. Some providers report cached tokens inside the input count. Gemini's promptTokenCount , for example, "includes the number of tokens in the cached content", so adding the cached count on top counts those tokens twice. Did you spot the bug before the answer? And if you call more than one provider, how do you keep their usage fields straight? Tell me in the comments.