Authentication
Send an API key as a bearer token. This endpoint does not state a specific permission in the specification, so give your key the least it needs and check the response rather than assuming.
This endpoint takes no organisation id. Your key already identifies the organisation it belongs to, and the response is scoped to it.
Try it
Replace anything in angle brackets with your own values, and the key placeholder with a key from your dashboard.
curl -X GET https://api.zinndigital.com/v1/sites/{siteId}/metrics \
-H "Authorization: Bearer zdk_live_…"Signed in? The API console in your dashboard fills in your real organisation id and your own key, and runs the request against the live API so you can see the actual response. Open this endpoint in the API console
Details
Point-in-time usage for the Overview panel — disk, bandwidth, visits, the PHP version, the TLS state, and a short daily bandwidth series for the usage chart. RLS-scoped to a site the caller can view (`sites.view`); an out-of-scope or unknown id is a `404`, exactly like `getSite`. **Collection is asynchronous.** Metrics are gathered off the request path (a scheduled pull from the site's fleet worker into a cached store — never a live per-request driver call, CLAUDE.md §2.16) and read back here. Until a sample has been collected — a freshly-provisioned or suspended site, or a site on a worker not yet reporting — `available` is `false`, `collected_at` is `null` and the usage counters are `null`; the shape is stable so the client renders "usage not yet available" without a contract change once the collector populates it.
Parameters
| Name | Type | Required | What it is |
|---|---|---|---|
siteId (path) | Uuid | Yes | Site ID (UUIDv7). |
Response
| Name | Type | Required | What it is |
|---|---|---|---|
available | boolean | Yes | Whether a metrics sample has been collected for this site yet. |
disk_used_mb | object | Yes | Disk the site is using, in MB, from the newest stored hourly reading. `null` when nothing has measured it yet **or** when the newest reading is too old to present as current — t… |
disk_limit_mb | object | Yes | The disk ceiling ENFORCED on the box, in MB, as last stored. `null` when no ceiling is recorded. ⛔ Not the plan allowance — see `getSiteQuota`, which returns both separately bec… |
bandwidth_mb | object | Yes | Bandwidth used in the current period. |
visits | object | Yes | — |
php_version | string | No | The PHP version the site runs; empty when not applicable/unknown. |
serving_ok | object | No | Whether the site actually answered over HTTPS **from outside the box** at `serving_checked_at`, established by the recurring serving sweep rather than on this request. `null` me… |
serving_checked_at | object | No | When a DEFINITE serving verdict was last obtained; `null` until one has been. Deliberately separate from the sweep's rotation cursor, which advances for every site the sweep rea… |
serving_detail | string | No | The short status line behind `serving_ok` — `"HTTP 522"`, `"ConnectTimeout"`. Never a raw vendor response body. |
ssl_status | string<active, pending, none, unknown> | No | The site's TLS certificate state. |
ssl_expires_at | object | No | — |
bandwidth_series | integer[] | Yes | Recent daily bandwidth samples (MB), oldest first — for the usage chart. Empty when unavailable. |
collected_at | object | Yes | When this sample was collected; null when unavailable. |
error_rate | SiteErrorRate | Yes | HTTP and PHP errors for one site over the newest closed collection window (#626). What `features/uptime-monitoring` sells: *"HTTP and PHP errors captured per site, so a fatal fr… |
Errors this endpoint can return
401 · 403 · 404 · 429