Every metric you see in my.cubepath.com comes from the CubePath GraphQL API. It's the same endpoint the panel uses, and you can query it yourself to build your own dashboards, reports and health checks.
POST https://api.cubepath.com/graphql
It's read-only and covers metrics and traffic: VPS, availability groups, baremetal (traffic, bandwidth and BMC sensors), IP transit, load balancers, NAT gateways and Kubernetes clusters. To create or change resources, use the REST API or the MCP server.
Authenticate
Use an API token in either header:
curl https://api.cubepath.com/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{"query":"{ vpsList { id name } }"}'
X-API-Key: YOUR_API_TOKEN works too. A token request needs no X-Requested-With header, even though it's a POST.
Each field checks the read scope of its product, so a monitoring token only needs the read scopes:
| Field | Scope |
|---|---|
vps, vpsList, availabilityGroup | vps:read |
baremetal | baremetal:read |
transit | network:read |
loadBalancer | loadbalancer:read |
natGateway | nat_gateway:read |
kubernetesCluster, kubernetesClusters | kubernetes:read |
bandwidthResources, bandwidthAggregate | the scope of each product it includes |
TipCreate a dedicated token with only the read scopes above and pin it to the IP of the machine that polls it. A leaked copy can then read graphs and nothing else.
Your first query
One request can ask for several resources at once, each with exactly the fields you need:
query Overview($id: ID!) {
vps(id: $id) {
name
metrics(range: H6, metrics: [CPU_USAGE, MEMORY_USAGE]) {
step
series { name unit points { ts value } }
}
bandwidthUsage { inBytes outBytes totalBytes }
}
kubernetesClusters {
name
health { up nodesReady nodesTotal podsPending }
}
}
Send it as JSON with query and variables:
curl https://api.cubepath.com/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-d '{"query":"query($id: ID!){ vps(id:$id){ name metrics(range: H6, metrics:[CPU_USAGE]){ step series{ name unit points{ ts value } } } } }","variables":{"id":"1234"}}'
What you can query
| Field | What it returns |
|---|---|
vps(id) / vpsList(projectId) | CPU, memory, disk I/O and network series, plus the month's bandwidth. vpsList returns up to 200 servers |
availabilityGroup(uuid) | Average CPU and total memory across the group, and how many servers it has |
baremetal(id) | Traffic and packets per second, the month's bandwidth, the last 30 days of traffic and the BMC sensors (temperatures, fans, power state) |
transit(id) | Traffic and packets per second of your IP transit port |
loadBalancer(uuid) | Active connections, new connections per second, traffic in and out, and the month's bandwidth |
natGateway(uuid) | Traffic in and out, and the month's bandwidth |
kubernetesCluster(uuid) / kubernetesClusters(projectId) | API server health, every node with its conditions and resources, and the cluster series (nodes ready, pending and failed pods, API latency) |
bandwidthResources / bandwidthAggregate | Every service that moves public traffic and their traffic summed by location, product, project or service. It's what powers the Bandwidth page |
Time ranges
metrics(range:) takes the same ranges as the panel: H1, H3, H6, H12, H24, D3, D7 and D30. The default is H1.
Longer ranges come back with a larger step (the bucket size, in seconds), so a 30-day chart has about 300 points rather than tens of thousands.
Reading a result
{
"data": {
"vps": {
"name": "web-1",
"metrics": {
"step": 30,
"series": [
{ "name": "cpu_usage", "unit": "RATIO", "points": [{ "ts": 1759406400, "value": 0.42 }] }
]
}
}
}
}
tsis a Unix timestamp in seconds, UTC.unittells you how to format the value:RATIOis 0 to 1 (0.42is 42%),BYTES,BYTES_PER_SECOND,PACKETS_PER_SECOND,CONNECTIONS,CELSIUS,RPM,MILLISECONDSand so on.- All the series of one
metricsresult share the same timestamps, so you can plot them on one chart without lining them up yourself.
Monthly bandwidth
bandwidthUsage(month: "2026-09") returns the traffic of that calendar month (UTC). Leave month out for the current month to date. The format is strictly YYYY-MM.
On the Bandwidth fields, inBytes is always traffic into your service and outBytes traffic out of it, whatever the product.
Errors
A request that GraphQL accepts always answers HTTP 200. When one field fails, that field is null and errors explains why, while the rest of the request still resolves:
extensions.code | Meaning |
|---|---|
UNAUTHENTICATED | No token, or the token is invalid |
FORBIDDEN | The token lacks the scope for that product, or the server is suspended |
NOT_FOUND | No such resource in your organization |
BAD_USER_INPUT | Invalid argument, such as a bad month or an unknown field |
METRICS_UNAVAILABLE | Metrics are temporarily unavailable. The resource still resolves and the series come back empty |
QUERY_TOO_COMPLEX | The request asks for too much at once (see the limits below) |
NoteResources in other organizations answer
NOT_FOUND, exactly like an ID that doesn't exist.
Limits
- 600 requests per minute per token. One request counts once, whatever it asks for. Over the limit you get
429withRetry-After. - A single request may return up to about 24,000 points: for example 200 one-hour series, or 80 series over 30 days. Split bigger requests in two.
- Queries up to 6 levels deep, 15 aliases and 2,000 tokens. Batched operation arrays are not accepted.
vpsList,kubernetesClustersand each product inbandwidthResourcesreturn up to 200 items.
Schema and tooling
The schema is open to authenticated callers through standard introspection, so tools like GraphQL Code Generator, Postman or Insomnia can load it with your token. Anonymous introspection is refused.
TipPolling for a dashboard? Ask for every resource in one request with the
metricsyou chart, and reuse the samerangeacross them. It's one request against your rate limit instead of one per chart.