Free models always look infinite on day one. You read the allowance, multiply it by your average prompt size, and conclude that you'll never hit the limit. Then a batch job runs overnight, a few loops forget their max_tokens, and suddenly your app starts returning 429s. The real problem isn't the quota—it's that your code has no idea how much budget is left until it's gone.
Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode is an open-source project that gives you access to free models with a 10 million token allowance and a free server for development workloads. Those numbers are real as of September 2026, but they can change. That's exactly why you need a self-healing budget, not a static assumption.
The Failure Mode Nobody Plans For
Most teams wire their app directly to a free API endpoint. The first sign of trouble is a wall of rate-limit errors in production. By then, every call site has already burned through the cheap tokens. What you want instead is a circuit breaker that watches your cumulative usage and switches providers before the free tier cuts you off.
This article builds a small Python service that does three things: tracks every token spent, compares it against a configurable budget, and reroutes traffic to a backup provider when the threshold is crossed. It runs comfortably on a free server, because it only needs a cron job and a CSV file.
The Design: Two Small Classes
We need a BudgetMonitor that owns the accounting and a Router that decides which provider gets the next request. The router doesn't care about model names or internal endpoints. It only asks the monitor: do we still have budget?
# budget.py
import csv
import os
from datetime import datetime
from dataclasses import dataclass
@dataclass
class BudgetMonitor:
budget: int
usage_file: str = "usage.csv"
def read_used(self) -> int:
if not os.path.exists(self.usage_file):
return 0
with open(self.usage_file) as f:
return sum(int(row["tokens"]) for row in csv.DictReader(f))
def record(self, prompt: str, tokens: int) -> None:
new_row = {
"ts": datetime.now().isoformat(),
"prompt": prompt[:60],
"tokens": tokens,
}
with open(self.usage_file, "a", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["ts", "prompt", "tokens"])
if f.tell() == 0:
writer.writeheader()
writer.writerow(new_row)
def remaining(self) -> int:
return self.budget - self.read_used()
def under_budget(self, safety_margin: int = 1000) -> bool:
return self.remaining() > safety_margin
The safety margin prevents edge-of-the-cliff failures. When you have less than 1,000 tokens left, the router should stop sending new work to the free provider.
The Router: Failover Without a Fuss
The router keeps a list of provider functions. Each function has the same signature: (prompt, max_tokens) -> (text, tokens_used). The router tries the primary provider first, but only if the monitor says we're under budget.
# router.py
from budget import BudgetMonitor
class ProviderRouter:
def __init__(self, monitor: BudgetMonitor, providers: dict):
self.monitor = monitor
self.providers = providers # e.g. {"free": free_fn, "backup": backup_fn}
self.active = "free"
def generate(self, prompt: str, max_tokens: int) -> str:
if self.active == "free" and not self.monitor.under_budget():
print("Budget exhausted, switching to backup provider")
self.active = "backup"
provider = self.providers[self.active]
text, tokens = provider(prompt, max_tokens)
self.monitor.record(prompt, tokens)
return text
The magic is in the ordering. The check happens before the call, and the recording happens after. If the free provider suddenly starts returning errors, you can extend this pattern with an exception handler that flips self.active immediately.
Wiring It to MonkeyCode's Free Models
To use this with MonkeyCode's free models, you just need a provider function that wraps the HTTP call. I'll use a stand-in endpoint because the actual URL may live behind an environment variable, but the shape is what matters.
# providers.py
import os
import requests
def monkeycode_free(prompt: str, max_tokens: int):
url = os.getenv("MONKEYCODE_API_URL")
key = os.getenv("MONKEYCODE_API_KEY")
resp = requests.post(
url,
json={"prompt": prompt, "max_tokens": max_tokens},
headers={"Authorization": f"Bearer {key}"},
timeout=30,
)
resp.raise_for_status()
data = resp.json()
tokens = data["usage"]["total_tokens"]
return data["choices"][0]["text"], tokens
def local_ollama(prompt: str, max_tokens: int):
# Replace with your own self-hosted fallback
import subprocess
result = subprocess.run(
["ollama", "run", "llama3.2", prompt],
capture_output=True, text=True, check=True,
)
return result.stdout.strip(), len(result.stdout.split())
Now the failover is honest: free models are the default, and a local model is the safety net. You're not pretending the free tier will last forever—you're planning for when it ends.
Running This on the Free Server
MonkeyCode's free server is good for exactly this kind of lightweight daemon. You don't need Kubernetes for a script that wakes up once a day. A simple systemd timer is enough.
# /etc/systemd/system/token-watch.timer
[Unit]
Description=Check token budget daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
And the service file:
# /etc/systemd/system/token-watch.service
[Unit]
Description=Token budget report
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /opt/budget/report.py
EnvironmentFile=/etc/monkeycode.env
report.py reads the usage CSV and sends a summary to your personal endpoint. The free server gives you a place to run this without paying for a VPS, but remember: it's a dev-grade box. Don't store production secrets there.
Decision Table: When to Switch Providers
| Condition | Action | Reason |
|---|---|---|
| Remaining > 20% of budget | Keep using free models | Plenty of headroom |
| Remaining between 5% and 20% | Enable caching, batch requests | Reduce token burn rate |
| Remaining < 5% | Switch to backup provider | Avoid 429s mid-task |
| Free provider returns error | Switch immediately | Don't retry a dying endpoint |
The percentages are tunable. For a weekend project, 5% is fine. For something users depend on, raise the threshold to 15% so the switch happens during low traffic.
A Test Plan That Proves the Switch
You can't trust the failover until you see it happen. Run these four steps:
- Set
BUDGET=50and send three prompts withmax_tokens=50. The first two should use the free provider, the third should trigger the switch. - Rename the
usage.csvfile and confirm the router resets cleanly to the free provider. - Make
monkeycode_free()raiserequests.HTTPErrorand verify thatRouter.generate()switches on the next call. - Confirm that every prompt appears in the CSV exactly once, no matter which provider handled it.
This takes about thirty minutes and gives you real confidence that the budget isn't just an idea in a README.
Who Should Not Use This Pattern
If your application has hard latency requirements, this router adds a pointless layer of indirection—a simple if check before the HTTP call isn't the bottleneck, but the logging and CSV I/O can add milliseconds you don't need. Also, if you're processing sensitive data that must never leave your network, free models are off the table entirely. The pattern only helps when a free tier is acceptable for the majority of your traffic and a local fallback covers the rest.
Try It Before You Trust It
The cheapest way to validate this design is to point it at a real free allowance and watch the CSV grow. MonkeyCode's free models give you 10 million tokens to play with, and the free server gives you a place to run the cron job. The code you write is portable—when the allowance changes, you just update one budget number, not your whole architecture.
If you want to see how this holds up under your own prompts, fork the pattern, plug in your provider functions, and let it run for a week. The worst case is a CSV file you can delete. The best case is an AI pipeline that survives its own pricing page.
Top comments (0)