Nearglass
Operate

Usage and spend caps

Viewer-hours and GiB, the monthly spending cap, and the alerts with their signatures.

What your streams used, what it costs this month, and the cap that keeps a surprise from becoming a bill.

What is counted

UnitFromNotes
Viewer-hourseach viewer session's lengthsessions under 10 s count no time (a join that never saw a picture)
GiBthe bytes our relays sent, every rolethe bill can be checked against it: egress is most of the cost
Sessionseach closed sessiona page opens a new session about every 11 minutes, so one viewer is several sessions

MoQ viewers are recorded by the relays, LL-HLS viewers by our HLS edge, per seat, into the same totals; the answer shows the two apart. A session is counted on the UTC day it ended, so a viewer watching now shows up when their session closes.

Reading it

curl -s "$API/v1/usage?from=2026-10-01&to=2026-10-03" -H "Authorization: Bearer $SK"

from and to are UTC days (default: the 1st of this month to today, at most 92 days a call); stream=st_... narrows it to one stream.

Illustrative example. The values below are made up to show the shape; they are not a measurement.

{
  "tenant": "tn_...", "from": "2026-10-01", "to": "2026-10-03",
  "days": [{
    "date": "2026-10-03", "viewer_hours": 41.2, "gib": 69.1, "bytes": 74196824064, "sessions": 412, "viewer_sessions": 398,
    "moq": { "viewer_hours": 39.8, "viewer_sessions": 380, "bytes": 71635353600 },
    "hls": { "viewer_hours": 1.4, "viewer_sessions": 18, "bytes": 2561470464 },
    "streams": [{ "stream": "st_8f2c...", "namespace": "live/st_8f2c...", "viewer_hours": 41.2, "...": "..." }]
  }],
  "total": { "viewer_hours": 41.2, "gib": 69.1, "...": "..." },
  "month": {
    "month": "2026-10", "viewer_hours": 41.2, "gib": 69.1, "bytes": 74196824064, "sessions": 412,
    "free_viewer_hours": "...", "free_gib": "...",
    "prices": { "per_gib_usd": "...", "per_viewer_hour_usd": "..." },
    "spend_usd": "...", "spend_cap_usd": 50, "percent_of_cap": "...", "cap_reached": false,
    "alerts": [{ "percent": 50, "at": "2026-10-12T08:14:03Z" }]
  }
}

(The usage numbers are an illustration of the shape, not a measurement; month also carries the free allowance, the prices and the spend, which are the pricing decision above.) A stream you deleted shows as ns:<namespace>. GET /v1/tenant shows your account: its limits, its cap (spend_cap_usd) and the same month block. month.alerts lists the spending alerts sent this month, oldest first, with when each went out (a new cap re-arms the levels, so a level can appear twice).

On a self-hosted deployment all of this comes from nearglass-meter shipping each relay's session log. Without it every number stays at zero and no cap ever triggers.

The spending cap

Every account has a monthly cap (50 USD by default during the pilot; a self-hoster sets NEARGLASS_API_DEFAULT_SPEND_CAP). Set your own, up to the most your account allows (500 USD unless the operator raised it, or NEARGLASS_API_MAX_SPEND_CAP); above that is 409 above_max_spend_cap:

curl -s -X PATCH $API/v1/tenant -H "Authorization: Bearer $SK" -d '{"spend_cap_usd": 80}'

0 allows the free allowance only: any spend at all reaches it. A new cap starts the alerts over. At the cap (spend equal to or over it):

  • New viewers are refused: viewer-tokens and the public waiting room answer 402 spend_cap_reached (with illustrative figures: this month's spending cap is reached (50.12 of 50.00 USD): no new viewers until it is raised (PATCH /v1/tenant) or the month ends; viewers already in keep watching), and the player shows "Cannot play".
  • Viewers already watching keep watching, to the end: a seat already held still gets its tokens.
  • Publishers and controllers are not refused: the viewers already in need the stream.

So the spend can pass the cap by what the viewers already in go on to watch, plus up to a minute of reporting delay. Raising the cap lets new viewers in again at once; a new month starts from zero.

Alerts

Set a webhook and we POST to it at 50 %, 80 % and 100 % of the cap, once each a month (a new cap re-arms them):

curl -s -X PATCH $API/v1/tenant -H "Authorization: Bearer $SK" -d '{"webhook_url": "https://example.com/hooks/video"}'

The URL must be a public address. The answer holds webhook_secret once. Each alert:

Illustrative example. The values below are made up to show the shape; they are not a measurement.

{ "type": "spend.threshold", "tenant": "tn_...", "month": "2026-10", "percent": 80,
  "spend_usd": 40.1, "spend_cap_usd": 50, "at": "2026-10-21T09:14:03Z", "note": "" }

type is spend.cap_reached at 100 %, and its note says new viewers are refused until the cap is raised or the month ends. The header X-Nearglass-Signature: t=<unix seconds>,v1=<hex> signs it. We try three times (at once, after 2 s, after 10 s). The signature check, with working code in Node.js and Python, the retry rules and the other events are on Webhooks.

"webhook_url": null removes the webhook.

Live numbers

GET /v1/streams/{id}/stats is the stream now: seats taken, people in line, the cap, the peak, LL-HLS seats, turns, and when the ingest last connected. It is for a dashboard polling every few seconds; the player never calls it. Its fields are on Monitoring.

What was run, and what was not

Run on one host from the packages: a cap set to 0, a publisher session shipped by nearglass-meter, GET /v1/usage and GET /v1/tenant showing the spend and cap_reached, 402 spend_cap_reached on viewer-tokens, the alerts at 50, 80 and 100 % arriving signed, 409 above_max_spend_cap for a cap over the maximum, a new cap re-arming the alerts, and a webhook on a loopback address refused.

On this page