Counts the session's calls to paid generation APIs (image, video, voice, LLM) and adds up reported costs

A Claude Code mod from Encore Mods. Know when a session is spending money outside your Claude plan.
curl: fal, ElevenLabs, HeyGen, Replicate, OpenAI, Runway, Veo, Suno, Stability, Luma, Kling and more.$ 3 paid API calls · ~$0.42 above the prompt. The amount is added up only when a response reports its cost./plugin marketplace add benjaminb10/encore-mods
/plugin install paid-api-meter@encore-mods
Then /reload-plugins or restart Claude Code. Needs Claude Code 2.1.287 or later.
extra_services (words in MCP tool names) and extra_hosts (API hosts for curl), both comma-separated. Set it in /plugin, or with /plugin configure paid-api-meter@encore-mods.
git clone https://github.com/benjaminb10/encore-mods
claude --plugin-dir encore-mods/plugins/paid-api-meterhooks/register.js 75 lines1// paid-api-meter: counts the calls this session makes to paid generation APIs (image, video, voice,
2// LLM providers), through MCP tools or curl, and shows the total above the prompt. When a response
3// reports what the call cost, the amount is added up too.
4
5const DEFAULT_SERVICES = [
6 'fal', 'elevenlabs', 'heygen', 'replicate', 'openai', 'runway', 'veo', 'suno', 'treg', 'stability',
7 'midjourney', 'luma', 'kling', 'ideogram', 'deepgram', 'assemblyai',
8]
9const DEFAULT_HOSTS = [
10 'api.elevenlabs.io', 'fal.run', 'queue.fal.run', 'api.replicate.com', 'api.heygen.com', 'api.openai.com',
11 'generativelanguage.googleapis.com', 'api.runwayml.com', 'api.stability.ai', 'api.lumalabs.ai',
12 'api.deepgram.com', 'api.assemblyai.com',
13]
14const PRICE = /"?(?:cost|price|charged|spent|amount_usd|cost_usd|credits_used)[a-z_]*"?\s*[:=]\s*"?\$?\s*(\d+(?:\.\d+)?)/i
15
16let toolRe = null
17let hostRe = null
18let calls = 0
19let cost = 0
20
21const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
22
23export function build(services, hosts) {
24 return {
25 tool: new RegExp('^mcp__.*(' + services.map(escape).join('|') + ')', 'i'),
26 host: new RegExp('(' + hosts.map(escape).join('|') + ')', 'i'),
27 }
28}
29
30export function isPaid(e, re) {
31 const tool = String(e.tool || '')
32 if (tool === 'Bash') return /\b(curl|wget|http|xh)\b/.test(String(e.command || '')) && re.host.test(String(e.command || ''))
33 return re.tool.test(tool)
34}
35
36export function isFree(text) {
37 return /"cost(_usd)?"\s*:\s*0(\.0+)?[,}\s]/.test(String(text))
38}
39
40export function priceOf(text) {
41 const m = String(text).match(PRICE)
42 return m ? Number(m[1]) || 0 : 0
43}
44
45const list = (s) => String(s || '').split(',').map((x) => x.trim()).filter(Boolean)
46
47export function register(on, options = {}) {
48 const re = build([...DEFAULT_SERVICES, ...list(options.extra_services)], [...DEFAULT_HOSTS, ...list(options.extra_hosts)])
49 toolRe = re.tool
50 hostRe = re.host
51
52 on('tool.call', async ($, e, next) => {
53 const paid = isPaid(e, { tool: toolRe, host: hostRe })
54 const result = await next(e)
55 if (paid && !result.deny && !result.isError) {
56 let text = ''
57 try { text = JSON.stringify(result).slice(0, 200000) } catch {}
58 // A call whose response says it cost nothing (a free endpoint, a status poll) is not a paid call
59 if (isFree(text)) return result
60 calls += 1
61 cost += priceOf(text)
62 $.ui.invalidate('ui.render')
63 }
64 return result
65 }).catch(() => undefined)
66
67 on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
68 if (e.props.isWorking || !calls) return next(e)
69 const { Box, Text } = $.ui.resolve(e)
70 const label = '$ ' + calls + ' paid API call' + (calls === 1 ? '' : 's') + (cost ? ' · ~$' + cost.toFixed(2) : '')
71 const theirs = await next(e)
72 return Box({ flexDirection: 'column', children: [Text({ dimColor: true, children: [label] }), theirs] })
73 })
74}
75