All guides

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

Check your AI API balances every morning: HeyGen, ElevenLabs and Anthropic in one Node script, before a render dies mid-week

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 fetch and --env-file.
  • Your API keys in a .env file: 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

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

Jordan Hong Tai

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