Metrics API
Account and API token · Plain-text docs for agents · OpenAPI schema
# Counter metrics API
Base URL: https://counter.expt.sh
## Authentication
Log in at https://counter.expt.sh/account and copy your API Token.
Set COUNTER_API_TOKEN in your agent's secret environment. Every metrics request
requires Authorization: Bearer <token>. Never put the token in URLs or prompts.
Tokens do not expire automatically and grant full account access, including writes
and account deletion; these are not read-only credentials. Queries only access the
authenticated account's metrics.
## Discover hosts
curl --fail-with-body 'https://counter.expt.sh/api/counter/hosts' \
-H "Authorization: Bearer $COUNTER_API_TOKEN"
Returns {"hosts": [...]} with hostname and last_seen for each registered host.
Use the exact hostname from this response in metric queries.
## Query metrics (GET)
curl --fail-with-body --get 'https://counter.expt.sh/api/counter/metrics' \
-H "Authorization: Bearer $COUNTER_API_TOKEN" \
--data-urlencode 'metric_type=system' \
--data-urlencode 'hostname=my-server' \
--data-urlencode 'last=1h'
## Query metrics (POST)
The same parameters can be sent as JSON to the existing query endpoint:
curl --fail-with-body 'https://counter.expt.sh/api/counter/query' \
-H "Authorization: Bearer $COUNTER_API_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"metric_type":"system","hostname":"my-server","last":"1h"}'
## Parameters (both query endpoints)
- metric_type (required): system, disk, network, process, or container.
- hostname (optional): exact host name. Omit to query all your registered hosts.
- last (default 1h): relative range, e.g. 30m, 24h, 7d. Units: m, h, d.
- start and end (optional): provide both ISO 8601 timestamps to replace last,
e.g. 2026-09-17T00:00:00Z and 2026-09-17T01:00:00Z. Use UTC timestamps.
- mountpoint: exact disk mountpoint; applies to disk only.
- container_name: exact container name; applies to container only.
- interface: exact network interface name, e.g. eth0; applies to network only.
- process_name: exact process name; applies to process only.
- process_pid: process ID as a string; takes precedence over process_name.
- aggregate (default false): for network, sum interfaces at each timestamp.
- summary (default false): return one row per process (name, pid), container,
or network interface for the whole range instead of raw samples. Requires
hostname and metric_type process, container, or network. cpu_percent and
memory_percent are averages over all sample intervals (absent = 0%), with
cpu_max/mem_max peaks. Byte and packet counters are totals for the range.
The response also has "intervals", the number of sample timestamps. At most
1,000 rows, highest CPU (or traffic) first. Not affected by the row limit.
## Response and limits
Both endpoints return {"rows": [...]} ordered by timestamp, newest first.
Each row includes hostname, timestamp, and the collector's metric fields.
For example, a system row can contain cpu_percent and memory_used.
See https://counter.expt.sh/docs for collected metric fields.
An empty rows array means no matching data was returned.
Metrics are retained for 30 days. The response limit scales with the requested
range, from 1,000 to at most 50,000 rows across all hosts. Process/container
and interface filters raise the minimum to 5,000. There is no pagination or total count.
Query individual hosts and smaller start/end windows for large datasets.
Process filters run after the row limit, so narrow time windows are recommended.
Multi-host queries skip hosts with telemetry errors and may return partial data;
query each host separately when completeness matters.
## Errors
- 401: missing or invalid Bearer token.
- 400: unsupported metric_type.
- 422: missing required parameter or invalid parameter type.
- 502: a single-host query failed at the telemetry backend; retry with backoff.
Machine-readable API schema: https://counter.expt.sh/openapi.json
Agent documentation: https://counter.expt.sh/docs/api.md