Measure the Burn Rate: A Token Meter for Quota-Limited AI Coding Servers

python dev.to

A free AI coding server is a budget, not a speed limit. The practical problem is not whether the quota is large enough — it is that nobody sees the consumption until a request fails. This article builds a small token meter that records every request, computes a daily burn rate, and predicts the exhaustion date. The tool works with any OpenAI-compatible endpoint, including the free server that the open-source MonkeyCode project currently advertises.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

Why invisible consumption is the real problem

Free tiers advertise a token allowance. The MonkeyCode project, for example, currently lists a free tier with 10 million tokens and a free hosted server. That number sounds abstract. In practice, consumption comes from many directions:

  • Editor autocomplete and inline chat requests
  • Scripts that batch-process files
  • CI jobs that run AI-assisted review
  • Manual experiments with prompts

Each request is small. The sum is not. Without a meter, the first sign of trouble is a 429 or a quota-exceeded error in the middle of a task.

A token meter in three parts

The meter has three components: a SQLite table for usage records, a wrapper around the OpenAI client, and a report function.

1. The storage layer

import sqlite3
import time
from pathlib import Path

DB_PATH = Path.home() / ".token_meter.db"

def init_db():
    conn = sqlite3.connect(DB_PATH)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS usage (
            ts INTEGER PRIMARY KEY,
            model TEXT,
            prompt_tokens INTEGER,
            completion_tokens INTEGER,
            total_tokens INTEGER
        )
    """)
    conn.commit()
    return conn
Enter fullscreen mode Exit fullscreen mode

2. The metered client

from openai import OpenAI

class MeteredClient:
    def __init__(self, base_url, api_key, model):
        self.client = OpenAI(base_url=base_url, api_key=api_key)
        self.model = model
        self.conn = init_db()

    def chat(self, messages, **kwargs):
        resp = self.client.chat.completions.create(
            model=self.model,
            messages=messages,
            **kwargs
        )
        usage = resp.usage
        self.conn.execute(
            "INSERT INTO usage (ts, model, prompt_tokens, completion_tokens, total_tokens) VALUES (?, ?, ?, ?, ?)",
            (int(time.time()), self.model, usage.prompt_tokens, usage.completion_tokens, usage.total_tokens)
        )
        self.conn.commit()
        return resp
Enter fullscreen mode Exit fullscreen mode

3. The report

def burn_rate(days=7):
    conn = sqlite3.connect(DB_PATH)
    cutoff = int(time.time()) - days * 86400
    row = conn.execute(
        "SELECT SUM(total_tokens), COUNT(*), MIN(ts), MAX(ts) FROM usage WHERE ts >= ?",
        (cutoff,)
    ).fetchone()
    total, count, first, last = row
    if not total or not first or first == last:
        return None
    elapsed_days = (last - first) / 86400
    return {
        "total_tokens": total,
        "requests": count,
        "elapsed_days": round(elapsed_days, 2),
        "tokens_per_day": int(total / elapsed_days),
    }

def report(allowance):
    rate = burn_rate()
    if not rate:
        print("No usage recorded yet.")
        return
    days_left = allowance / rate["tokens_per_day"]
    print(f"Usage over {rate['elapsed_days']} days:")
    print(f"  Total tokens: {rate['total_tokens']:,}")
    print(f"  Requests: {rate['requests']}")
    print(f"  Burn rate: {rate['tokens_per_day']:,} tokens/day")
    print(f"  At this rate, {allowance:,} tokens last ~{days_left:.0f} days")
Enter fullscreen mode Exit fullscreen mode

Wiring it into a workflow

Replace the direct client construction with the metered version. The interface is identical, so existing code keeps working.

client = MeteredClient(
    base_url="https://your-server.example.com/v1",
    api_key="none",  # free tiers often ignore the key
    model="model-id-from-docs"
)

resp = client.chat([
    {"role": "user", "content": "Refactor this function to use async/await."}
])
print(resp.choices[0].message.content)
Enter fullscreen mode Exit fullscreen mode

Check the report anytime:

python -c "from meter import report; report(10_000_000)"
Enter fullscreen mode Exit fullscreen mode

The output shows the daily burn rate and a projected exhaustion date. Run it once a day, or add a cron job that emails the report when the projected days-left drops below a threshold.

Reading the numbers

Three patterns matter.

  • Steady burn below 25% of the allowance per month. The free tier is a reasonable fit. Keep monitoring, but no action needed.
  • Burn rate above 25% per month. The allowance will not last. Start evaluating paid APIs or self-hosting before the quota hits zero.
  • Burn rate accelerating. Compare this week to last week. If the rate is climbing, the workload changed — a new script, a new editor plugin, a teammate joining the project. Find the source before it becomes a surprise.

Limitations

The meter only sees requests that go through the wrapped client. Editor extensions, other scripts, or CI jobs that call the API directly bypass the recording. A complete view requires integrating the meter into every entry point, or reading server-side usage logs if the provider exposes them.

The prediction is a linear extrapolation. Bursty workloads — a large refactor, a batch review of a pull request — can push actual consumption far above the forecast. Treat the meter as an early warning system, not a billing audit.

Who should not use this approach

  • One-off experimenters. A single weekend of testing will not exhaust a 10-million-token allowance. The meter is overhead.
  • Paid API users. No quota, no need for a meter.
  • Teams that need exact billing. This tool is an approximation. It does not capture prompt caching, retries, or provider-side adjustments.

Conclusion

The quota problem on a free AI coding server is not the size of the allowance. It is the visibility. A thirty-line meter turns "how much is left" from a guess into a reading. Collect data for a few days, then decide whether the free tier, a paid API, or a self-hosted setup is the right host. The numbers decide; the meter just makes them visible.

MonkeyCode provides free models that can run this workflow.

Source: dev.to

arrow_back Back to Tutorials