Reference

Budget status API

Read a budget's level from your own code to degrade features before a cap is needed.

A cap is the last resort. Before it, your app can shed cost in steps: pause previews, turn off AI features, serve stale cache, go read-only. The status API tells it when.

Levels

LevelRuleTypical response
okunder 75% and projection under 100%everything on
warn75% or more, or projection 100% or morepause optional background work
critical90% or moredisable expensive features
exceeded100% or moreread-only or static fallback; caps run here too

Levels are estimates, never the invoice.

Forcing a level

Under Graceful degrade in the dashboard, an owner or admin can override a budget’s level to warn, critical or exceeded, and clear it again. The status API and @signalflare/guard serve the override (with overridden: true) until it is cleared, so you can switch your app into its degraded mode by hand and switch it back without touching Cloudflare or waiting for spend to move. Overrides never run cap actions.

Keys

Create a key under Graceful degrade in the dashboard. It is shown once: sf_status_ followed by 48 hex characters. We store only its SHA-256. A status key can read budget levels for your organization and nothing else. Up to 10 per organization.

Request

curl https://api.signalflare.dev/api/v1/status \
  -H "Authorization: Bearer sf_status_…"

Add ?budget=<budget id> for one budget.

{
  "estimate": true,
  "level": "warn",
  "budgets": [
    {
      "id": "…", "name": "Monthly cap", "level": "warn",
      "spendCents": 4012, "projectedCents": 6230, "budgetCents": 5000,
      "pctUsed": 80.2, "evaluatedAt": 1760550000, "stale": false
    }
  ],
  "generatedAt": 1760550120
}
  • level is the worst level across the returned budgets.
  • stale: true means there is no reading for this month yet, or the last one is over an hour old.
  • 401 for a missing or revoked key, 404 for an unknown budget, 429 past 120 requests a minute per key (with Retry-After).
  • Responses are cacheable for 60 seconds. Data only changes once per poll, so cache it.

In a Worker

let cached: { level: string; at: number } | null = null;

async function budgetLevel(env: Env): Promise<string> {
  if (cached && Date.now() - cached.at < 60_000) return cached.level;
  try {
    const res = await fetch('https://api.signalflare.dev/api/v1/status', {
      headers: { Authorization: `Bearer ${env.SIGNALFLARE_STATUS_KEY}` },
    });
    const body = (await res.json()) as { level: string };
    cached = { level: body.level, at: Date.now() };
    return body.level;
  } catch {
    return cached?.level ?? 'ok';
  }
}

Decide per feature whether an outage of the status API should fail open (ok) or closed (exceeded).

Something unclear or missing? hello@signalflare.dev