DEV Community

Jordan Huang
Jordan Huang

Posted on

Your Free Model Quota Is a Bank Account. Build a Budget Proxy on a Free Server

Your team got a free model API key last month. Everyone loved it. Then, one morning, every call started returning 429. No warning. No dashboard. No idea which project burned the quota.

I've been there. Free tiers are generous until they aren't. The fix isn't another monitoring SaaS. It's a tiny proxy that counts tokens before they vanish.

This article walks you through a reproducible budget proxy. You'll run it on a free server, give it a daily token limit, and watch it reject expensive calls automatically. All with open-source Python.

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

MonkeyCode offers free models and a free server tier for experiments like this. That means you can test the whole workflow below without paying for infrastructure.

Why a Budget Proxy?

Raw API calls are dangerous. Every dev on your team sends their own prompts. Some are small. Some dump a 50k-token context. You have no central place to say: “we've spent enough today.”

The proxy sits between your code and the model API. It sees every request, reads the response usage, stores the running total, and blocks new calls when the daily cap is hit. Simple, cheap, auditable.

Step 1: Understand the usage Field

Most OpenAI-compatible endpoints return a usage object in each response. It looks like this:

{
  "usage": {
    "prompt_tokens": 1200,
    "completion_tokens": 350,
    "total_tokens": 1550
  }
}
Enter fullscreen mode Exit fullscreen mode

That total_tokens is your meter. The proxy reads it and adds it to a local counter. No guessing, no heuristics.

Step 2: Build the Proxy

We'll use Flask because it's minimal. Save this as budget_proxy.py:

import json
import os
import time
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)

# Change these constants for your environment
DAILY_BUDGET = 500_000  # tokens
USAGE_FILE = "usage.json"


def load_usage():
    """Return today's spent tokens and the date string."""
    today = time.strftime("%Y-%m-%d")
    if os.path.exists(USAGE_FILE):
        with open(USAGE_FILE) as f:
            data = json.load(f)
        if data.get("date") == today:
            return data["spent"]
    return 0


def save_usage(spent):
    with open(USAGE_FILE, "w") as f:
        json.dump({"date": time.strftime("%Y-%m-%d"), "spent": spent}, f)


@app.route("/v1/chat/completions", methods=["POST"])
def proxy():
    spent = load_usage()
    if spent >= DAILY_BUDGET:
        return jsonify({"error": "Daily budget exceeded"}), 429

    payload = request.json
    # Forward to your model endpoint. Replace URL with the real one.
    upstream = "https://api.example.com/v1/chat/completions"
    headers = {"Authorization": request.headers.get("Authorization")}

    try:
        resp = requests.post(upstream, json=payload, headers=headers, timeout=60)
        data = resp.json()
        usage = data.get("usage", {})
        tokens = usage.get("total_tokens", 0)
        if tokens:
            save_usage(spent + tokens)
        return jsonify(data), resp.status_code
    except requests.exceptions.Timeout:
        return jsonify({"error": "Upstream timeout"}), 504


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
Enter fullscreen mode Exit fullscreen mode

The proxy stores usage in a JSON file. For a low-traffic personal setup, that's enough. If you expect many concurrent requests, swap JSON for SQLite.

Step 3: Add a Reset Policy

The load_usage() function already resets the counter when the date changes. The first call of each day starts from zero. That's the default behavior. If you need a rolling 24-hour window instead, adjust the logic with a timestamp instead of a date string.

Step 4: Test Locally

Run the proxy:

python budget_proxy.py
Enter fullscreen mode Exit fullscreen mode

In a second terminal, send a fake request:

curl http://localhost:5000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_KEY" \
  -d '{"model": "gpt-3.5-turbo", "messages": [{"role": "user", "content": "Hello"}]}'
Enter fullscreen mode Exit fullscreen mode

You'll get a response from the upstream API (or a timeout error if the endpoint is invalid). Check usage.json:

cat usage.json
Enter fullscreen mode Exit fullscreen mode

You should see today's date and the token count from your test.

Step 5: Deploy to a Free Server

MonkeyCode's free server tier runs small Python apps like this without a container registry. Clone your repo, set up the environment, and start the process.

Here's a minimal requirements.txt:

flask==2.3.3
requests==2.31.0
Enter fullscreen mode Exit fullscreen mode

Then run:

pip install -r requirements.txt
gunicorn -w 2 -b 0.0.0.0:8000 budget_proxy:app
Enter fullscreen mode Exit fullscreen mode

Gunicorn gives you multiple workers. Note: the JSON file approach has a race condition when two workers write at once. For a serious deployment, switch to SQLite or a simple lock.

Step 6: Point Your Code at the Proxy

Now, every client should call your proxy URL instead of the model API directly. In OpenAI's Python SDK:

from openai import OpenAI

client = OpenAI(
    base_url="https://your-proxy.example.com/v1",
    api_key="your-api-key"
)

response = client.chat.completions.create(
    model="your-model",
    messages=[{"role": "user", "content": "Hello"}]
)
Enter fullscreen mode Exit fullscreen mode

That's it. Every request now passes through your budget gate.

What This Proxy Does NOT Do

  • It doesn't sanitize prompts or block prompt injection.
  • It doesn't rotate API keys.
  • It doesn't give you a metrics dashboard.
  • It doesn't handle upstream rate limits separately.

If you need those, extend the proxy or pair it with a proper gateway.

When Should You Not Use This?

Skip the proxy when:

  • You already have an enterprise gateway with usage tracking.
  • You're building a high-throughput production service where latency and reliability matter.
  • Your free server is too small to handle the extra hop.

For experiments, internal tools, and small teams, it's perfect.

Final Thoughts

A free model quota disappears faster than you expect. The trick is to see it coming. This proxy gives you a daily stop-loss without adding complex infrastructure.

Try it with MonkeyCode's free model access and free server. Deploy the proxy, set a conservative budget, and let it protect you from your own over-eager prompts. Your future self will thank you.

Top comments (0)