Check your AI API balances every morning: HeyGen, ElevenLabs and Anthropic in one Node script, before a render dies mid-week
Last updated November 2026 · About 30 minutes · Free (one 1-token Anthropic call a day) · Comfortable

An AI content pipeline rarely breaks on a bug. It breaks on a Tuesday when a wallet hits zero and every render after that fails. Each tool keeps its balance somewhere different, and one of them doesn't expose a balance at all. This is the 70-line script that reads all three every morning.
An AI content pipeline rarely breaks on a bug. It breaks on a Tuesday when a wallet hits zero and every render after that fails. My HeyGen account is a prepaid wallet with auto-reload off, so that's a real risk every week. The morning I wrote this, it read $6.83, about 0.2 weeks of runway at my normal spend.
The annoying part is that every tool keeps its balance somewhere different. HeyGen gives you dollars, ElevenLabs gives you characters, and Anthropic doesn't give a standard API key a balance at all. This guide is the script that reads all three every morning and tells you before anything breaks. I've run a version of it daily since 27 August 2026.
What you'll have when you're done
- One Node script that prints a status line for HeyGen, ElevenLabs and Anthropic
- Each balance turned into weeks of runway, not a raw number
- An exit code a scheduler can act on: 0 when everything is funded, 1 when something needs you
- An alert rule that only messages you when something changed
- [SCREENSHOT: terminal output, three status lines, one "warn"]
Before you start
- Node.js 20 or newer, for built-in
fetchand--env-file. - Your API keys in a
.envfile:HEYGEN_API_KEY,ELEVENLABS_API_KEY,ANTHROPIC_API_KEY. Never paste them into the script. - Your normal weekly spend per tool, roughly. The script compares against it.
- About 30 minutes.
There's no download pack for this one. The script is on the page.
Step 1: Know where each balance lives
Read 6 November 2026 from each vendor's docs:
HeyGen GET https://api.heygen.com/v3/users/me
header X-Api-Key, prepaid accounts get data.wallet.remaining_balance (USD)
/v2/user/remaining_quota: v1/v2 sunset 31 Oct 2026, calls may fail
ElevenLabs GET https://api.elevenlabs.io/v1/user/subscription
header xi-api-key, returns character_count, character_limit,
next_character_count_reset_unix
Anthropic no balance endpoint for a standard API key
Usage & Cost API = spend, needs an Admin key, not for individual accounts
-> send a 1-token request; an empty account fails "credit balance is too low"
If you built against HeyGen's old quota endpoint, move now. HeyGen's own version table says v1 and v2 reached their sunset on 31 October 2026 and calls may fail intermittently while removal is in progress.
Check it worked: you can name, for each tool you pay for, the endpoint and the field that holds the balance. Any tool you can't do that for goes on a manual list you check by hand.
Step 2: Run the script
Save this as balances.mjs. Set WEEKLY_HEYGEN_USD to what you spend in a normal week.
// node --env-file=.env balances.mjs -> one line per API, exit 1 if anything needs you
const { HEYGEN_API_KEY, ELEVENLABS_API_KEY, ANTHROPIC_API_KEY } = process.env;
const WEEKLY_HEYGEN_USD = 30; // what you spend in a normal week
async function heygen() {
const r = await fetch("https://api.heygen.com/v3/users/me", {
headers: { "X-Api-Key": HEYGEN_API_KEY },
});
if (!r.ok) return ["dead", `HTTP ${r.status}`];
const w = (await r.json())?.data?.wallet;
if (!w) return ["unknown", "no wallet on this account (subscription billing?)"];
const usd = Number(w.remaining_balance);
const reload = Boolean(w.auto_reload?.enabled);
const weeks = (usd / WEEKLY_HEYGEN_USD).toFixed(1);
const status = reload ? "ok" : usd < WEEKLY_HEYGEN_USD ? "warn" : "ok";
return [status, `$${usd.toFixed(2)}, auto-reload ${reload ? "on" : "off"}, ~${weeks} weeks`];
}
async function elevenlabs() {
const r = await fetch("https://api.elevenlabs.io/v1/user/subscription", {
headers: { "xi-api-key": ELEVENLABS_API_KEY },
});
if (r.status === 401) {
const body = await r.json().catch(() => ({}));
if (body?.detail?.status === "missing_permissions")
return ["blind", "key can't read the account: enable User read on this key"];
return ["dead", "key rejected"];
}
if (!r.ok) return ["unknown", `HTTP ${r.status}`];
const s = await r.json();
const left = s.character_limit - s.character_count;
const reset = s.next_character_count_reset_unix
? new Date(s.next_character_count_reset_unix * 1000).toISOString().slice(0, 10)
: "?";
const status = left / s.character_limit < 0.2 ? "warn" : "ok";
return [status, `${left.toLocaleString("en-US")} of ${s.character_limit.toLocaleString("en-US")} left, resets ${reset}`];
}
// No balance endpoint for a standard key, so make the smallest real call.
async function anthropic() {
const r = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-haiku-4-5-20251001",
max_tokens: 1,
messages: [{ role: "user", content: "hi" }],
}),
});
if (r.ok) return ["ok", "probe accepted, account funded"];
const text = await r.text();
if (/credit balance is too low/i.test(text)) return ["critical", "OUT OF CREDIT"];
if (r.status === 429) return ["ok", "rate-limited, billing is fine"];
return ["unknown", `HTTP ${r.status}`];
}
const checks = { heygen, elevenlabs, anthropic };
let bad = 0;
for (const [name, fn] of Object.entries(checks)) {
const [status, detail] = await fn().catch((e) => ["unknown", e.message]);
if (status !== "ok") bad++;
console.log(`${status.padEnd(8)} ${name.padEnd(11)} ${detail}`);
}
process.exit(bad ? 1 : 0);
node --env-file=.env balances.mjs
Here's what it printed on my machine on 6 November 2026:
warn heygen $6.83, auto-reload off, ~0.2 weeks
ok elevenlabs 181,031 of 233,320 left, resets 2026-11-20
ok anthropic probe accepted, account funded
Check it worked: you get one line per tool and the exit code is 1 if any line isn't "ok". Run echo $? in bash, or $LASTEXITCODE in PowerShell, to see it.
Step 3: Fix the ElevenLabs blind spot
ElevenLabs lets you restrict which endpoints a key can reach, which is good practice for a key that only makes speech. The catch is that a restricted key can't read the account. The subscription endpoint answers 401 with missing_permissions, and a careless check reads that as a dead key.
My key did exactly this in August 2026. The fix is to edit the existing key on ElevenLabs and allow it to read user data. Nothing else about the key changes. The script reports this case as blind, not dead, so you know the key still works for speech.
Check it worked: the ElevenLabs line shows characters left and a reset date. If it says blind, edit the key's permissions and run it again.
Step 4: Check Anthropic the only way that works
There's no balance call for a standard API key, so the script makes the cheapest real request: one output token on the smallest model. If the account is funded, it goes through. If it's empty, it fails with a "credit balance is too low" error, which is the exact failure you want to catch the morning it starts. A 429 means you're rate-limited, which is a throughput problem, not a funding one.
While you're in the Anthropic console, check whether your billing has automatic top-up turned on. If it does, this line will rarely matter.
Check it worked: the Anthropic line says "probe accepted". If you want to see the failure path, run it with a deliberately wrong key: you should get unknown HTTP 401, not ok.
Step 5: Turn balances into runway
I think a raw balance doesn't tell you when to act. Weeks of runway does. The script divides the HeyGen wallet by your weekly spend; do the same for anything else you track.
Thresholds I use
critical this week will break (wallet under ~1/3 of a week's spend, or a dead key)
warn under one week of runway (wallet under one week's spend)
ok a week or more, or auto-reload on
ElevenLabs: warn under 20% of the monthly characters left
Tools with no API for their balance go on the list anyway. Higgsfield is one in my stack: I read its credits by hand, or through its connector inside a Claude session, and pass the number to the check.
Check it worked: every line tells you either "fine" or "act by this day". If a line gives you a number and no decision, change the threshold.
Step 6: Schedule it and alert only on change
Run it once every morning: Windows Task Scheduler, a cron job, or a scheduled Claude task. Mine runs at 09:30 with my other session checks.
The rule I think keeps it useful: alert only when the set of problems changes, then re-alert every 24 hours for something broken and every 72 hours for a warning. A check that emails you daily about the same warning gets ignored, and then it misses the day that matters. Save the last alert and its time in a small JSON file and compare before sending.
Alert rule
problems = sorted list of "tool:status" that aren't ok
if problems is empty -> no alert (and clear the saved state)
if problems != last saved -> alert now
if broken and 24h since alert -> alert again
if warning and 72h since alert -> alert again
Check it worked: run it twice in a row with the same warning. The first run alerts and the second one doesn't.
The honest part about this check
I think the limit is worth saying plainly: it only sees what each vendor exposes. HeyGen's wallet is exact, ElevenLabs is exact once the key can read it, and Anthropic is only "funded or not", never how much. For the full picture you still log in to the consoles.
And it can't top anything up. Paying is a human step, and I think it should stay one.
What I'd do differently
Move off sunset endpoints before the date. The HeyGen v2 quota call was announced well ahead.
Treat 401 with a reason as different from 401. "Can't read the balance" and "dead key" need different fixes.
Alert on change, not on state. That's what keeps you reading the alerts.
Turn auto-reload on where it exists. The best alert is the one that never fires.
FAQ
How do I check my HeyGen API balance?
Call GET https://api.heygen.com/v3/users/me with your key in the X-Api-Key header. On a prepaid account the response has a wallet object with remaining_balance in US dollars. HeyGen's version table maps the old /v2/user/remaining_quota to this endpoint and lists 31 October 2026 as the v1/v2 sunset.
Why does the ElevenLabs subscription endpoint return 401 with a working key?
ElevenLabs lets you restrict which endpoints a key can reach. A key restricted to speech can't read the account, so /v1/user/subscription answers 401 with missing_permissions. Edit the key and allow it to read user data; you don't need a new key. That's what happened on my account in August 2026.
Is there an Anthropic API endpoint for my remaining credit balance?
Not for a standard API key, as far as Anthropic's docs show. The Usage and Cost API reports spend, needs an Admin API key, and is unavailable for individual accounts. The practical check is a 1-token request: an empty account fails with a 'credit balance is too low' error.
When does the ElevenLabs character quota reset?
ElevenLabs' pricing page says the credit allowance resets at the start of each billing cycle, which begins on the day you subscribed. The subscription endpoint returns next_character_count_reset_unix, so the script can print the exact date.
Won't a daily check spam me?
Only if it alerts on every run. Alert when the set of problems changes, then re-alert every 24 hours for something broken and every 72 hours for a warning. Nothing wrong means no message at all.
Related guides
Automations that work while you sleep (free, no Zapier)
You'll put your AI research on a timer: it runs at 7 in the morning and the results are waiting when you wake up. Twenty minutes of setup, no Zapier, no subscription.
I stopped filming my reels (my AI avatar reads the script)
You'll set up a pipeline where a text file becomes a talking-head clip of your own AI avatar, in your voice, with no camera. Claude Code calls HeyGen's API from a skill file, waits for the render, and drops the clip in your editing folder. After the 30-minute setup, a reel starts with a paragraph, not a tripod.
One-person content agency: 30 short videos a week, the real cost sheet
Seven avatar reels cost me $14.71 last week. That is the whole render bill, measured, not estimated. This guide takes the per-unit costs from a pipeline that actually runs every week and does the arithmetic for 30 short videos a week: what HeyGen bills per finished second, what ElevenLabs charges per character, what local editing costs, and what the whole month comes to per finished video. Then it puts that next to what agencies and freelancers publicly charge for short-form retainers, so you can see the gap the business lives in.
Get the next build in your inbox
One email a week: the newest guides, plus one thing I only share with the list.
No spam. Unsubscribe anytime.
Build alongside others
Join the free community and share what you're shipping.

Jordan Hong Tai
I've scaled products to over 500K users, and now I build AI systems in public from a balcony in Tokyo.