The last thing I expected to find in my free-tier AI allowance was math. I had been building a small script to watch a public course schedule page and email me when a lecture time changed, something I coded in an evening and scheduled on a nightly cron. For a student, that feels like free money: a free model access, a free server, and zero operating cost. I was wrong in the interesting way, the way that only shows up after two weeks of quiet logs.
This article is about that two-week run, the metering layer I added to my own bot, and the exact reason why a 10 million token allowance taught me less than a 4,000-token daily cap. If you have ever wondered why your own small automation feels inexplicably heavy, this is for you.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
The setup that looked free
I decided to use MonkeyCode for this project because its free plan offers free models and a free server tier, which is the simplest possible starting point for a student who does not want to touch a credit card. I want to be precise about what I verified and what I did not. I read the project materials the day I started, and the materials claimed a 10 million token allowance and a free server option. I did not benchmark those numbers, I did not stress-test the server, and I have no idea whether those numbers will hold tomorrow. Treat them as context, not as a promise.
The important part is what happened after I signed up. A large allowance does not discipline your code; it only hides its waste. My bot was tiny, maybe forty lines of Python, but it was built the way most student bots are built: fetch a page, diff it, summarize the diff with a model, and send the summary. It felt lean because each step seemed short.
That feeling was the trap.
Metering the invisible tax
I built a small token meter into every function that touched text. It is not a real tokenizer, and it will disagree with the platform's own counter, but it is consistent and that consistency matters more than precision. My rule of thumb is one token per four characters of English or code, which is good enough for comparing relative sizes.
def estimate_tokens(text: str) -> int:
return max(1, len(text) // 4)
Here is the meter class I used. It records every spend event so I can see exactly where the allowance went after a run.
class TokenMeter:
def __init__(self, budget: int):
self.budget = budget
self.used = 0
self.events = []
def spend(self, label: str, text: str) -> int:
tokens = estimate_tokens(text)
self.used += tokens
self.events.append((label, tokens))
return tokens
def remaining(self) -> int:
return self.budget - self.used
def print_report(self) -> None:
for label, tokens in self.events:
print(f"{label}: {tokens}")
print(f"TOTAL: {self.used}")
The bot then used this meter on every stage: the system prompt, the raw diff output, the final summary prompt, and even the summary response before it was emailed. That last one surprised me, because I had never thought of counting the output as part of my budget.
SYSTEM_PROMPT = "You are a calendar assistant. Report only schedule changes in under 100 words."
class ScheduleBot:
def __init__(self, meter: TokenMeter):
self.meter = meter
def run(self, current_page: str, yesterday_page: str) -> dict:
self.meter.spend("system_prompt", SYSTEM_PROMPT)
diff = self._diff(current_page, yesterday_page)
self.meter.spend("raw_diff", diff)
if not diff.strip():
return {"status": "no_change", "tokens_used": self.meter.used}
summary = self._summarize(diff)
self.meter.spend("summary_response", summary)
return {"status": "changed", "summary": summary, "tokens_used": self.meter.used}
def _diff(self, a: str, b: str) -> str:
from difflib import unified_diff
lines = list(unified_diff(a.splitlines(), b.splitlines(), lineterm=""))
return "\n".join(lines)
def _summarize(self, diff: str) -> str:
# In production this would be a real LLM call through the free models.
return f"Schedule changed. Affected lines: {len(diff.splitlines())}"
The output for a normal night looked like this:
system_prompt: 38
raw_diff: 214
summary_response: 12
TOTAL: 264
That is under three hundred tokens for a quiet night. The whole experiment was designed to run under a self-imposed cap of four thousand tokens per day, far below what MonkeyCode's free models would ever complain about. I wanted a tight ceiling because I wanted to see the leaks.
The night the ceiling broke
Day six was calm. Day nine was a bomb. The course schedule page had been redesigned, and the diff grew to 1,340 lines of mostly irrelevant HTML formatting changes. The raw_diff spend alone was over fifteen thousand tokens, and my four thousand token cap died instantly. The bot failed, sent me an error email, and stopped.
That failure was the best thing that happened to the project. Instead of patching the bot and moving on, I printed the meter report and looked at the largest line. It was the raw_diff stage, not the model call. The fix was not smaller prompts; it was removing lines that did not matter before the model ever saw them. I added a preprocessing step that filtered the diff to lines containing the words "time", "room", or "date".
import re
def filter_relevant_diff(diff: str) -> str:
pattern = re.compile(r"(time|room|date|professor)", re.IGNORECASE)
lines = [line for line in diff.splitlines() if pattern.search(line)]
return "\n".join(lines[:50])
The result surprised me. The filtered diff was typically under twenty lines, which brought the total run down to around four hundred tokens. The same task, the same free models, and I had cut the real cost by a factor of thirty. The model was not the leak. My own laziness in sending raw text was the leak.
The decision table you actually need
I made a small table for myself to decide when a foreground meter like this is worth the effort. It is not a universal rule, just a heuristic that worked for my student-sized workflows.
| Situation | Meter worth it? | Why |
|---|---|---|
| One-off script, run twice | No | The setup time exceeds the savings. |
| Nightly cron with static prompt | Yes | Recurring cost compounds and hides regressions. |
| Long conversation loop | Yes | Context growth is the classic silent budget killer. |
| Batch processing thousands of files | Yes | A single bad file can burn a day's quota. |
| Exploratory notebook in a single session | No | You are learning, not shipping. Just print the response. |
The rule that emerged from my two weeks is simple: the more automatic the job, the more you need a meter. A human in the loop can see a long output and stop. A cron job cannot.
Mistakes I will not repeat
I have three mistakes to list, and they are all embarrassing in a useful way.
The first mistake was treating the free server as free attention. The server cost nothing, but my own attention was the real bill. A nightly job that fails at 2am sits there silently until I check my email. The free tier paid for compute, not for vigilance.
The second mistake was not measuring the system prompt. My prompt was only 38 tokens, but it appears in every single run. Over a month that is over a thousand tokens of pure overhead with zero information value. Small numbers become big numbers when they are multiplied by thirty days.
The third mistake was trusting the diff to be small because the page looked small. A page can look stable and still contain a thousand lines of churn in its HTML. The filter step is now the default in every scraping bot I write, regardless of which model or provider I use.
Who should ignore this article
If you are trying a model API for the first time this weekend, you should ignore almost everything here. Print the response, look at it, feel the latency, and move on. Meters are for code that runs more than once a day, not for curiosity. Also, I have to be honest about a sharp limit of my experiment: I never once checked the actual tokenization used by the free models, and my four-characters-per-token rule is a rough estimate. The relative numbers are trustworthy; the absolute numbers are not.
The free server also has constraints I did not test. I have no data on cold starts, CPU limits, or network reliability. If you are building something with real uptime requirements, run your own stress test against the documentation before you trust it.
The extension you can try tonight
Take any automation you already run, even a tiny one, and wrap it with this meter for one week. Then raise the size of the system prompt by five hundred words and run it again. The difference between those two weeks will show you the hidden tax of prompt overhead, and I suspect it will be larger than you guess.
My two weeks with the meter taught me a durable lesson that has nothing to do with any specific provider. Free models give you a ceiling, not a budget. The allocation is finite, and the only way to respect it is to know exactly where it goes. I thought I was saving money by using a free tier; it turned out I was saving nothing because I was spending sloppily. After the fix, the bot runs in four hundred tokens a night, which is cheap enough to feel truly free. That feeling is the real benchmark.
Top comments (0)