AutomationFlowsAI & RAG › Track AI Agent Token Costs and Store Receipts with the N8n API and Data Tables

Track AI Agent Token Costs and Store Receipts with the N8n API and Data Tables

ByBen van Steenbergen @benvsteenbergen on n8n.io

Ever wondered how much your AI Agent is costing you per run?

Cron / scheduled trigger★★★★☆ complexity13 nodesn8nData Table
AI & RAG Trigger: Cron / scheduled Nodes: 13 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #15177 — we link there as the canonical source.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "3smw3ch1i7q3sJ2A",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "AI Agent Cost Calculator",
  "tags": [],
  "nodes": [
    {
      "id": "e3bc701e-9a6e-4fb9-849a-c35da44dcf28",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3520,
        1968
      ],
      "parameters": {
        "color": 4,
        "width": 450,
        "height": 716,
        "content": "## AI Agent Cost Calculator\n\nScans your AI agent workflows every hour, extracts token usage per model, calculates the estimated USD cost, and stores a receipt in an n8n Data Table.\n\nSupports **Claude, OpenAI, Gemini, Perplexity** and any future model.\n\n## How to setup\n\n**Step 1**: Create a Data Table called `execution_receipts` with columns:\n`workflowid` (text) \u00b7 `executionid` (text) \u00b7 `receipt` (text) \u00b7 `created_at` (text) \u00b7 `units` (number)\n\n**Step 2**: Configure the 3 marked nodes:\n\n- **1.1** \u2192 Set your n8n API credential\n- **1.2** \u2192 Set your n8n API credential\n- **1.3** \u2192 Select your Data Table\n\n\n**Step 3**: Tag your AI agent workflows with `agent`\n\n\n**Step 4**: Activate the workflow\n\nTo add pricing for a new model, add one line to `MODEL_RATES` in the code node.\n\nSelf-hosted? Set `N8N_ENABLED_MODULES=data-table`."
      },
      "typeVersion": 1
    },
    {
      "id": "0662b756-f02e-4a25-96c3-26830089e3b6",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4048,
        2240
      ],
      "parameters": {
        "color": 2,
        "width": 630,
        "height": 240,
        "content": "## Collect Workflows\nFetches all workflows tagged `agent` and loops through them one by one."
      },
      "typeVersion": 1
    },
    {
      "id": "909f1174-32f5-4935-b683-e63ed07393f5",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4512,
        2656
      ],
      "parameters": {
        "color": 6,
        "width": 528,
        "height": 196,
        "content": "## Extract Token Usage\nPulls all executions per workflow, parses AI model token data, and calculates USD cost using the `MODEL_RATES` pricing table."
      },
      "typeVersion": 1
    },
    {
      "id": "ff2f390d-9c9a-434a-a09e-26b4fac7d00c",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4688,
        2112
      ],
      "parameters": {
        "color": 3,
        "width": 820,
        "height": 218,
        "content": "## Store Receipts\nFilters out empty results and saves each receipt to the `execution_receipts` Data Table."
      },
      "typeVersion": 1
    },
    {
      "id": "7aef630f-a314-4baa-a833-d7d191f68587",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        5056,
        2656
      ],
      "parameters": {
        "color": 5,
        "width": 428,
        "height": 198,
        "content": "\ud83e\uddfe **itonomy.nl** \u00b7 Built by Ben van Steenbergen\n\n*This workflow costs less to run than the agents it monitors.*\n\nLike it? Let me know! [Follow me on LinkedIn](https://www.linkedin.com/in/ben-van-steenbergen-9006b37/)"
      },
      "typeVersion": 1
    },
    {
      "id": "83a2f90e-0fb0-4b40-b730-22f982ec7d2f",
      "name": "Hourly Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        4080,
        2368
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtMinute": 10
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "15036d2b-7209-422c-8d6a-a972f102567a",
      "name": "1.1 Get AI Agent Workflows",
      "type": "n8n-nodes-base.n8n",
      "position": [
        4256,
        2368
      ],
      "parameters": {
        "filters": {
          "tags": "agent",
          "activeWorkflows": false,
          "excludePinnedData": true
        },
        "requestOptions": {}
      },
      "typeVersion": 1
    },
    {
      "id": "9c99328d-3866-436b-b351-423a9070626c",
      "name": "Loop Workflows",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        4464,
        2368
      ],
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "0e9749fc-c8f3-479b-99d7-e3d0ca8ed395",
      "name": "1.2 Get Workflow Executions",
      "type": "n8n-nodes-base.n8n",
      "onError": "continueErrorOutput",
      "position": [
        4704,
        2480
      ],
      "parameters": {
        "filters": {
          "workflowId": {
            "__rl": true,
            "mode": "id",
            "value": "={{ $json.id }}"
          }
        },
        "options": {
          "activeWorkflows": true
        },
        "resource": "execution",
        "returnAll": true,
        "requestOptions": {}
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "46b55b0e-fbc7-4870-92f4-36f5fad06cac",
      "name": "Extract Token Usage & Cost",
      "type": "n8n-nodes-base.code",
      "position": [
        4912,
        2480
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// AI Agent Cost Calculator \u2014 Token Usage & Cost Extractor\n// Supports: Anthropic Claude, OpenAI, Google Gemini, Perplexity\n// To add a new model, add an entry to MODEL_RATES below.\n\nif (Object.keys($json).length === 0) return {};\n\nconst usage = $json.data?.resultData?.runData;\nconst workflowId = $json.workflowId;\nconst runId = $json.id;\n\nif (!usage) return {};\n\n// \u2500\u2500 Model rates (USD per 1M tokens) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Add new models here as providers release them.\n// Format: \"model-id\": { input: $/1M, output: $/1M }\nconst MODEL_RATES = {\n  // \u2500\u2500 Anthropic Claude \u2500\u2500\n  \"claude-opus-4-7\":            { input: 5.00,  output: 25.00 },\n  \"claude-opus-4-6\":            { input: 5.00,  output: 25.00 },\n  \"claude-sonnet-4-6\":          { input: 3.00,  output: 15.00 },\n  \"claude-sonnet-4-5-20250929\": { input: 3.00,  output: 15.00 },\n  \"claude-sonnet-4-5\":          { input: 3.00,  output: 15.00 },\n  \"claude-haiku-4-5-20251001\":  { input: 1.00,  output: 5.00  },\n  \"claude-haiku-4-5\":           { input: 1.00,  output: 5.00  },\n  \"claude-opus-4-5-20251101\":   { input: 5.00,  output: 25.00 },\n  \"claude-opus-4-5\":            { input: 5.00,  output: 25.00 },\n  \"claude-opus-4-1-20250805\":   { input: 15.00, output: 75.00 },\n  \"claude-opus-4-1\":            { input: 15.00, output: 75.00 },\n  \"claude-opus-4-20250514\":     { input: 15.00, output: 75.00 },\n  \"claude-opus-4-0\":            { input: 15.00, output: 75.00 },\n  \"claude-sonnet-4-20250514\":   { input: 3.00,  output: 15.00 },\n  \"claude-sonnet-4-0\":          { input: 3.00,  output: 15.00 },\n  \"claude-3-7-sonnet-20250219\": { input: 3.00,  output: 15.00 },\n  \"claude-3-5-sonnet-20241022\": { input: 3.00,  output: 15.00 },\n  \"claude-3-5-sonnet-20240620\": { input: 3.00,  output: 15.00 },\n  \"claude-3-5-haiku-20241022\":  { input: 0.80,  output: 4.00  },\n  \"claude-3-opus-20240229\":     { input: 15.00, output: 75.00 },\n  \"claude-3-sonnet-20240229\":   { input: 3.00,  output: 15.00 },\n  \"claude-3-haiku-20240307\":    { input: 0.25,  output: 1.25  },\n\n  // \u2500\u2500 OpenAI \u2500\u2500\n  \"gpt-4.1\":       { input: 2.00,  output: 8.00  },\n  \"gpt-4.1-mini\":  { input: 0.40,  output: 1.60  },\n  \"gpt-4.1-nano\":  { input: 0.10,  output: 0.40  },\n  \"gpt-4o\":        { input: 2.50,  output: 10.00 },\n  \"gpt-4o-mini\":   { input: 0.15,  output: 0.60  },\n  \"gpt-4-turbo\":   { input: 5.00,  output: 15.00 },\n  \"gpt-4\":         { input: 30.00, output: 60.00 },\n  \"gpt-3.5-turbo\": { input: 0.50,  output: 1.50  },\n  \"o4-mini\":       { input: 1.10,  output: 4.40  },\n  \"o3\":            { input: 2.00,  output: 8.00  },\n  \"o3-mini\":       { input: 1.10,  output: 4.40  },\n  \"o3-pro\":        { input: 20.00, output: 80.00 },\n  \"o1\":            { input: 15.00, output: 60.00 },\n  \"o1-mini\":       { input: 1.10,  output: 4.40  },\n\n  // \u2500\u2500 Google Gemini \u2500\u2500\n  \"gemini-2.5-pro\":        { input: 1.25,  output: 10.00 },\n  \"gemini-2.5-flash\":      { input: 0.30,  output: 2.50  },\n  \"gemini-2.5-flash-lite\": { input: 0.10,  output: 0.40  },\n  \"gemini-2.0-flash\":      { input: 0.15,  output: 0.60  },\n  \"gemini-2.0-flash-lite\": { input: 0.075, output: 0.30  },\n  \"gemini-1.5-pro\":        { input: 1.25,  output: 5.00  },\n  \"gemini-1.5-flash\":      { input: 0.075, output: 0.30  },\n  \"gemini-1.5-flash-8b\":   { input: 0.0375,output: 0.15  },\n\n  // \u2500\u2500 Perplexity \u2500\u2500\n  \"sonar\":               { input: 1.00, output: 1.00  },\n  \"sonar-pro\":           { input: 3.00, output: 15.00 },\n  \"sonar-reasoning\":     { input: 1.00, output: 5.00  },\n  \"sonar-reasoning-pro\": { input: 2.00, output: 8.00  },\n};\n\nfunction calculateCost(model, promptTokens, completionTokens) {\n  const rates = MODEL_RATES[model];\n  if (!rates) return null;\n  const inputCost = (promptTokens / 1000000) * rates.input;\n  const outputCost = (completionTokens / 1000000) * rates.output;\n  return Math.round((inputCost + outputCost) * 1000000) / 1000000;\n}\n\nconst receipt = {};\nreceipt[workflowId] = {};\nreceipt[workflowId][runId.toString()] = {};\n\nfor (const key in usage) {\n  if (!Object.prototype.hasOwnProperty.call(usage, key)) continue;\n  const steps = usage[key];\n\n  for (const i in steps) {\n    try {\n      const element = steps[i];\n      const elementData = element.data;\n      const elementDataOverride = element.inputOverride;\n\n      if (!elementData) continue;\n\n      let model = undefined;\n      let tokenData = undefined;\n\n      // LLM nodes: Anthropic, OpenAI, Gemini (ai_languageModel output)\n      if (Object.prototype.hasOwnProperty.call(elementData, 'ai_languageModel')) {\n        const override = elementDataOverride?.['ai_languageModel']?.[0]?.[0]?.json;\n        const output = elementData['ai_languageModel']?.[0]?.[0]?.json;\n\n        model = override?.options?.model\n          ?? override?.options?.model_name\n          ?? override?.model\n          ?? output?.model\n          ?? undefined;\n\n        tokenData = output?.tokenUsageEstimate ?? output?.tokenUsage ?? undefined;\n\n      // HTTP/API nodes: Perplexity and other REST-based LLMs\n      } else if (Object.prototype.hasOwnProperty.call(elementData, 'main')) {\n        const output = elementData['main']?.[0]?.[0]?.json;\n        model = output?.model ?? undefined;\n        tokenData = output?.usage ?? undefined;\n      }\n\n      if (model && tokenData) {\n        const pt = tokenData.promptTokens ?? tokenData.prompt_tokens ?? 0;\n        const ct = tokenData.completionTokens ?? tokenData.completion_tokens ?? 0;\n        const tt = tokenData.totalTokens ?? tokenData.total_tokens ?? (pt + ct);\n        const cost = calculateCost(model, pt, ct);\n\n        const existing = receipt[workflowId][runId.toString()][model];\n        if (existing && typeof existing === 'object' && existing.promptTokens !== undefined) {\n          // Aggregate tokens when same model appears multiple times in one execution\n          existing.promptTokens += pt;\n          existing.completionTokens += ct;\n          existing.totalTokens += tt;\n          if (cost !== null) existing.costUSD = (existing.costUSD ?? 0) + cost;\n        } else {\n          receipt[workflowId][runId.toString()][model] = {\n            promptTokens: pt,\n            completionTokens: ct,\n            totalTokens: tt,\n            ...(cost !== null ? { costUSD: cost } : {})\n          };\n        }\n        receipt[workflowId][runId.toString()]['createdAt'] = $json.startedAt ?? \"\";\n      }\n    } catch (e) {\n      // Skip steps that cannot be parsed\n      continue;\n    }\n  }\n}\n\nif (Object.keys(receipt[workflowId][runId.toString()]).length === 0) return {};\n\nreturn receipt;\n"
      },
      "executeOnce": false,
      "typeVersion": 2
    },
    {
      "id": "2f64c928-1c3f-41ff-af5e-cae9d6324c44",
      "name": "Loop Receipts",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        4736,
        2224
      ],
      "parameters": {
        "options": {
          "reset": false
        }
      },
      "typeVersion": 3
    },
    {
      "id": "1cef6528-b49a-4a1d-bfa5-f4fd98cbfae0",
      "name": "Has Usage Data?",
      "type": "n8n-nodes-base.if",
      "position": [
        4944,
        2240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "96b37396-1c38-423d-9d9e-32f1a99fa391",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ $json.keys().length }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "79cd6d09-997e-416f-8f3a-951feec99f7d",
      "name": "1.3 Save Receipt to Data Table",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        5280,
        2224
      ],
      "parameters": {
        "columns": {
          "value": {
            "units": "1",
            "receipt": "={{ $json[$json.keys().first()][$json[$json.keys().first()].keys().first()].toJsonString() }}",
            "created_at": "={{ $json[$json.keys().first()][$json[$json.keys().first()].keys().first()]['createdAt'] ?? DateTime.now().format('yyyy-MM-dd HH:mm:ss') }}",
            "workflowid": "={{ $json.keys().first() }}",
            "executionid": "={{ $json[$json.keys().first()].keys().first() }}"
          },
          "schema": [
            {
              "id": "workflowid",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "workflowid",
              "defaultMatch": false
            },
            {
              "id": "executionid",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "executionid",
              "defaultMatch": false
            },
            {
              "id": "receipt",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "receipt",
              "defaultMatch": false
            },
            {
              "id": "created_at",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "created_at",
              "defaultMatch": false
            },
            {
              "id": "units",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "units",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "",
          "cachedResultName": "execution_receipts"
        }
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "6445f510-a0fa-49a9-9a02-b94dff0d016d",
  "connections": {
    "Loop Receipts": {
      "main": [
        [],
        [
          {
            "node": "Has Usage Data?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hourly Trigger": {
      "main": [
        [
          {
            "node": "1.1 Get AI Agent Workflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Workflows": {
      "main": [
        [
          {
            "node": "Loop Receipts",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "1.2 Get Workflow Executions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Usage Data?": {
      "main": [
        [
          {
            "node": "1.3 Save Receipt to Data Table",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Loop Receipts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1.1 Get AI Agent Workflows": {
      "main": [
        [
          {
            "node": "Loop Workflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Token Usage & Cost": {
      "main": [
        [
          {
            "node": "Loop Workflows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1.2 Get Workflow Executions": {
      "main": [
        [
          {
            "node": "Extract Token Usage & Cost",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1.3 Save Receipt to Data Table": {
      "main": [
        [
          {
            "node": "Loop Receipts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Ever wondered how much your AI Agent is costing you per run?

Source: https://n8n.io/workflows/15177/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Automatically scan major financial newswires for biotech catalyst events, score them with AI sentiment analysis, and surface ranked trade candidates — all without manual monitoring.

RSS Feed Read, Data Table, HTTP Request +4
AI & RAG

This workflow serves as a comprehensive "Workflow Nodes SEO & Documentation Generator". It uses AI to analyze, rename, and document n8n workflows, offering a streamlined way to optimize workflow reada

Form Trigger, n8n, Output Parser Autofixing +11
AI & RAG

This workflow automates customer outreach for marketing campaigns, including customer prioritization, AI-generated emails, automated sending, reply tracking, and meeting scheduling. Data Synchronizati

Agent, Data Table, OpenAI Chat +6
AI & RAG

Automate the discovery and analysis of PDF files across a deeply nested OneDrive folder structure. This workflow recursively searches folders, filters for new or updated PDFs, extracts text, and uses

Microsoft One Drive, Lm Chat Mistral Cloud, Output Parser Structured +3
AI & RAG

V2 (2026) available! An intelligent, fully automated news aggregation system that collects articles from multiple sources (RSS feeds + Google Search), uses AI to classify and summarize the most import

N8N Nodes Serpapi, Text Classifier, Output Parser Structured +6