Give Your AI Script a Spending Limit

Build a CLI script that tracks AI API spend and stops itself at your cost cap

Introduction

30 Second Summary

Most people find out what an AI run cost after it has already finished. By then the money is gone and all you can do is read the receipt.

In this project, you will build a script that checks its own spending before every model call. It adds up what it has spent and refuses to keep going once it crosses a limit you set in cents.

What You'll Build

You will watch your script chew through a list of prompts, print its running total after each one, and stop itself mid list the moment it hits the ceiling you gave it.

By the end of this project, you'll have:

  • A command line script that runs your prompts against two different open models and prints the exact number of tokens each one used, so the cheaper model is obvious at a glance.
  • A live balance readout printed before and after every call. You get to watch your balance drop by the amount the rate card said it would.
  • A spending ceiling in cents that the script enforces on itself. Feed it a prompt list that is too long and watch it refuse its next call and tell you why.
  • Secret Mission: an optional challenge to push your script further once the main build works.

Are there any prerequisites?

You need a laptop, an engy account with an API key, and any OpenAI compatible client library. Everything else is set up in Step 1.

Rates are published per token with no subscription, for example $0.045 input and $0.09 output per million tokens on deepseek-v4-flash, so the whole build costs a few cents.

Before We Start

Every model call you make costs real money, and most tools only tell you the bill after the run has finished.

You are about to build a script that knows its own spending limit before it calls anything. That starts with an account to spend against and a rate card to do the maths with.

Your provider for this project is engy, a hosted service that serves open models over an OpenAI compatible API.

In this step, get ready to:
  • Create an engy account.
  • Generate an API key that your script will use later.
  • Note the input and output rates for two models on the pricing table.
Create your engy account

Everything in this project runs against your own account, including the balance your script will read back later.

  • Open engy.ai in your browser.
  • Create your account by following the sign-up prompts on the page.
  • Sign in once your account exists.

You are now looking at your own account area. This is where your API keys and your balance live.

Can't get past the sign-up screen?

  • Check your spam folder if you are waiting on a confirmation email.
  • Turn off ad blockers for the page, since they often block sign-in pop-ups.

Why can't I finish signing up for a new service?

Generate your API key

Your API key is the credential your script sends with every request. It identifies your account, so anyone holding it can spend your balance.

  • Find the API keys area of your account.
  • Create a new key.
  • Copy the key the moment it appears, because most providers show it in full only once.
  • Paste it into your password manager or a private note for now.

Keep your key out of screenshots

Only screenshot the keys list once the full value is hidden. A key sitting in an image is a live credential that anyone can reuse.

Before you look, do you think the keys list shows the full key, or a shortened version of it?

  • Close the key creation window and return to the API keys list.

Your key should now be listed with most of its characters hidden and only the last few visible. That masked entry is your proof the key exists on the account.

Lost the key before you copied it?

  • Create a second key and copy that one instead, since a hidden key is rarely shown in full again.
  • Delete the unused key so you only carry one live credential.

How should I store an API key safely on my laptop?

Note the rates for two models

The models table on the engy homepage lists what each model charges per million tokens.

  • Switch back to the engy homepage in your browser.
  • Scroll down to the models table.
  • Find the row that starts with deepseek-v4-flash.
  • Read across that row to the input and output columns.

Every price in the table is per 1M tokens. On deepseek-v4-flash that is $0.045 for input and $0.09 for output.

How much will this project cost?

At $0.045 per million input tokens, a short prompt costs a tiny fraction of a cent. A few dozen calls across this whole project stays well under a dollar.

That is exactly why the spending cap you build later is measured in cents rather than dollars. A limit in whole dollars would never trigger.

  • Pick a second row from the same table with a visibly different price, so the comparison later is obvious.

Before you read across that row, do you expect its output rate to be higher or lower than its input rate?

  • Record the model id: your second model id.
  • Record its input rate per million tokens: your second model input rate.
  • Record its output rate per million tokens: your second model output rate.

Output costs more than input on every row in this table. Your spending counter has to add the two up separately to land on a real number.

Your account is live, your key is stored safely, and you know what each model charges. Next up, you will point an OpenAI compatible client at engy and run your first request.

Send Your First Chat Request

You have an account, a key, and a rate card that prices every million tokens. None of that costs you anything until a request actually goes out.

So let's send one. The reply is the obvious part of what comes back, but the token counts sitting next to it are what the rest of this project runs on.

In this step, get ready to:
  • Point an OpenAI-compatible client at engy's API base URL.
  • Send one chat request and read the model's reply.
  • Print the token counts engy returns alongside that reply.
Set up your project folder

Your script needs two things before it can talk to engy: a Python you can run, and the client library that speaks the OpenAI request format.

  • Check that Python is installed by running this command:
python3 --version

✔️ I see a version number

Python is ready. Carry on with the bullets below.

ⓧ Command not found

  • On Windows, try the other spelling of the command first by running:
python --version

If that also comes back empty, Python isn't installed yet.

  • Open python.org/downloads in your browser.
  • Click the big download button for your operating system.
  • Run the installer and accept the defaults.
  • Close your terminal, open a fresh one, and run the version command again.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir engy-spend-tracker
cd engy-spend-tracker
  • Install the OpenAI client library by running this command:
pip install openai
  • Confirm the install worked by looking for a line starting with Successfully installed at the bottom of the output.

Install didn't finish?

If your terminal doesn't recognise pip, try pip3 instead. It's the same tool, named for the Python 3 install.

If the install stops with a permissions error, your setup is trying to write outside your user folder.

How do I fix a pip install that fails on my machine?

Store your key and point a client at engy

engy speaks the same request format as OpenAI, so any OpenAI-compatible client works against it. You swap the base URL and the key, and the rest of your code stays the same.

Your key belongs in an environment variable rather than in the script, so it never ends up in a file you might share.

macOS

  • Store your key in this terminal session by running this command, with your own key pasted in place of your-api-key-here:
export ENGY_API_KEY="your-api-key-here"
  • Confirm the variable is set by running this command:
if [ -n "$ENGY_API_KEY" ]; then echo "Key is set"; fi

You should see Key is set printed back. The check never prints the key itself, which is exactly what you want.

Windows

  • Store your key in this PowerShell session by running this command, with your own key pasted in place of your-api-key-here:
$env:ENGY_API_KEY = "your-api-key-here"
  • Confirm the variable is set by running this command:
if ($env:ENGY_API_KEY) { "Key is set" }

You should see Key is set printed back. The check never prints the key itself, which is exactly what you want.

Your key lives in this terminal only

The variable disappears when you close this terminal window. If you come back later and your script complains about a missing key, run the same command again.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Select the engy-spend-tracker folder on your Desktop, then click Open.

The engy-spend-tracker folder now shows in the Explorer sidebar, empty for now.

  • Click the New File icon at the top of the Explorer sidebar.
  • Type spend_tracker.py and press Enter.
  • Build your client by copying this code into the empty spend_tracker.py file:
import os
from openai import OpenAI

# Point the OpenAI client at engy's API instead of OpenAI's
client = OpenAI(
    base_url="https://api.engy.ai/v1",
    api_key=os.environ["ENGY_API_KEY"],
)

# The model every request in this script will use
MODEL = "deepseek-v4-flash"

What does this code do?

  • base_url sends every request to engy's gateway rather than OpenAI's.
  • os.environ reads the key out of the variable you just set, so the key never sits in the file.
  • MODEL holds the model id in one place, which makes swapping models later a one-line change.
  • Save spend_tracker.py.

Before you run it, do you expect a traceback or a clean exit? Your script builds a client but never sends anything.

  • Run your script by returning to your terminal and running this command:
python3 spend_tracker.py

You get your prompt back with a clean exit. That silence is the pass: Python found your key and built a client pointed at engy without complaining.

Got a traceback instead?

An error naming the openai module means the package landed in a different Python than the one running your script. Try pip3 for the install, then run the script again.

An error naming ENGY_API_KEY means the variable isn't set in this terminal window. Set it again with the command from above.

Why is my script failing before it sends anything?

Send a prompt and read the token counts

A client on its own spends nothing. The moment you send a prompt, engy charges you for the tokens going in and the tokens coming back.

Those two counts arrive in the response itself, so you never have to guess at them with your own tokenizer.

  • Add the request function by copying this code to the bottom of spend_tracker.py, below the MODEL line:
def run_chat(prompt):
    # Send one chat request to engy and wait for the full reply
    response = client.chat.completions.create(
        model=MODEL,
        messages=[{"role": "user", "content": prompt}],
    )

    # The reply text sits on the first choice in the response
    print(response.choices[0].message.content)

    # engy counts the tokens for us and reports them on usage
    print("prompt_tokens:", response.usage.prompt_tokens)
    print("completion_tokens:", response.usage.completion_tokens)

run_chat("Explain what an API rate limit is in two sentences.")

What does this code do?

  • client.chat.completions.create is the one call that costs money. Everything else in the file is preparation.
  • messages carries your prompt as a single turn from the user.
  • response.usage is engy's own count of what it billed you for, which is why you read it instead of counting tokens yourself.
  • The last line calls run_chat once so the script does something when you run it.
  • Save spend_tracker.py.

Before you run this, which number do you think will be bigger: the prompt tokens or the completion tokens? Your prompt is one short sentence, and the model is being asked for two.

  • Send your first request by running this command:
python3 spend_tracker.py

After a second or two you see the model's answer, then a prompt_tokens: line and a completion_tokens: line.

Those two numbers are the whole basis of your spending limit. Multiply them by the rates you noted from the pricing table and you have the exact cost of this one call.

No reply coming back?

An authentication error means the key in ENGY_API_KEY is wrong or picked up a trailing space when you pasted it. Copy it again from your account dashboard and set the variable once more.

An error naming the model means your pricing table spells it differently. Copy the model id exactly as the table lists it into the MODEL line.

On Windows, run python spend_tracker.py if python3 isn't recognised.

Why is my chat request failing?

✔️ Awesome, I've got everything!

Great, double check you've saved spend_tracker.py.

ⓧ I'd like to double check the full code

Here's the whole file as it should look at the end of this step.

import os
from openai import OpenAI

# Point the OpenAI client at engy's API instead of OpenAI's
client = OpenAI(
    base_url="https://api.engy.ai/v1",
    api_key=os.environ["ENGY_API_KEY"],
)

# The model every request in this script will use
MODEL = "deepseek-v4-flash"

def run_chat(prompt):
    # Send one chat request to engy and wait for the full reply
    response = client.chat.completions.create(
        model=MODEL,
        messages=[{"role": "user", "content": prompt}],
    )

    # The reply text sits on the first choice in the response
    print(response.choices[0].message.content)

    # engy counts the tokens for us and reports them on usage
    print("prompt_tokens:", response.usage.prompt_tokens)
    print("completion_tokens:", response.usage.completion_tokens)

run_chat("Explain what an API rate limit is in two sentences.")

Your script now talks to engy and reports what each reply cost in tokens. Next up, you'll ask engy for your balance before and after a call and watch it drop by the amount the rate card predicts.

Check Balance Before and After a Call

Last step your script sent a prompt to engy and printed the reply along with the prompt and completion token counts. Those counts tell you how much work the model did, and nothing about what it cost you.

You could multiply tokens by the rate card yourself and hope your math matches engy's. The provider already knows the real number, and it publishes it on a read-only balance and usage endpoint.

In this step you call that endpoint immediately before and immediately after the chat request. The gap between the two readings is what the call actually cost.

In this step, get ready to:
  • Call engy's read-only balance and usage endpoint from Python.
  • Print your balance before and after a chat request.
  • Compare the balance drop against what the rate card predicts.
Find the balance endpoint and install requests

engy reports balance and usage on a read-only endpoint, separate from the chat API. Reading it is a plain HTTP GET, so your script needs one more library to make the request.

  • In engy's docs, open the page describing the read-only balance and usage endpoint.
  • Copy the endpoint's full URL and note it here: the balance and usage endpoint URL from the engy docs.
  • Install the requests library by running this command:
pip install requests
  • Check the terminal output for a success line naming requests and its version number.

Install not working?

  • If your terminal says pip is not recognised, try pip3 instead. Some systems install Python's package manager under that name.
  • If the install runs but reports a permissions problem, close any Python process using the folder and run the command again.

Why did my install fail?

Read your balance before the call
  • Return to spend_tracker.py in your editor.
  • Teach your script to ask engy for its balance by adding these lines directly below your import lines:
import requests

BALANCE_URL = '[[ENGY_BALANCE_URL="the balance and usage endpoint URL from the engy docs"]]'

def get_balance():
    # Engy checks this GET with the same API key the chat client uses
    headers = {"Authorization": f"Bearer {os.environ['ENGY_API_KEY']}"}
    response = requests.get(BALANCE_URL, headers=headers)
    return response.json()

What does this code do?

  • BALANCE_URL holds the read-only endpoint you copied out of engy's docs.
  • The Authorization header sends your API key, the same key the chat client already uses.
  • response.json() hands back the whole payload, so every field engy reports stays visible to you.
  • Save spend_tracker.py.
  • Check your script still runs by running this command:
python spend_tracker.py

You should see the same reply and token counts as last time. That output confirms requests imported cleanly and your new function parses.

Seeing an import error?

  • A ModuleNotFoundError naming requests means the install landed on a different Python than the one running your script.
  • An IndentationError usually means the pasted block picked up extra leading spaces. The four lines inside get_balance() each need exactly four.

Why can't Python find requests?

  • In spend_tracker.py, find the first line inside run_chat(prompt):
    response = client.chat.completions.create(model=MODEL, messages=[{'role': 'user', 'content': prompt}])
  • Take a balance reading before the request by adding two lines above it, so the top of run_chat looks like this:
    before = get_balance()
    print('Balance before:', before)
    response = client.chat.completions.create(model=MODEL, messages=[{'role': 'user', 'content': prompt}])

Before you run this, what do you think engy sends back? A single number, or a payload carrying several fields?

  • Save spend_tracker.py.
  • See your first balance reading by running this command:
python spend_tracker.py

Your balance payload prints first, then the reply and the token counts. Usage endpoints report more than one figure, so expect a small block of JSON rather than a bare number.

Read your balance after the call and print the drop

One reading tells you where you stand. Two readings, taken either side of the same request, tell you what that request cost.

  • Note the exact name of the field holding your spendable balance: the balance field name from your output.
  • In spend_tracker.py, find the line that prints response.usage.completion_tokens.
  • Measure the cost of the call by adding these lines directly below it:
    after = get_balance()
    print('Balance after:', after)
    # Look up the same field in both readings so the subtraction lines up
    balance_field = '[[BALANCE_FIELD="the balance field name from your output"]]'
    print('This call cost:', before[balance_field] - after[balance_field])

What does this code do?

  • get_balance() runs a second time, this time after engy has billed you for the request.
  • balance_field holds the field name you read off your own output, so both lookups stay in step.
  • Subtracting the two readings gives the price of exactly one call, in whatever unit engy reports.

Before you run this, work out roughly what the call should cost. deepseek-v4-flash charges $0.045 input and $0.09 output per million tokens, and your script already prints both token counts.

  • Save spend_tracker.py.
  • Watch your balance move by running this command:
python spend_tracker.py

You now see a balance before, the reply, the token counts, a balance after, and the cost of that one call. The drop should land within a rounding error of your own rate card math.

Cost printing as zero?

  • Billing can settle a moment behind the response, so the second reading may not have caught up. Run the script again and compare the two costs.
  • At these rates a short prompt costs a fraction of a cent, which a rounded balance figure can swallow. Send a longer prompt to make the drop obvious.
  • A KeyError means the field name you noted does not match the payload. Copy it straight out of the printed balance.

Why is my balance not moving?

✔️ Awesome, I've got everything!

Great. Double check you've saved spend_tracker.py before you move on.

ⓧ I'd like to double check the full code

Your base URL and test prompt are the ones you set last step. Everything else should match this:

import os
from openai import OpenAI
import requests

BALANCE_URL = '[[ENGY_BALANCE_URL="the balance and usage endpoint URL from the engy docs"]]'

def get_balance():
    # Engy checks this GET with the same API key the chat client uses
    headers = {"Authorization": f"Bearer {os.environ['ENGY_API_KEY']}"}
    response = requests.get(BALANCE_URL, headers=headers)
    return response.json()

client = OpenAI(base_url='[[ENGY_BASE_URL="engy API base URL"]]', api_key=os.environ['ENGY_API_KEY'])

MODEL = 'deepseek-v4-flash'

def run_chat(prompt):
    before = get_balance()
    print('Balance before:', before)
    response = client.chat.completions.create(model=MODEL, messages=[{'role': 'user', 'content': prompt}])
    print(response.choices[0].message.content)
    print('Prompt tokens:', response.usage.prompt_tokens)
    print('Completion tokens:', response.usage.completion_tokens)
    after = get_balance()
    print('Balance after:', after)
    balance_field = '[[BALANCE_FIELD="the balance field name from your output"]]'
    print('This call cost:', before[balance_field] - after[balance_field])

run_chat('[[TEST_PROMPT="your test prompt from the last step"]]')

Your script now knows the price of a call the moment that call finishes. Next up, you'll turn that number into a running total and give it a ceiling it refuses to cross.

Add a Self-Enforced Spending Ceiling

Your script can already watch its own balance move. Reading the drop after the request has gone out still leaves the money spent.

A ceiling flips the order. The script keeps a running total of what it has spent, and it checks that total before it makes the next call.

In this step you teach run_chat to report the cost of its own call in cents. Then you wrap it in a loop that refuses to keep going once the total crosses a limit you set.

In this step, get ready to:
  • Return the cost of each call in cents from run_chat.
  • Set a spending ceiling and a prompt list long enough to blow past it.
  • Run the script and watch it stop itself with prompts still in the queue.
Return the cost of each call in cents

A ceiling needs a number to compare against. The pricing table gives you rates per million tokens, and every response hands back its own token counts.

At $0.045 input and $0.09 output per million tokens on deepseek-v4-flash, one short call lands in thousandths of a cent. Working in cents keeps those numbers readable.

  • In spend_tracker.py, scroll to the bottom of your run_chat function, below the print that shows the before and after balance difference.
  • Price each call by adding this code as the last thing run_chat does:
    # engy's pricing table rates for deepseek-v4-flash, in dollars per million tokens
    input_rate = 0.045
    output_rate = 0.09

    # Turn the token counts into a price, then convert dollars into cents
    cost_cents = (
        response.usage.prompt_tokens * input_rate
        + response.usage.completion_tokens * output_rate
    ) / 1_000_000 * 100

    print(f"This call cost {cost_cents:.5f} cents")

    # Hand the cost back so the caller can add it to a running total
    return cost_cents

What does this code do?

  • input_rate and output_rate are the per million token prices you noted from the pricing table.
  • Dividing by 1_000_000 turns a per million rate into a per token price. Multiplying by 100 turns dollars into cents.
  • The token counts come from the response body, so the price reflects what the provider actually billed.
  • return cost_cents hands the number back to whatever called the function. That is the number your ceiling adds up.
  • Save spend_tracker.py.
  • Price your test call by running this command:
python3 spend_tracker.py

Underneath the reply and the token counts, you now see a new line like This call cost 0.00095 cents.

No cost line in the output?

  • The new lines must sit inside run_chat, indented to the same level as the print statements above them.
  • If Python complains that response is not defined, the code landed at the far left of the file and runs before any call has been made.

Why is my cost calculation not running inside my Python function?

Set a ceiling and a list of prompts

A ceiling only proves itself when the work in front of it is bigger than the budget. A short prompt list with a tiny ceiling gets you that proof in seconds.

  • In spend_tracker.py, delete the last line of the file, the single run_chat call with your test prompt from the previous step.
  • Cap the run by adding this code at the bottom of the file:
SPEND_LIMIT_CENTS = 0.003

PROMPT_LIST = [
    "Name one benefit of metering AI spend.",
    "Name one risk of an unmetered AI script.",
    "Describe a runaway AI cost in one sentence.",
    "Give one reason to cap spend in code.",
    "Name one thing a usage endpoint reports.",
    "Give one tip for keeping API costs low.",
]

total_spent_cents = 0

for prompt in PROMPT_LIST:
    # Check the ceiling before spending anything else
    if total_spent_cents >= SPEND_LIMIT_CENTS:
        print(f"Ceiling reached: {total_spent_cents:.5f} of {SPEND_LIMIT_CENTS} cents spent. Stopping.")
        break

    cost_cents = run_chat(prompt)
    total_spent_cents += cost_cents
    print(f"Running total: {total_spent_cents:.5f} cents")

What does this code do?

  • SPEND_LIMIT_CENTS is your ceiling. Three thousandths of a cent sits just above the price of a couple of short calls.
  • total_spent_cents starts at zero and grows by the cost that run_chat returns after every call.
  • The if check sits at the top of the loop, so the script refuses the next request instead of discovering the overspend afterwards.
  • break leaves the loop with prompts still unused. That is the whole point of a cap.
  • Save spend_tracker.py.
Watch the script stop itself

Six prompts are queued and the ceiling is three thousandths of a cent. Before you run this, how many of the six do you think the script gets through, and what do you expect to see once it stops?

  • Start the capped run by running this command:
python3 spend_tracker.py

The script works through the first few prompts, printing a running total after each one. Then it prints Ceiling reached and stops with prompts still in the list.

What should I see?

Each call adds a few thousandths of a cent to the running total. Once that total reaches your ceiling, the check at the top of the loop fires before the next request ever leaves your machine.

The prompt it stops on depends on how long each reply was. A chattier reply burns more output tokens and pushes the stop one prompt earlier.

Script ran through all six prompts?

  • Your replies were short enough that six calls stayed under the ceiling. Lower SPEND_LIMIT_CENTS to 0.001 and run the script again.
  • If the running total stays at zero, run_chat is not returning a cost. Check that return cost_cents is the last line of the function.

Why does my spending ceiling never trigger?

✔️ Awesome, I've got everything!

Great. Double check you have saved spend_tracker.py before you move on.

ⓧ I'd like to double check the full code

Scroll up through spend_tracker.py and confirm each piece is present, in this order:

  • Your imports, the client pointed at engy's API base URL, and MODEL = 'deepseek-v4-flash' from the earlier steps.
  • The get_balance() function from the previous step, untouched.
  • The run_chat(prompt) function, now ending with the rate lines, the cost calculation and return cost_cents.
  • SPEND_LIMIT_CENTS, then PROMPT_LIST, then total_spent_cents = 0 at the bottom of the file.
  • The loop over PROMPT_LIST, with the ceiling check and break sitting above the call to run_chat.

Your script now polices its own spending. Next up, you'll run the same prompt list against a second model and put both totals side by side.

Compare Two Models Side by Side

Your script now stops itself before it can overspend. That makes it safe, and it also turns it into a measuring instrument.

So far it has only measured one model. The engy pricing table lists several, and the rates between them differ by more than most people guess.

In this step you point the same prompt list at a second model and print both totals in one table. The cheaper model stops being a guess from a rate card and becomes a number you measured yourself.

In this step, get ready to:
  • Make run_chat work with any model from the pricing table.
  • Wrap your capped loop in a function you can run once per model.
  • Print both totals in one table and see which model costs less.
Teach run_chat which model to call

Your script has one model name baked into it, and one pair of prices baked into the cost math. A fair comparison needs both to become inputs.

  • In spend_tracker.py, find the line that says MODEL = 'deepseek-v4-flash'.
  • Give the script a second model and a price lookup by copying this code below that line:
SECOND_MODEL = '[[SECOND_MODEL="your second model name"]]'

# Per-million-token rates you noted from the pricing table
RATES = {
    MODEL: {'input': 0.045, 'output': 0.09},
    SECOND_MODEL: {'input': [[SECOND_INPUT_RATE="your second model input rate"]], 'output': [[SECOND_OUTPUT_RATE="your second model output rate"]]},
}

What is the RATES table for?

  • RATES maps each model name to the per-million-token prices you noted from the pricing table.
  • Looking prices up by model name means one piece of cost math can price a call to either model.
  • Scroll up to your run_chat function and find its first line:
def run_chat(prompt):
  • Add a model parameter and pull that model's prices out of the table, so the top of the function looks like this:
def run_chat(model, prompt):
    # Look up this model's prices instead of using fixed numbers
    input_rate = RATES[model]['input']
    output_rate = RATES[model]['output']
  • Inside run_chat, change model=MODEL in the chat request to model=model.
  • Further down run_chat, swap the two prices you typed into your cost math for input_rate and output_rate.
  • In the loop at the bottom of the file, change run_chat(prompt) to run_chat(MODEL, prompt).

Why pass the model in?

A function that knows only one model can only ever measure one model. Passing the name in means the same code prices and runs whichever model you hand it.

  • Save spend_tracker.py.
  • Check the refactor did not break anything by running this command:
python3 spend_tracker.py

You should see exactly the run you saw in Step 4. Prompts go through one by one, the balance prints around each call, and the loop stops itself once it reaches your ceiling.

Script stopping with an argument error?

  • Check the loop at the bottom now calls run_chat(MODEL, prompt) with both arguments.
  • If the error mentions a name that does not exist, check input_rate and output_rate are defined above the cost math that uses them.

My Python script errors after I added a model argument to run_chat, what should I check?

Wrap the loop in a reusable function

Your capped loop sits loose at the bottom of the file, so it can only run once. To run it twice, once per model, it needs to become a function with its own counter.

  • In spend_tracker.py, find the total_spent_cents = 0 line and the loop over PROMPT_LIST underneath it.
  • Turn both of them into a reusable function by replacing them with this code:
def run_prompt_list(model):
    # Announce which model this run belongs to
    print(f"\n=== {model} ===")

    # Each model gets its own fresh counter and its own ceiling
    total_spent_cents = 0

    for prompt in PROMPT_LIST:
        if total_spent_cents >= SPEND_LIMIT_CENTS:
            print(f"Ceiling reached: {total_spent_cents} cents spent of {SPEND_LIMIT_CENTS}.")
            break

        total_spent_cents += run_chat(model, prompt)

    # Hand the final total back so it can go in the table
    return total_spent_cents

run_prompt_list(MODEL)

What does this code do?

  • The header line makes each run label itself, so two runs in one terminal stay readable.
  • total_spent_cents now lives inside the function, so every model starts from zero and gets the full ceiling.
  • return total_spent_cents hands the final figure back to whoever called the function, which is what the comparison table will need.
  • Save spend_tracker.py.
  • See the new labelled run by running this command:
python3 spend_tracker.py

A new === deepseek-v4-flash === header now sits above the run. Below it, the same prompts go through and the same ceiling message stops the loop.

Seeing an indentation error?

  • Every line inside run_prompt_list needs to be indented one level deeper than the def line.
  • Check the old total_spent_cents = 0 line is gone from the bottom of the file, and only the one inside the function remains.
  • Check run_prompt_list(MODEL) sits flush against the left edge, outside the function.

I wrapped my loop in a function and now Python complains about indentation, what went wrong?

Run both models and print the table

Two totals scrolling past in a long terminal log are hard to compare. Collecting them into a two row table at the end makes the cheaper model obvious at a glance.

  • In spend_tracker.py, find the very last line of the file:
run_prompt_list(MODEL)
  • Replace that single line with these lines, so both models run and both totals land in one table:
# Run the same prompt list against each model in turn
first_total = run_prompt_list(MODEL)
second_total = run_prompt_list(SECOND_MODEL)

# Put both totals side by side so the cheaper model stands out
print("\nModel                Total spend (cents)")
print(f"{MODEL:<20} {first_total}")
print(f"{SECOND_MODEL:<20} {second_total}")

What does this code do?

  • first_total and second_total catch the figure each run returns.
  • The {MODEL:<20} part pads each model name out to 20 characters, which lines the numbers up into a column.

Each model gets its own ceiling

Both runs cap themselves at SPEND_LIMIT_CENTS, so a full comparison can spend up to twice that figure in total.

That is the honest cost of a fair test. Both models face the same prompt list and the same budget.

  • Save spend_tracker.py.

Before you run this, which of your two models do you think will show the lower total? Look at the input and output rates you noted from the pricing table and commit to an answer.

  • Compare both models by running this command:
python3 spend_tracker.py

Two labelled runs scroll past, each stopping itself at the ceiling. At the very bottom you get a two row table with a total in cents beside each model name.

One number is lower than the other. That is your prediction settled, measured against your own balance rather than a rate card.

Both totals looking identical?

  • Check the two entries in RATES hold different numbers, and that they match the rates you noted from the pricing table.
  • Check the model name in SECOND_MODEL is spelled exactly as the pricing table spells it.
  • If the final balance figure looks unchanged, wait a moment and run the balance check again, because billing can settle slightly behind the response.

My two models are reporting the same total spend, what should I check first?

✔️ Awesome, I've got everything!

Great, double check you have saved spend_tracker.py.

ⓧ I'd like to double check the full code

Scroll up through this step and confirm each piece is present in your file, in this order:

  • SECOND_MODEL and the RATES table, sitting just below MODEL = 'deepseek-v4-flash'.
  • def run_chat(model, prompt): with the two rate lookup lines underneath it.
  • model=model in the chat request, and input_rate and output_rate in the cost math.
  • The run_prompt_list function holding your capped loop and returning the total.
  • The two run calls and the three print lines that build the table, at the very bottom of the file.

Secret mission

Make Your Spending Limit Survive a Restart

Your ceiling resets to zero every time you run the script, so three runs can spend three times your limit. In this mission you save the running total to a log file and check it before the first call, turning a per-run cap into a lifetime budget.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. Nothing here runs in the background, so your only spend was the few cents of engy credit your script used while it ran.

Cost warning

Your engy balance is prepaid, and any live API key can spend it. Note your remaining balance now so you can confirm nothing kept spending after you closed the terminal.

If you are not coming back to this soon, delete the API key. A key that no longer exists cannot be used to burn through your balance.

Resources you used:

  • The API key you generated in your engy account.
  • The ENGY_API_KEY environment variable in your terminal.
  • The spend_tracker.py script in your project folder.

Keep everything running

No action needed. Choose this if you're still actively building on your spend tracker.

  • Set SPEND_LIMIT_CENTS to an amount you're happy to spend before your next run.
  • Check your remaining balance in your engy account dashboard before that run.

Your engy account stays open either way. There is no ongoing charge for having one.

Pause - I'll come back to this later

Take the key out of reach so nothing can spend your balance, and keep your files for later.

  • Clear the key from your current terminal session by running this command:
unset ENGY_API_KEY
  • Leave spend_tracker.py where it is so you can pick it back up.
  • Write down the balance shown in your engy account dashboard so you can compare it next time.

Delete - I don't want to use this again

Remove the key and the script so nothing is left that can call the API.

  • In your engy account dashboard, open the API keys area where you generated your key.
  • Delete the API key you created for this project.
  • Clear the key from your current terminal session by running this command:
unset ENGY_API_KEY
  • Remove the line that sets ENGY_API_KEY from your shell profile if you added one there.
  • Move to the folder that holds spend_tracker.py by running this command:
cd [[PROJECT_FOLDER="path to the folder holding spend_tracker.py"]]
  • Delete the script on macOS or Linux by running this command:
rm spend_tracker.py
  • Delete the script on Windows by running this command:
del spend_tracker.py
  • Confirm the script is gone by running this command:
ls

The output no longer lists spend_tracker.py.

Nice Work!

Nice Work

You've built a script that knows what it costs before it decides whether to keep going. Your spending ceiling lives in the code now, not in your head.

You've learned how to:

  • Point an OpenAI-compatible client at engy's API and read the token counts back out of the response body instead of guessing at them.
  • Call a read-only balance and usage endpoint around each model call and watch your balance move by the amount the rate card predicts.
  • Meter your own spend against a ceiling in cents so the script refuses its next call and tells you why, instead of quietly draining your balance.
  • Take on the Secret Mission and push the project past what the steps covered.

Ready to quiz yourself?