{"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-09-09T13:23:31","modified_gmt":"2026-09-09T11:23:31","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 bill for an API arrives once a month and states a total. Between that total and the code that produced it usually sits nothing at all &#8211; no record of which feature spent what, and no way to tell an expensive week from an expensive habit.<\/p>\n<p>The gap is easy to close, because the information is already in every response. The work below is twenty lines of logging, one conversion table, and one check that runs before the request rather than after it.<\/p>\n<figure class=\"lw-diagram\">\n<img src=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/diagrams\/tokenverbrauch-messen-en.png\" width=\"1120\" height=\"580\" decoding=\"async\" loading=\"lazy\"\n     alt=\"A receipt for a single request with four token classes priced separately, beside a monthly budget bar showing spend against a soft threshold and a hard stop\"><figcaption>The same request, four prices. The 96 000 tokens read from cache cost less than the 12 400 sent fresh &#8211; which is why one aggregate token count explains nothing.<\/figcaption><\/figure>\n<h2>Where the True Number Already Is<\/h2>\n<p>Every response from a chat completion carries a usage block. It is not an estimate and not a rounding; it is what the meter recorded.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>{\n  \"usage\": {\n    \"input_tokens\": 12400,\n    \"cache_creation_input_tokens\": 8200,\n    \"cache_read_input_tokens\": 96000,\n    \"output_tokens\": 1850\n  }\n}<\/code><\/pre>\n<p>The four fields are four separate prices, and the split matters more than the sum. Freshly sent input is the base rate. Tokens written into a prompt cache cost a surcharge once. Tokens read back out of that cache cost a fraction of the base rate. Output is the most expensive class of all, usually several times the input rate.<\/p>\n<p>Other providers name the same things differently &#8211; <code>prompt_tokens<\/code>, <code>completion_tokens<\/code> and a nested <code>cached_tokens<\/code> are the common alternative &#8211; but the structure is the same, and so is the consequence: a single number labelled &#8220;tokens&#8221; cannot be converted into money.<\/p>\n<p>Streaming needs one extra note. The usage block does not arrive with the first chunk but at the end, in the closing event of the stream. Code that reads the text and discards the rest therefore loses exactly the part that costs something.<\/p>\n<h2>Counting Before Sending<\/h2>\n<p>Sometimes the number is needed before the request exists &#8211; to decide whether a document still fits, or whether a conversation has to be shortened. For that there is a counting endpoint, which is free and returns what the model would see.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>curl https:\/\/api.anthropic.com\/v1\/messages\/count_tokens \\\n  -H \"x-api-key: $API_KEY\" \\\n  -H \"anthropic-version: 2023-06-01\" \\\n  -H \"content-type: application\/json\" \\\n  -d '{\n    \"model\": \"claude-sonnet-5\",\n    \"messages\": [{\"role\": \"user\", \"content\": \"...\" }]\n  }'<\/code><\/pre>\n<p>A local tokeniser answers the same question without a network call and is close enough for a length check, but it is not the meter. System prompts, tool definitions and image blocks all add tokens that a text tokeniser never sees, and the difference runs to thousands on a request with tools attached. For a budget, only the usage block counts.<\/p>\n<h2>One Row per Request<\/h2>\n<p>The logging itself is unremarkable, and its value lies entirely in the fields that describe the context rather than the request. Model and token counts alone answer nothing; a label saying which feature made the call answers almost everything.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>import json, sqlite3, time, uuid\n\nDB = sqlite3.connect(\"verbrauch.db\")\nDB.execute(\"\"\"CREATE TABLE IF NOT EXISTS aufrufe (\n    id TEXT PRIMARY KEY, zeit INTEGER, modell TEXT, zweck TEXT,\n    ein INTEGER, cache_neu INTEGER, cache_gelesen INTEGER, aus INTEGER,\n    kosten REAL, versuch INTEGER)\"\"\")\n\ndef buchen(antwort, modell, zweck, versuch=1):\n    u = antwort[\"usage\"]\n    zeile = dict(\n        id=str(uuid.uuid4()), zeit=int(time.time()), modell=modell, zweck=zweck,\n        ein=u[\"input_tokens\"],\n        cache_neu=u.get(\"cache_creation_input_tokens\", 0),\n        cache_gelesen=u.get(\"cache_read_input_tokens\", 0),\n        aus=u[\"output_tokens\"], versuch=versuch)\n    zeile[\"kosten\"] = kosten(modell, zeile)\n    DB.execute(\"INSERT INTO aufrufe VALUES (:id,:zeit,:modell,:zweck,:ein,\"\n               \":cache_neu,:cache_gelesen,:aus,:kosten,:versuch)\", zeile)\n    DB.commit()\n    return zeile[\"kosten\"]<\/code><\/pre>\n<p>The <code>versuch<\/code> column is the one that pays for itself unexpectedly. A retry after a timeout sends the whole input again and is billed again, while the application sees one logical call. Without that column, retries hide inside the average and a bad afternoon looks like an expensive feature.<\/p>\n<h2>Turning Tokens into Money<\/h2>\n<p>Prices belong in one place, per model and per token class, expressed per million tokens because that is how they are published.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code># Example rates in currency per million tokens - the real ones\n# come from the provider's price list and change over time.\nPREISE = {\n    \"claude-sonnet-5\": {\"ein\": 3.00, \"cache_neu\": 3.75, \"cache_gelesen\": 0.30, \"aus\": 15.00},\n}\n\ndef kosten(modell, z):\n    p = PREISE[modell]\n    return round(sum(z[k] * p[k] for k in (\"ein\", \"cache_neu\", \"cache_gelesen\", \"aus\")) \/ 1e6, 6)<\/code><\/pre>\n<p>Applied to the request above, the four classes come out at 0.0372, 0.0308, 0.0288 and 0.0278 &#8211; together 0.1245 per call. The interesting comparison is the third figure against the first: 96 000 tokens read from cache cost less than 12 400 sent fresh. A cache that is being hit does not merely reduce the bill, it changes which part of the request dominates it.<\/p>\n<p>With a few days of rows in the table, the useful question becomes answerable in one statement.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>SELECT zweck,\n       COUNT(*)                          AS aufrufe,\n       ROUND(SUM(kosten), 2)             AS gesamt,\n       ROUND(AVG(kosten), 4)             AS je_aufruf,\n       ROUND(100.0 * SUM(cache_gelesen) \/\n             NULLIF(SUM(ein + cache_neu + cache_gelesen), 0), 1) AS cache_anteil\nFROM aufrufe\nWHERE zeit &gt;= strftime('%s', 'now', 'start of month')\nGROUP BY zweck ORDER BY gesamt DESC;<\/code><\/pre>\n<h2>The Guard That Actually Stops Something<\/h2>\n<p>A budget that only warns is a report. Stopping requires the check to sit in front of the call, and it requires a number that is current rather than nightly.<\/p>\n<pre class=\"wp-block-kevinbatdorf-code-block-pro\"><code>class BudgetErschoepft(Exception):\n    pass\n\nMONATSBUDGET = 400.0\nWARNSCHWELLE = 0.80\n\ndef monatssumme():\n    (s,) = DB.execute(\n        \"SELECT COALESCE(SUM(kosten), 0) FROM aufrufe \"\n        \"WHERE zeit &gt;= strftime('%s', 'now', 'start of month')\").fetchone()\n    return s\n\ndef pruefen(geschaetzte_kosten):\n    verbraucht = monatssumme()\n    if verbraucht + geschaetzte_kosten &gt; MONATSBUDGET:\n        raise BudgetErschoepft(f\"{verbraucht:.2f} von {MONATSBUDGET:.2f} verbraucht\")\n    if verbraucht &gt; MONATSBUDGET * WARNSCHWELLE:\n        logging.warning(\"Budget zu %.0f %% verbraucht\", 100 * verbraucht \/ MONATSBUDGET)<\/code><\/pre>\n<p>Three decisions turn this from a formality into something that holds. The estimate passed in should be the pessimistic one &#8211; maximum output tokens at the output rate &#8211; because a call is either allowed or not, and being allowed on an optimistic estimate is how a budget gets exceeded by exactly one request.<\/p>\n<p>The second is what happens when the exception is raised. A batch job should stop and say where it stopped. An interactive feature should degrade rather than fail: a smaller model, a shorter context, or a queue that resumes next month. Both are better than a stack trace reaching a user.<\/p>\n<p>The third is that a shared budget needs a shared counter. Several processes each holding their own total will each stay under the limit and jointly exceed it. A single table with a transaction around read and write, or a counter in a shared store, is the whole fix.<\/p>\n<h2>What the Guard Cannot See<\/h2>\n<p>Three kinds of spend never pass the check, and each has its own way of being caught.<\/p>\n<p>Batch processing is billed at a discount and often runs asynchronously, so the usage block arrives hours later with the results. Its cost belongs in the same table, entered when the results are fetched, with the submission time rather than the fetch time &#8211; otherwise a month boundary lands the spending in the wrong month.<\/p>\n<p>Failed requests are the second. A call that runs into a timeout after the model has already generated most of its answer is still billed, and it produces no usage block to read. What it does produce is a request identifier in the response headers, and the provider&#8217;s own usage report will carry it. A monthly reconciliation between the local table and that report takes ten minutes and finds exactly this class of difference.<\/p>\n<p>And the third is everything that is not a chat completion: embeddings, transcriptions, image generation, a search tool called on the server side. Each has its own meter and its own price, and none of them appears in the usage block being logged here. The reconciliation catches those too, which is the practical reason to do it at all.<\/p>\n<div class=\"lw-quellen\">\n<h2>Sources<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.rfc-editor.org\/rfc\/rfc9110.html\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9110: HTTP Semantics<\/a><\/li>\n<\/ul>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Every API response states exactly how many tokens it cost, in four separate classes. Logging that number instead of estimating it is twenty lines &#8211; and it is the only basis on which a budget can be more than a warning.<\/p>\n","protected":false},"author":1,"featured_media":13222,"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 - Blog<\/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 - Blog\" \/>\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=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13020-tutorial-measuring-token-consumption-per.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=\"luky\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"luky\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 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\":\"luky\",\"@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\",\"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\":993,\"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\\\/08\\\/hero-13020-tutorial-measuring-token-consumption-per.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 - Blog\",\"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\\\/08\\\/hero-13020-tutorial-measuring-token-consumption-per.png\",\"datePublished\":\"2026-09-13T05:35:00+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\\\/08\\\/hero-13020-tutorial-measuring-token-consumption-per.png\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/hero-13020-tutorial-measuring-token-consumption-per.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\":\"luky\",\"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\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"],\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/author\\\/luky\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Tutorial: Measuring Token Consumption per Request and Enforcing a Monthly Budget - Lukas Wojcik - Blog","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 - Blog","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","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13020-tutorial-measuring-token-consumption-per.png","type":"image\/png"}],"author":"luky","twitter_card":"summary_large_image","twitter_misc":{"Written by":"luky","Est. reading time":"7 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":"luky","@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","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":993,"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\/08\/hero-13020-tutorial-measuring-token-consumption-per.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 - Blog","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\/08\/hero-13020-tutorial-measuring-token-consumption-per.png","datePublished":"2026-09-13T05:35:00+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\/08\/hero-13020-tutorial-measuring-token-consumption-per.png","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/hero-13020-tutorial-measuring-token-consumption-per.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":"luky","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":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"],"url":"https:\/\/www.lukaswojcik.com\/blog\/author\/luky\/"}]}},"_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":1,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13020\/revisions"}],"predecessor-version":[{"id":17293,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/posts\/13020\/revisions\/17293"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media\/13222"}],"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}]}}