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
| Level | Rule | Typical response |
|---|---|---|
ok | under 75% and projection under 100% | everything on |
warn | 75% or more, or projection 100% or more | pause optional background work |
critical | 90% or more | disable expensive features |
exceeded | 100% or more | read-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
}
levelis the worst level across the returned budgets.stale: truemeans there is no reading for this month yet, or the last one is over an hour old.401for a missing or revoked key,404for an unknown budget,429past 120 requests a minute per key (withRetry-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).