{"id":13020,"date":"2026-09-13T07:35:00","date_gmt":"2026-09-13T05:35:00","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/?p=13020"},"modified":"2026-10-26T12:44:08","modified_gmt":"2026-10-26T11:44:08","slug":"tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget","status":"publish","type":"post","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/","title":{"rendered":"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget"},"content":{"rendered":"<p>A useful API budget connects three records: a reservation before a request, measured usage after the response and a later reconciliation with provider charges. A token total alone is insufficient. Provider, model, service tier, cache class and additional tools can change the price.<\/p>\n<h2>1. The provider\u2019s usage schema<\/h2>\n<p>In Anthropic Messages, <code>input_tokens<\/code> describes uncached input, while cache creation and cache reads are separate input categories. Cache creation can be split by lifetime. In OpenAI Responses, <code>input_tokens<\/code> is the input total; cached tokens and supported cache-write categories are included within it. Adding them to that total would count input twice. These are different accounting structures, not merely different names.<\/p>\n<p>The following invented normalized record uses four non-overlapping classes. The rates are arithmetic examples in USD per million tokens, not a current quotation for a named model. A real adapter must handle the selected endpoint and all applicable cache lifetimes.<\/p>\n<pre><code>from decimal import Decimal, ROUND_CEILING\n\n# Fictional normalized classes and example USD rates per million.\ncounts = {&quot;input&quot;: 12400, &quot;write_5m&quot;: 8200, &quot;read&quot;: 96000, &quot;output&quot;: 1850}\nrates = {k: Decimal(v) for k, v in {\n    &quot;input&quot;: &quot;3.00&quot;, &quot;write_5m&quot;: &quot;3.75&quot;, &quot;read&quot;: &quot;0.30&quot;, &quot;output&quot;: &quot;15.00&quot;\n}.items()}\ncost_usd = sum(Decimal(counts[k]) * rates[k] for k in counts) \/ Decimal(1_000_000)\nactual_u = int((cost_usd * 1_000_000).to_integral_value(rounding=ROUND_CEILING))\nprint(cost_usd, actual_u)  # 0.1245, 124500\n<\/code><\/pre>\n<p>The exact components are USD 0.0372, 0.03075, 0.0288 and 0.02775, totalling USD 0.1245. Rounding every displayed component first can change the apparent sum. Decimal arithmetic retains the calculation; the reservation ledger below uses integer micro-USD, where USD 1 equals 1,000,000 units. Rounding upward to that unit is a local budgeting policy, not the provider\u2019s invoice rule.<\/p>\n<figure class=\"lw-diagram\"><img decoding=\"async\" src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/dia-13020-token-budget-20261024-en.png\" width=\"1120\" height=\"580\" loading=\"lazy\" alt=\"Four stages of API budget control\"><figcaption>Four stages of API budget control<\/figcaption><\/figure>\n<h2>2. Counting before sending and recording after completion<\/h2>\n<p>Anthropic\u2019s counting endpoint is free within its rate limits and provides an estimate without applying caching logic. It does not accept every input supported by Messages: the current documentation excludes most server tools, the MCP connector and URL\/file-sourced image or document blocks. The model and complete supported request structure matter. A plain-text tokenizer is not a universal estimator for tools, images or message framing.<\/p>\n<p>Streaming usage does not universally appear only in the last event. Anthropic supplies initial usage in <code>message_start<\/code> and cumulative updates in <code>message_delta<\/code>; adding every cumulative value would overcount. For example, successive totals of 10 and 20 mean 20 tokens so far, not 30. A supported SDK\u2019s completed-message helper simplifies collection. Interrupted streams and failed connections can leave incomplete usage. Missing data must remain unknown, not become zero.<\/p>\n<p>A durable audit record should link logical operation, unique attempt ID, provider request\/response ID when available, timestamps, feature, provider\/model, pricing snapshot, reservation and raw usage. Prompts and API keys are unnecessary for this ledger. A retry is a distinct attempt whose possible charge needs its own reservation. A timeout neither proves a charge nor proves that processing stopped; a response header may never have reached the client.<\/p>\n<h2>3. A single transaction for the full reservation<\/h2>\n<p>The upper bound must cover input, the maximum applicable output, possible cache writes and bounded tool or loop costs. Maximum output alone is not enough. Unknown pricing, unbounded tool loops or a failed ledger write must prevent dispatch. Cache hits should not be assumed when computing the bound.<\/p>\n<p>This executable <code>ledger.py<\/code> demonstrates admission control for one application, one currency and one UTC calendar-month policy. All callers must use the same local SQLite database. It is not a multi-host database or a complete billing integration. The amount validator caps each value at one million USD in micro-units.<\/p>\n<p>Before the first reservation, <code>initialise(database_path)<\/code> creates the tables. Output usage can include non-visible reasoning or formatting tokens; visible answer length is not the output meter.<\/p>\n<pre><code>import sqlite3\nfrom datetime import datetime, timezone\n\nclass BudgetExceeded(Exception):\n    pass\n\ndef units(value):\n    if type(value) is not int or not 0 &lt;= value &lt;= 10**12:\n        raise ValueError(&quot;Expected non-negative integer micro-USD, at most 10^12&quot;)\n    return value\n\ndef connect(path):\n    db = sqlite3.connect(path, timeout=10, isolation_level=None)\n    db.execute(&quot;PRAGMA foreign_keys=ON&quot;)\n    return db\n\ndef initialise(path):\n    db = connect(path)\n    try:\n        db.executescript(&quot;&quot;&quot;\n        CREATE TABLE IF NOT EXISTS budgets(\n          month TEXT PRIMARY KEY, limit_u INTEGER NOT NULL,\n          breached INTEGER NOT NULL DEFAULT 0);\n        CREATE TABLE IF NOT EXISTS calls(\n          attempt_id TEXT PRIMARY KEY,\n          month TEXT NOT NULL REFERENCES budgets(month),\n          reserved_u INTEGER NOT NULL, actual_u INTEGER,\n          state TEXT NOT NULL CHECK(state IN (&#x27;pending&#x27;,&#x27;settled&#x27;)));\n        &quot;&quot;&quot;)\n    finally:\n        db.close()\n\ndef reserve(path, attempt_id, upper_u, limit_u, now=None):\n    upper_u, limit_u = units(upper_u), units(limit_u)\n    if not isinstance(attempt_id, str) or not attempt_id:\n        raise ValueError(&quot;Stable, nonempty attempt ID required&quot;)\n    now = datetime.now(timezone.utc) if now is None else now\n    if now.tzinfo is None or now.utcoffset() is None:\n        raise ValueError(&quot;Timezone-aware timestamp required&quot;)\n    month = now.astimezone(timezone.utc).strftime(&quot;%Y-%m&quot;)\n    db = connect(path)\n    try:\n        db.execute(&quot;BEGIN IMMEDIATE&quot;)\n        if db.execute(&quot;SELECT 1 FROM calls WHERE attempt_id=?&quot;,\n                      (attempt_id,)).fetchone():\n            db.commit()\n            return False  # No second permission to send.\n        db.execute(&quot;INSERT OR IGNORE INTO budgets(month,limit_u) VALUES (?,?)&quot;,\n                   (month, limit_u))\n        stored_limit, breached = db.execute(\n            &quot;SELECT limit_u,breached FROM budgets WHERE month=?&quot;, (month,)\n        ).fetchone()\n        if stored_limit != limit_u:\n            raise ValueError(&quot;Conflicting limit for this month&quot;)\n        used = db.execute(\n            &quot;SELECT COALESCE(SUM(COALESCE(actual_u,reserved_u)),0) &quot;\n            &quot;FROM calls WHERE month=?&quot;, (month,)\n        ).fetchone()[0]\n        if breached or used + upper_u &gt; limit_u:\n            raise BudgetExceeded(month)\n        db.execute(&quot;INSERT INTO calls VALUES (?,?,?,?,?)&quot;,\n                   (attempt_id, month, upper_u, None, &quot;pending&quot;))\n        db.commit()\n        return True\n    except BaseException:\n        db.rollback()\n        raise\n    finally:\n        db.close()\n\ndef settle(path, attempt_id, actual_u):\n    actual_u = units(actual_u)\n    db = connect(path)\n    try:\n        db.execute(&quot;BEGIN IMMEDIATE&quot;)\n        row = db.execute(\n            &quot;SELECT month,reserved_u,actual_u,state FROM calls WHERE attempt_id=?&quot;,\n            (attempt_id,)\n        ).fetchone()\n        if row is None:\n            raise ValueError(&quot;Unknown attempt&quot;)\n        month, reserved, old_actual, state = row\n        if state == &quot;settled&quot;:\n            if old_actual != actual_u:\n                raise ValueError(&quot;Conflicting settlement&quot;)\n            db.commit()\n            return\n        db.execute(&quot;UPDATE calls SET actual_u=?,state=&#x27;settled&#x27; WHERE attempt_id=?&quot;,\n                   (actual_u, attempt_id))\n        if actual_u &gt; reserved:\n            db.execute(&quot;UPDATE budgets SET breached=1 WHERE month=?&quot;, (month,))\n        db.commit()\n    except BaseException:\n        db.rollback()\n        raise\n    finally:\n        db.close()\n<\/code><\/pre>\n<p><code>BEGIN IMMEDIATE<\/code> acquires the write transaction before reading the total. This prevents two concurrent callers from reserving the same remaining budget. The reservation is committed before the network request; the transaction is not held while waiting for the API. Only a true return from <code>reserve()<\/code> permits dispatch. False means that the attempt already exists, not permission to send it again. A lock or database error must stop the call.<\/p>\n<pre><code>PSEUDOCODE\nvalidate provider, model, pricing snapshot and complete cost bound\npersist a unique attempt ID and audit context\nif reserve(database, attempt_id, upper_bound_u, monthly_limit_u) is True:\n    send exactly one provider attempt outside the database transaction\n    on complete usage:\n        persist the original usage and provider identifiers\n        calculate cost with the recorded pricing snapshot\n        settle(database, attempt_id, actual_cost_u)\n    on unknown outcome or unknown cost:\n        keep the reservation pending and investigate\n<\/code><\/pre>\n<p>The stored limit is fixed for that month. A later breach flag does not cancel requests already admitted.<\/p>\n<p>The flow is pseudocode for the provider adapter: a reviewed pricing snapshot and a complete bound are prerequisites. The actual code above performs no API call. It records a settlement once, accepts an identical repeated settlement and rejects conflicting amounts. If the actual cost exceeds the reservation, it records that fact and blocks further admissions for that month. It cannot undo spend already incurred.<\/p>\n<h2>4. Visible records of uncertain attempts<\/h2>\n<p>A crash after reservation but before dispatch can leave unused funds reserved. A timeout after dispatch can leave real spend unresolved. Automatically expiring both cases would reopen the budget without evidence. Recovery needs the audit record and provider information; only confirmed no-charge attempts should settle at zero.<\/p>\n<p>The month is assigned when the application reserves the attempt in UTC. Settlement remains attached to that month, even if it arrives later. This is an internal policy, not a statement about invoice timing. Submission, execution, settlement and provider billing periods should remain separate for asynchronous batches and month-boundary reconciliation.<\/p>\n<div class=\"lw-faq\">\n<h2>Questions and answers<\/h2>\n<h3>Does this guarantee the provider invoice stays below the budget?<\/h3>\n<p>No. The bound, pricing adapter and routing of every charged operation must be correct. Other API keys, omitted tool charges, contract terms, taxes or later adjustments can sit outside the local ledger. Provider-side limits can add protection where available, but their documented scope matters.<\/p>\n<h3>Can reconciliation always match each request?<\/h3>\n<p>No. Anthropic\u2019s Usage and Cost APIs provide aggregated time buckets and grouping dimensions; a request-by-request export must not be assumed. Cost reporting also has documented coverage limits. Matching periods, currency and scope is necessary before interpreting a difference.<\/p>\n<h3>What did the example tests establish?<\/h3>\n<p>Eight parallel reservation attempts admitted only two amounts of 40 under a limit of 100. Further isolated tests covered duplicate settlements, uncertain pending attempts, conflicting limits, invalid amounts and UTC month boundaries. No paid API request or real invoice was tested.<\/p>\n<\/div>\n<h2>Sources<\/h2>\n<ul>\n<li><a href=\"https:\/\/platform.claude.com\/docs\/en\/build-with-claude\/token-counting\">Anthropic: token counting<\/a><\/li>\n<li><a href=\"https:\/\/platform.claude.com\/docs\/en\/build-with-claude\/prompt-caching\">Anthropic: prompt caching<\/a><\/li>\n<li><a href=\"https:\/\/platform.claude.com\/docs\/en\/build-with-claude\/streaming\">Anthropic: streaming messages<\/a><\/li>\n<li><a href=\"https:\/\/platform.claude.com\/docs\/en\/api\/usage-cost-api\">Anthropic: Usage and Cost API<\/a><\/li>\n<li><a href=\"https:\/\/developers.openai.com\/api\/docs\/guides\/token-counting\">OpenAI: counting tokens<\/a><\/li>\n<li><a href=\"https:\/\/developers.openai.com\/api\/docs\/guides\/prompt-caching\">OpenAI: prompt caching<\/a><\/li>\n<li><a href=\"https:\/\/developers.openai.com\/api\/reference\/resources\/responses\/methods\/retrieve\">OpenAI: Responses usage<\/a><\/li>\n<li><a href=\"https:\/\/sqlite.org\/lang_transaction.html\">SQLite: transactions<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>API usage schemas differ, and interrupted requests can leave costs unknown. The tutorial combines precise example costs with atomic SQLite reservations, duplicate protection and reconciliation boundaries for a monthly application budget.<\/p>\n","protected":false},"author":1,"featured_media":24196,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[92636],"tags":[91380,91445,91404,91219],"class_list":["post-13020","post","type-post","status-publish","format-standard","hentry","category-tutorials-en-cloud-ai","tag-artificial-intelligence","tag-budgeting","tag-llm","tag-tutorial"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget | Lukas Wojcik<\/title>\n<meta name=\"description\" content=\"Reading the usage block, logging one row per request, converting tokens into money and stopping a run before a monthly budget is exceeded.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget | Lukas Wojcik\" \/>\n<meta property=\"og:description\" content=\"Reading the usage block, logging one row per request, converting tokens into money and stopping a run before a monthly budget is exceeded.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-13T05:35:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-10-26T11:44:08+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-13020-token-budget-20261024-dark.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Lukas Wojcik\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Lukas Wojcik\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/\"},\"author\":{\"name\":\"Lukas Wojcik\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"headline\":\"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget\",\"datePublished\":\"2026-09-13T05:35:00+00:00\",\"dateModified\":\"2026-10-26T11:44:08+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/\"},\"wordCount\":966,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-13020-token-budget-20261024-dark.png\",\"keywords\":[\"Artificial Intelligence\",\"Budgeting\",\"LLM\",\"Tutorial\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/\",\"name\":\"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget | Lukas Wojcik\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-13020-token-budget-20261024-dark.png\",\"datePublished\":\"2026-09-13T05:35:00+00:00\",\"dateModified\":\"2026-10-26T11:44:08+00:00\",\"description\":\"Reading the usage block, logging one row per request, converting tokens into money and stopping a run before a monthly budget is exceeded.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-13020-token-budget-20261024-dark.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/10\\\/hero-13020-token-budget-20261024-dark.png\",\"width\":1200,\"height\":630,\"caption\":\"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/cloud-ai\\\/tutorials-en-cloud-ai\\\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"Lukas Wojcik\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"Lukas Wojcik\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget | Lukas Wojcik","description":"Reading the usage block, logging one row per request, converting tokens into money and stopping a run before a monthly budget is exceeded.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/","og_locale":"en_US","og_type":"article","og_title":"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget | Lukas Wojcik","og_description":"Reading the usage block, logging one row per request, converting tokens into money and stopping a run before a monthly budget is exceeded.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/","og_site_name":"Lukas Wojcik - Blog","article_published_time":"2026-09-13T05:35:00+00:00","article_modified_time":"2026-10-26T11:44:08+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-13020-token-budget-20261024-dark.png","type":"image\/png"}],"author":"Lukas Wojcik","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Lukas Wojcik","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/#article","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/"},"author":{"name":"Lukas Wojcik","@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"headline":"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget","datePublished":"2026-09-13T05:35:00+00:00","dateModified":"2026-10-26T11:44:08+00:00","mainEntityOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/"},"wordCount":966,"commentCount":0,"publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-13020-token-budget-20261024-dark.png","keywords":["Artificial Intelligence","Budgeting","LLM","Tutorial"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/","name":"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget | Lukas Wojcik","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/#primaryimage"},"image":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/#primaryimage"},"thumbnailUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-13020-token-budget-20261024-dark.png","datePublished":"2026-09-13T05:35:00+00:00","dateModified":"2026-10-26T11:44:08+00:00","description":"Reading the usage block, logging one row per request, converting tokens into money and stopping a run before a monthly budget is exceeded.","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/#primaryimage","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-13020-token-budget-20261024-dark.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/10\/hero-13020-token-budget-20261024-dark.png","width":1200,"height":630,"caption":"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget"},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/cloud-ai\/tutorials-en-cloud-ai\/tutorial-measuring-token-consumption-per-request-and-enforcing-a-monthly-budget\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"Lukas Wojcik","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"Lukas Wojcik"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13020","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=13020"}],"version-history":[{"count":5,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13020\/revisions"}],"predecessor-version":[{"id":27008,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13020\/revisions\/27008"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/24196"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=13020"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/categories?post=13020"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=13020"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}