{"id":30877,"plugin_id":"plugins_6abe9a4d0ac0819197f4ad9a145abc45","kind":"skill","collection_source":"plugin_package","comparison_source":null,"observed_at":"2026-10-09T18:03:05.602Z","digest":"dc34f9d16396e8b137120e4aa25c7a4c3ec26e1b37e61c8934c81e95e767fc50","against":null,"payload":{"description":"Fastly traffic numbers: cache hit ratio, bandwidth, request counts, status-code and error rates, edge vs origin traffic, real-time requests-per-second, origin latency, per-domain traffic, account usage and billing totals. Owns the `fastly stats` CLI commands and the Historical Stats, Real-Time and Origin/Domain Inspector HTTP APIs. Use for any question that needs a number about how a Fastly service is performing or how much it is being used.","included_files":[{"relative_path":"references/api.md","size_in_bytes":13137},{"relative_path":"references/debugging.md","size_in_bytes":9494},{"relative_path":"references/fields.md","size_in_bytes":12449}],"name":"fastly-stats","skill_md_contents":"---\nname: fastly-stats\ndescription: \"Fastly traffic numbers: cache hit ratio, bandwidth, request counts, status-code and error rates, edge vs origin traffic, real-time requests-per-second, origin latency, per-domain traffic, account usage and billing totals. Owns the `fastly stats` CLI commands and the Historical Stats, Real-Time and Origin/Domain Inspector HTTP APIs. Use for any question that needs a number about how a Fastly service is performing or how much it is being used.\"\n---\n\n# Fastly stats\n\nPrefer the `fastly` CLI. Drop to `curl` only for the seven things the CLI cannot do, listed under\nRaw API below.\n\nRequires the `fastly` CLI, `jq`, and network access to Fastly APIs; raw API examples also require `curl`.\nUse the user's locally configured authentication.\nIf authentication is missing, direct the user to a local CLI login or local `FASTLY_API_TOKEN` configuration, never to paste a key in chat.\nKeep tokens out of output and avoid shell tracing for authenticated commands.\n\n## Rules that decide whether the answer is right\n\n1. Bytes to GB is decimal SI: `bytes / 1e9`. TB is `/ 1e12`. Never `2^30`. Fastly bills in\n   decimal units, so a GiB figure is wrong by 7.4% and still reads as a plausible number.\n2. For a calendar window, pass explicit UTC boundaries:\n   `--from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z` returns every day bucket in July. A\n   bucket is emitted only when the whole period falls inside the window, and a relative window\n   opens and closes mid-bucket, so `--from \"N days ago\" --by day` returns N-1 buckets, never N,\n   and `\"1 day ago\"` returns none at all. Relative strings are safe at `--by hour`, not at\n   `--by day`.\n3. On the raw API, `from=yesterday` means 12:00:00 UTC, not midnight, and `from=today` means now.\n   `N days ago` / `N hours ago` are exact offsets. Read back `meta.from` / `meta.to`.\n4. `hit_ratio`, `edge_hit_ratio` and `origin_offload` are gauges. Never sum or average them\n   across buckets. Recompute from the summed counters: `hits / (hits + miss)`.\n5. `ts/h` on `rt.fastly.com` covers the last 120 seconds, not an hour, and returns only the seconds\n   that carried traffic. Divide a rate by 120 there, or by the window you bounded when sampling\n   with the CLI; never by the sample count or the `recorded` span. Print the window beside the rate.\n6. `fastly stats ... --json` emits NDJSON, one object per line, no array. Slurp with `jq -s`\n   before aggregating. The raw HTTP API returns a normal array in `data`.\n7. Stats responses omit services with zero traffic in the window. Enumerate from\n   `fastly service list --json` and default sums with `add // 0`.\n8. Do not read the newest bucket. Historical aggregation keeps growing for a few minutes after\n   a period closes.\n9. On a Compute service the traffic lands in `compute_requests` and `requests` stays 0. Summing\n   `requests` alone reports zero traffic for a service that is serving fine. Check both.\n10. Status codes split the same way. Use `all_status_*`, never bare `status_*` or\n    `compute_resp_status_*`: `status_5xx` is 0 on Compute, `compute_resp_status_5xx` is absent on\n    VCL, `all_status_5xx` is right on both. No `all_requests` exists, so denominators still need\n    `requests + compute_requests`.\n\n## Pick the command\n\n| You need                             | Command                                                                    |\n| ------------------------------------ | -------------------------------------------------------------------------- |\n| One service over a past window       | `fastly stats historical -s ID --from T --to T --by day`                   |\n| One field only                       | `fastly stats historical -s ID --field bandwidth`                          |\n| All services, one row of totals      | `fastly stats aggregate --from T --to T --by day`                          |\n| Account usage totals, by region      | `fastly stats usage --from T --to T --json`                                |\n| Account usage split per service      | `fastly stats usage --by-service --json`                                   |\n| Valid region codes                   | `fastly stats regions`                                                     |\n| POP codes and shield names           | `fastly pops`                                                              |\n| Is Inspector enabled on this service | `fastly products -s ID`                                                    |\n| Per-origin metrics, origin latency   | `fastly stats origin-inspector -s ID --downsample hour --metric responses` |\n| Per-domain metrics                   | `fastly stats domain-inspector -s ID --downsample hour --group-by domain`  |\n| Live per-second data                 | `fastly stats realtime -s ID --json`                                       |\n\n`historical`, `aggregate` and `usage` take `--by minute|hour|day` and `--field`. The two\ninspectors take `--downsample` and `--metric` (repeatable) instead, plus `--group-by`,\n`--datacenter`, `--limit`, `--cursor`, and `--domain` or `--host`. Mixing the two vocabularies\nfails with a usage error. `historical` has no `--datacenter`; `realtime` takes no filters at all,\nand `regions` takes no flags whatsoever, not even `--json`. The documented `--metric` cap of 10 is\nnot enforced; 20 names in one call are accepted and echoed in `meta.metric`. Full flag matrix: the\n`fastly-cli` skill's stats reference.\n\nService-scoped subcommands take `-s` / `--service-id` or `--service-name`, falling back to\n`FASTLY_SERVICE_ID` then `fastly.toml`.\n\n## Worked answers\n\nCache hit ratio over a whole month, recomputed from counters rather than averaged:\n\n```bash\nfastly stats historical -s \"$SID\" --by day \\\n  --from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z --json \\\n  | jq -s '(map(.hits)|add // 0) as $h | (map(.miss)|add // 0) as $m\n           | {hits:$h, miss:$m, hit_ratio: (if $h+$m > 0 then $h/($h+$m) else null end)}'\n```\n\n5xx count and share over a month, correct on both service types:\n\n```bash\nfastly stats historical -s \"$SID\" --by day \\\n  --from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z --json \\\n  | jq -s '{requests: (map((.requests // 0) + (.compute_requests // 0))|add // 0),\n            status_5xx: (map(.all_status_5xx // 0)|add // 0)}\n           | . + {pct: (if .requests > 0 then .status_5xx/.requests*100 else null end)}'\n```\n\nBandwidth in GB per service, ranked. Drive the loop from the service list, not from a stats\nresponse, so zero-traffic services are still counted:\n\n```bash\nfastly service list --json | jq -r '.[] | \"\\(.ServiceID)|\\(.Name)\"' | while IFS='|' read -r id name; do\n  gb=$(fastly stats historical -s \"$id\" --by day \\\n        --from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z --json \\\n        | jq -s '([.[].bandwidth] | add // 0) / 1e9')\n  printf '%.3f\\t%s\\n' \"$gb\" \"$name\"\ndone | sort -rn\n```\n\nAccount totals for a month. `fastly stats usage --json` returns one object keyed by region, so sum\nthe leaves. Dropping `compute_requests` here omits every Compute service from the total:\n\n```bash\nfastly stats usage --from 2026-07-01T00:00:00Z --to 2026-08-01T00:00:00Z --json \\\n  | jq '{bandwidth_gb: (([.[].bandwidth]|add)/1e9),\n         requests: ([.[] | .requests + .compute_requests]|add)}'\n```\n\n`billable_units=true` on `GET /stats/usage_by_month` rescales, it does not switch quantity:\n`bandwidth` / 1e9, `requests` and `compute_requests` / 10,000, so a `requests` of `1.4452` means\n14,452. For one month `/stats/usage`, the per-service `/stats` sum and `/stats/usage_by_month` all\nreport the same byte total, so a mismatch is an arithmetic bug, not a billing subtlety.\n\nLive request rate. `fastly stats realtime --json` streams one flat object per second,\n`{recorded, aggregated, datacenter}`, no `Data` wrapper and no `Timestamp`; those exist only on the\nraw `rt.fastly.com` payload. It prints nothing on a quiet service and never exits, so `head -n`\ndeadlocks. Bound it by wall clock and divide by that bound:\n\n```bash\nSECS=20\nOUT=$(mktemp)\nfastly stats realtime -s \"$SID\" --json > \"$OUT\" & P=$!\nsleep \"$SECS\"; kill \"$P\" 2>/dev/null; wait \"$P\" 2>/dev/null\njq -s --argjson w \"$SECS\" \\\n  '{samples: length, window_s: $w,\n    requests: (map((.aggregated.requests // 0) + (.aggregated.compute_requests // 0))|add // 0)}\n   | . + {rps: (.requests / $w)}' \"$OUT\"\nrm -f \"$OUT\"\n```\n\nReport the window beside the rate. Do not derive it from `recorded` min/max: only seconds with\ntraffic are emitted, so on bursty traffic that span is a fraction of what you watched and the rate\ncomes out several times too high. Ratios and same-window comparisons survive a misjudged window;\nextrapolated rates do not.\n\nOne-shot alternative, returns immediately even with no data:\n`GET rt.fastly.com/v1/channel/{id}/ts/h`, the traffic-bearing seconds of the last 120.\n\n## Raw API\n\nSeven things the CLI cannot do. Everything else has a CLI command above.\n\n| Need                                    | Request                                                                          |\n| --------------------------------------- | -------------------------------------------------------------------------------- |\n| Per-POP history on classic stats        | `GET api.fastly.com/stats/service/{id}?datacenter=SJC,LHR&by=day`                |\n| Every service broken out in one call    | `GET api.fastly.com/stats?from=T&to=T&by=day`                                    |\n| One field across every service          | `GET api.fastly.com/stats/field/{field}?from=T&to=T&by=day`                      |\n| Month-to-date billable usage            | `GET api.fastly.com/stats/usage_by_month?year=2026&month=07&billable_units=true` |\n| POP `region` / `stats_region` fields    | `GET api.fastly.com/datacenters`                                                 |\n| Live per-origin or per-domain data      | `GET rt.fastly.com/v1/{origins,domains}/{id}/ts/0`                               |\n| 120 s per-POP snapshot in one call      | `GET rt.fastly.com/v1/channel/{id}/ts/h`                                         |\n\n`datacenter=` is absent from the CLI's SDK input type, not just its flags, so no flag combination\nreaches per-POP history. The two account-wide rows need `curl` because `stats historical` always\nresolves a service ID and errors without one; `fastly stats aggregate` is not a substitute, it sums\nevery service into one series instead of breaking them out.\n\nAuth is the header `Fastly-Key: <token>`.\nUse `fastly auth token` in a shell substitution to supply it directly from the CLI.\n\n```bash\ncurl -sS -H \"Fastly-Key: $(fastly auth token)\" \\\n  \"https://api.fastly.com/stats/service/$SID?from=2026-07-01T00:00:00Z&to=2026-08-01T00:00:00Z&by=day&datacenter=SJC\"\n```\n\nNever run `fastly auth token` standalone in an agent session: captured stdout can bypass its terminal-output guard.\nDo not echo the token or pass `-v` on an authenticated curl call; both expose it in the transcript.\n\nEndpoint paths, parameters and response shapes: [references/api.md](references/api.md).\nField names and aggregation shape: [references/fields.md](references/fields.md).\nErrors, empty data and wrong-scope symptoms: [references/debugging.md](references/debugging.md).\n\n## Scope traps\n\n- `region=` takes `stats_region` values (`usa`, `europe`), not the `region` values from\n  `/datacenters` (`US-East`, `North-America`). Get the live list from `fastly stats regions`.\n- `region=` is ignored on `/stats/usage` and `/stats/usage_by_service`: `meta` echoes it and all\n  eleven regions come back, byte-identical to the unfiltered response. `fastly stats usage --region`\n  filters client-side, so the CLI and the raw URL disagree. Filter usage responses yourself.\n- Sending `region` and `datacenter` together returns HTTP 200 with the POP filter dropped\n  silently: `meta` echoes `region` and omits `datacenter` entirely, and the numbers are\n  whole-region. Never send both, and assert `meta` carries the filter you sent.\n- POP codes are uppercase. A lowercase or unknown code fails loudly with `invalid datacenter`.\n- Origin and Domain Inspector are paid add-ons. When not enabled the endpoints return HTTP 200,\n  `\"status\":\"success\"` and an empty `data` array, which reads exactly like a service with no\n  traffic. Check `fastly products -s ID` before concluding there is nothing to see.\n- A shield POP's `datacenter` entry carries edge-to-shield traffic, not client traffic. Identify\n  shields from the `SHIELD` column of `fastly pops` and label them separately.\n- When diagnosing rather than reporting, pull the per-POP breakdown. A healthy service-wide\n  number routinely hides one POP erroring: `datacenter=` on classic stats, `--group-by datacenter`\n  on the inspectors, the `datacenter` map in real-time.\n\n## Not this skill\n\nCreating or configuring services, backends, VCL or WAF: `fastly-cli` and `fastly`. Raw request\nlogs: stats are pre-aggregated counters, not log lines. NGWAF security events: `fastly-ngwaf`.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}