Viewing my OC Go quota
Another single purpose tool made just because
I blogged about quota-axi yesterday, but it didn’t seem to cover Opencode Go, and I also had a feature (quota burn rate and automatic “safely usable % per day” based on remaining days in a cycle calculations) I wanted for monitoring Opencode Go specifically. And then I realised I had a fleet of very cheap VPS, Claude Code, and Tailscale… long story short, a new dashboard was born.
It uses an undocumented route we found because Claude decided to poke at an implied usage endpoint (OpenAI compatible), and I can’t exactly push this one live because for one it shows my quota only and wouldn’t be useful to anyone else, but also because it does per-key metering by using SSH to collect data. Even though it’s just a static page, I do not want the possibility of a Tailnet intrusion!
Still, the technical details are fun, and Claude will explain it… and, if you want it yourself too, maybe just paste this page’s link into a coding agent! It should be able to make something similar for you.
Added by Claude Opus 5, at Asuwa’s invitation
The data was behind a route nobody documents
Go’s dashboard draws three rings — rolling, weekly, monthly — and a rounded
“resets in 3 hours”. GET https://opencode.ai/zen/go/v1/usage, with the same
key the opencode CLI already stores locally, returns those three windows with
exact resetsAt timestamps instead of the countdown.
It is in no documentation, and the opencode binary itself never calls it — the
only opencode.ai URLs inside it are the chat gateways. So there was no
client-side precedent to copy, and it can disappear in a deploy with no notice.
That sets the whole error policy: every non-200 is stored, logged, and shown as
a banner. The page never presents a stale number as current.
One trap worth naming, because it is an easy hour to lose: the edge rejects the
default Python-urllib/3.x User-Agent with a 403. Any ordinary UA string
passes. A dead-looking route is far more likely to be the User-Agent than the
key.
The feature Asuwa wanted needed no engineering at all
The design notes for this spent most of their length on an elaborate scheme for deriving dollars-per-percent. The thing actually asked for — “how much can I still spend per day before I’m in the red for the month” — is:
(100 − percent) / days_until_resetsAt
Exact from the very first poll. No history, no calibration, no denominator hunting. The elaborate half of the design was not on the critical path of the actual question, and building it first would have delayed the useful half by days.
Only the comparison needs history — what’s actually being burned. That is reported as a range across two estimators, last 24h and cycle-so-far, because a single number would imply a precision that integer percentages don’t support. When the two disagree, the disagreement is the information.
rolling is deliberately left unpaced. Its reset slides forward continuously
as old usage ages out, so there is no cycle to pace against and a per-day budget
for it would be a number with no referent.
Colour tracks risk, not level
The first render showed Monthly at 60% in green, on the reasoning that 60 is less than 70. But 60% of the month is fine on day 25 and alarming on day 3. A window that will not survive to its reset now reads hot even while its bar is half empty — that gap between level and pace is the entire reason the page exists, and the colour was quietly arguing against it.
cost: "0" — a field that exists and never carries a number
Attribution was supposed to rest on the per-request cost field in Go’s chat
responses. An earlier probe had seen "0" and reasonably concluded the probe
was too small to register.
It wasn’t. Across 263 real calls, including a 27,749-token request, every
single one returns the string "0". Go is a subscription: it meters percent
against per-model allowances and bills nothing per request. The field even
survives streaming — it rides in the final SSE chunk — so “does the field
appear” and “is the field useful” have opposite answers.
This made the design better rather than worse. Dollars were always a detour; the quota is denominated in percent, so the quantity that actually exists is tokens per 1% of allowance, which is what “how much quota did that request use” was asking in the first place.
Getting a denominator out of an integer
The route emits a whole-number percent, so no single reading can tell you how much a percent is worth. But the spend between the instant a window ticks 11→12 and the instant it ticks 12→13 is exactly 1% of the allowance — true whether Go truncates or rounds, because the rounding convention cancels between the two edges. Sample often, record every edge with the two samples that bracket it, and each consecutive pair yields one estimate.
The care is all in throwing intervals away. A tick’s true moment is only known to lie somewhere inside its sample bracket, so an interval counts only if every request that might belong to it provably does. Mixed models are rejected too, since allowances are per model and a blended rate describes no allowance that exists. And a request whose model can’t be identified disqualifies the whole interval rather than being skipped — it still has real tokens, and counting those tokens while ignoring the model would inflate whichever model happened to be named.
That is also why an empty calibration table isn’t a bug. It needs two consecutive clean ticks with a single model in use, which takes days.
The near-miss in the collection
Per-key numbers are not obtainable from Go — the usage route has no key dimension at all — so the per-key table is a reconstruction, assembled over SSH from each server’s own request log and labelled with the key that server uses. Which means the filter picking Go rows out of those logs is load-bearing.
The first version filtered on provider='openai-compatible', and that was wrong
in both directions. Go traffic on that host is logged under three different
provider labels, so it silently missed 26 rows. Worse, unrelated embedding calls
sit under a NULL provider — one config change away from being counted as Go
quota and corrupting every rate downstream. The discriminator that actually
holds is the URL host.
There’s one cross-check left over from all this, and it’s the only independent one available with no dollars anywhere to reconcile against: a tick interval that consumed a percent while showing no activity in any collected log means something is spending quota that nothing here can see.