Fastly stats
Prefer the fastly CLI. Drop to curl only for the seven things the CLI cannot do, listed under
Raw API below.
Requires the fastly CLI, jq, and network access to Fastly APIs; raw API examples also require curl.
Use the user's locally configured authentication.
If authentication is missing, direct the user to a local CLI login or local FASTLY_API_TOKEN configuration, never to paste a key in chat.
Keep tokens out of output and avoid shell tracing for authenticated commands.
Rules that decide whether the answer is right
- Bytes to GB is decimal SI:
bytes / 1e9. TB is/ 1e12. Never2^30. Fastly bills in decimal units, so a GiB figure is wrong by 7.4% and still reads as a plausible number. - For a calendar window, pass explicit UTC boundaries:
--from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Zreturns every day bucket in July. A bucket is emitted only when the whole period falls inside the window, and a relative window opens and closes mid-bucket, so--from "N days ago" --by dayreturns N-1 buckets, never N, and"1 day ago"returns none at all. Relative strings are safe at--by hour, not at--by day. - On the raw API,
from=yesterdaymeans 12:00:00 UTC, not midnight, andfrom=todaymeans now.N days ago/N hours agoare exact offsets. Read backmeta.from/meta.to. hit_ratio,edge_hit_ratioandorigin_offloadare gauges. Never sum or average them across buckets. Recompute from the summed counters:hits / (hits + miss).ts/honrt.fastly.comcovers the last 120 seconds, not an hour, and returns only the seconds that carried traffic. Divide a rate by 120 there, or by the window you bounded when sampling with the CLI; never by the sample count or therecordedspan. Print the window beside the rate.fastly stats ... --jsonemits NDJSON, one object per line, no array. Slurp withjq -sbefore aggregating. The raw HTTP API returns a normal array indata.- Stats responses omit services with zero traffic in the window. Enumerate from
fastly service list --jsonand default sums withadd // 0. - Do not read the newest bucket. Historical aggregation keeps growing for a few minutes after a period closes.
- On a Compute service the traffic lands in
compute_requestsandrequestsstays 0. Summingrequestsalone reports zero traffic for a service that is serving fine. Check both. - Status codes split the same way. Use
all_status_*, never barestatus_*orcompute_resp_status_*:status_5xxis 0 on Compute,compute_resp_status_5xxis absent on VCL,all_status_5xxis right on both. Noall_requestsexists, so denominators still needrequests + compute_requests.
Pick the command
historical, aggregate and usage take --by minute|hour|day and --field. The two
inspectors take --downsample and --metric (repeatable) instead, plus --group-by,
--datacenter, --limit, --cursor, and --domain or --host. Mixing the two vocabularies
fails with a usage error. historical has no --datacenter; realtime takes no filters at all,
and regions takes no flags whatsoever, not even --json. The documented --metric cap of 10 is
not enforced; 20 names in one call are accepted and echoed in meta.metric. Full flag matrix: the
fastly-cli skill's stats reference.
Service-scoped subcommands take -s / --service-id or --service-name, falling back to
FASTLY_SERVICE_ID then fastly.toml.
Worked answers
Cache hit ratio over a whole month, recomputed from counters rather than averaged:
5xx count and share over a month, correct on both service types:
Bandwidth in GB per service, ranked. Drive the loop from the service list, not from a stats response, so zero-traffic services are still counted:
Account totals for a month. fastly stats usage --json returns one object keyed by region, so sum
the leaves. Dropping compute_requests here omits every Compute service from the total:
billable_units=true on GET /stats/usage_by_month rescales, it does not switch quantity:
bandwidth / 1e9, requests and compute_requests / 10,000, so a requests of 1.4452 means
14,452. For one month /stats/usage, the per-service /stats sum and /stats/usage_by_month all
report the same byte total, so a mismatch is an arithmetic bug, not a billing subtlety.
Live request rate. fastly stats realtime --json streams one flat object per second,
{recorded, aggregated, datacenter}, no Data wrapper and no Timestamp; those exist only on the
raw rt.fastly.com payload. It prints nothing on a quiet service and never exits, so head -n
deadlocks. Bound it by wall clock and divide by that bound:
Report the window beside the rate. Do not derive it from recorded min/max: only seconds with
traffic are emitted, so on bursty traffic that span is a fraction of what you watched and the rate
comes out several times too high. Ratios and same-window comparisons survive a misjudged window;
extrapolated rates do not.
One-shot alternative, returns immediately even with no data:
GET rt.fastly.com/v1/channel/{id}/ts/h, the traffic-bearing seconds of the last 120.
Raw API
Seven things the CLI cannot do. Everything else has a CLI command above.
datacenter= is absent from the CLI's SDK input type, not just its flags, so no flag combination
reaches per-POP history. The two account-wide rows need curl because stats historical always
resolves a service ID and errors without one; fastly stats aggregate is not a substitute, it sums
every service into one series instead of breaking them out.
Auth is the header Fastly-Key: <token>.
Use fastly auth token in a shell substitution to supply it directly from the CLI.
Never run fastly auth token standalone in an agent session: captured stdout can bypass its terminal-output guard.
Do not echo the token or pass -v on an authenticated curl call; both expose it in the transcript.
Endpoint paths, parameters and response shapes: references/api.md [blocked]. Field names and aggregation shape: references/fields.md [blocked]. Errors, empty data and wrong-scope symptoms: references/debugging.md [blocked].
Scope traps
region=takesstats_regionvalues (usa,europe), not theregionvalues from/datacenters(US-East,North-America). Get the live list fromfastly stats regions.region=is ignored on/stats/usageand/stats/usage_by_service:metaechoes it and all eleven regions come back, byte-identical to the unfiltered response.fastly stats usage --regionfilters client-side, so the CLI and the raw URL disagree. Filter usage responses yourself.- Sending
regionanddatacentertogether returns HTTP 200 with the POP filter dropped silently:metaechoesregionand omitsdatacenterentirely, and the numbers are whole-region. Never send both, and assertmetacarries the filter you sent. - POP codes are uppercase. A lowercase or unknown code fails loudly with
invalid datacenter. - Origin and Domain Inspector are paid add-ons. When not enabled the endpoints return HTTP 200,
"status":"success"and an emptydataarray, which reads exactly like a service with no traffic. Checkfastly products -s IDbefore concluding there is nothing to see. - A shield POP's
datacenterentry carries edge-to-shield traffic, not client traffic. Identify shields from theSHIELDcolumn offastly popsand label them separately. - When diagnosing rather than reporting, pull the per-POP breakdown. A healthy service-wide
number routinely hides one POP erroring:
datacenter=on classic stats,--group-by datacenteron the inspectors, thedatacentermap in real-time.
Not this skill
Creating or configuring services, backends, VCL or WAF: fastly-cli and fastly. Raw request
logs: stats are pre-aggregated counters, not log lines. NGWAF security events: fastly-ngwaf.

