RackAtlas API
A read-only JSON API for the data RackAtlas collects about your servers: current state, metrics history, systemd services, backups and alerts. It is made for dashboards. Grafana can read it directly, see the Grafana guide.
The base URL of every endpoint is:
https://rackatlas.dev/api/v1Create a key in Settings → Teams → your team → API keys. Then try it:
export RACKATLAS_KEY=rak_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
curl https://rackatlas.dev/api/v1/me \
-H "Authorization: Bearer $RACKATLAS_KEY"Authentication
Send the key of your team as a bearer token in the Authorization header of every request. A key belongs to one team and sees only the data of that team.
Authorization: Bearer rak_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx- A key is shown once, when you create it. RackAtlas keeps only a hash, so a lost key cannot be recovered. Create a new one and revoke the old one.
- A key can expire (30, 90 or 365 days) or live until you revoke it. A revoked or expired key answers
401. - Use one key per integration, so you can revoke one without breaking the others. The page of the keys shows when each was last used.
- Keep keys out of dashboards that are shared publicly and out of source control.
Scopes
A key gets only the scopes you tick when you create it. A request to an endpoint whose scope the key lacks answers 403. All scopes are read-only.
| Scope | Allows |
|---|---|
servers:read | List and read servers with their latest sample |
metrics:read | Metrics and disk history of a server |
applications:read | Systemd services of the servers |
backups:read | Backups and the result of their last run |
alerts:read | Open and resolved alerts |
Conventions
- JSON only. Every response, errors included, is
application/json. A client that accepts nothing else than another format gets406, and a request body that is not JSON gets415. - Read-only. Only
GETis supported. Other methods answer405. - Envelope. Data is in the
datakey. Time series add ametaobject with the range and bucket size that was applied. - Times are ISO 8601 with an offset, for example
2026-11-05T13:41:02+00:00. Sizes are in bytes, rates in bytes per second, percentages are 0 to 100. - Time series. Allowed intervals are
60,300,900,3600,21600,86400seconds. A request may span at most 31 days and 5000 points. The history is kept for 30 days.
Errors
Errors carry a message. Validation errors (422) also list the invalid fields under errors.
| Status | Meaning |
|---|---|
401 | The key is missing, wrong, revoked or expired. |
403 | The key lacks the scope of the endpoint. |
404 | No such endpoint, or the server does not belong to your team. |
405 | Only GET is supported. |
406 | The client does not accept application/json. |
415 | The request body is not application/json. |
422 | A parameter is invalid. |
429 | Too many requests, see Retry-After. |
{
"message": "The selected interval is invalid.",
"errors": { "interval": ["The selected interval is invalid."] }
}Rate limits
A key may send 120 requests per minute. Wrong keys are limited per IP address: after 20 failed attempts in a minute the address is blocked for the rest of that minute. When a limit is hit you get 429 and a Retry-After header with the seconds to wait.
Servers report once a minute, so polling more often than that returns the same data. A dashboard refresh of 30 to 60 seconds is plenty.
Who am I
/api/v1/meany keyReturns the team the key belongs to and the scopes it has. Needs no scope, so it is the best first call and a good health check for a data source.
curl https://rackatlas.dev/api/v1/me \
-H "Authorization: Bearer $RACKATLAS_KEY"{
"data": {
"team": { "id": 3, "name": "Acme" },
"key": {
"name": "Grafana",
"scopes": ["servers:read", "metrics:read"],
"expires_at": null
}
}
}List servers
/api/v1/serversservers:readAll servers of the team with their latest sample. Servers that never reported have "current": null.
curl https://rackatlas.dev/api/v1/servers \
-H "Authorization: Bearer $RACKATLAS_KEY"{
"data": [
{
"id": 12,
"name": "web-1",
"category": "Production",
"provider": "Hetzner",
"country": "DE",
"host": "203.0.113.10",
"status": "online",
"agent_version": "1.6",
"system_info": { "os": "Debian GNU/Linux 12", "kernel": "6.1.0" },
"last_seen_at": "2026-11-05T13:41:02+00:00",
"last_checked_at": "2026-11-05T13:41:02+00:00",
"current": {
"collected_at": "2026-11-05T13:41:02+00:00",
"cpu_percent": 12.4,
"cpu_cores": 4,
"load_1": 0.42,
"load_5": 0.38,
"load_15": 0.31,
"memory_total_bytes": 8321486848,
"memory_used_bytes": 3120562176,
"memory_percent": 37.5,
"swap_total_bytes": 0,
"swap_used_bytes": 0,
"uptime_seconds": 864000,
"net_rx_bps": 18200,
"net_tx_bps": 40100,
"disks": [
{
"mount": "/",
"device": "/dev/vda1",
"filesystem": "ext4",
"total_bytes": 85899345920,
"used_bytes": 31457280000,
"used_percent": 36.6
}
]
}
}
]
}Get a server
/api/v1/servers/{id}servers:readOne server, in the same shape as an item of the list. A server of another team answers 404.
| Parameter | Type | Description |
|---|---|---|
id | integer, path | The id of the server. |
curl https://rackatlas.dev/api/v1/servers/12 \
-H "Authorization: Bearer $RACKATLAS_KEY"{ "data": { "id": 12, "name": "web-1", "status": "online", "current": { "...": "..." } } }Metrics history
/api/v1/servers/{id}/metricsmetrics:readCPU, memory, load and network of one server as one flat row per time bucket. Rows are averaged (and maximums kept) inside each bucket. Buckets without samples are left out.
| Parameter | Type | Description |
|---|---|---|
from | datetime, optional | Start of the range, ISO 8601. Default: a day before "to". |
to | datetime, optional | End of the range, ISO 8601. Default: now. |
interval | integer, optional | Bucket size in seconds. Default: the smallest allowed value that keeps the range readable. |
curl https://rackatlas.dev/api/v1/servers/12/metrics?from=2026-11-04T00:00:00Z&interval=300 \
-H "Authorization: Bearer $RACKATLAS_KEY"{
"data": [
{
"time": "2026-11-04T00:00:00+00:00",
"samples": 5,
"cpu_percent_avg": 11.8,
"cpu_percent_max": 24.1,
"memory_percent_avg": 37.2,
"memory_percent_max": 37.6,
"memory_used_bytes_avg": 3095000000,
"swap_percent_avg": null,
"load_1": 0.4,
"load_5": 0.38,
"load_15": 0.31,
"net_rx_bps_avg": 17800,
"net_rx_bps_max": 61000,
"net_tx_bps_avg": 39500,
"net_tx_bps_max": 120000
}
],
"meta": {
"from": "2026-11-04T00:00:00+00:00",
"to": "2026-11-05T13:45:00+00:00",
"interval_seconds": 300
}
}Disk history
/api/v1/servers/{id}/metrics/disksmetrics:readDisk usage of one server in long format: one row per mount point and time bucket. Partition or filter by "mount" in Grafana. Takes the same parameters as the metrics history.
curl https://rackatlas.dev/api/v1/servers/12/metrics/disks?interval=3600 \
-H "Authorization: Bearer $RACKATLAS_KEY"{
"data": [
{
"time": "2026-11-05T12:00:00+00:00",
"mount": "/",
"used_bytes": 31457280000,
"total_bytes": 85899345920,
"used_percent": 36.6
}
],
"meta": { "from": "...", "to": "...", "interval_seconds": 3600 }
}Applications
/api/v1/applicationsapplications:readThe systemd services of the servers that run the agent, with their state as the agent last reported it.
| Parameter | Type | Description |
|---|---|---|
server_id | integer, optional | Only the services of this server. |
status | string, optional | One of failed, running, stopped or disabled. |
curl https://rackatlas.dev/api/v1/applications?status=failed \
-H "Authorization: Bearer $RACKATLAS_KEY"{
"data": [
{
"id": 481,
"server_id": 12,
"server_name": "web-1",
"unit": "php8.5-fpm.service",
"description": "The PHP 8.5 FastCGI Process Manager",
"status": "failed",
"active_state": "failed",
"sub_state": "failed",
"unit_file_state": "enabled"
}
]
}Backups
/api/v1/backupsbackups:readThe backups of the team and how their last run went. "overdue" is true when no run was reported within the expected interval. The secret webhook address of a backup is never part of the answer.
curl https://rackatlas.dev/api/v1/backups \
-H "Authorization: Bearer $RACKATLAS_KEY"{
"data": [
{
"id": 5,
"name": "Database dump",
"server_id": 12,
"server_name": "web-1",
"is_active": true,
"expected_interval_hours": 24,
"last_run_at": "2026-11-05T03:00:11+00:00",
"last_status": "success",
"last_message": null,
"last_size_bytes": 482934112,
"overdue": false
}
]
}Alerts
/api/v1/alertsalerts:readProblems RackAtlas detected on the servers of the team, newest first (at most 500).
| Parameter | Type | Description |
|---|---|---|
status | string, optional | open (default), resolved or all. |
server_id | integer, optional | Only the alerts of this server. |
curl https://rackatlas.dev/api/v1/alerts?status=open \
-H "Authorization: Bearer $RACKATLAS_KEY"{
"data": [
{
"id": 77,
"server_id": 12,
"server_name": "web-1",
"type": "resource",
"key": "disk:/",
"context": { "percent": 93.1 },
"opened_at": "2026-11-05T11:20:00+00:00",
"resolved_at": null,
"open": true
}
]
}Grafana
Use the Infinity data source, which reads JSON from any HTTP API. You need no Prometheus or database in between.
- Create a key with the scopes
servers:readandmetrics:read(add the others for the panels you want). - In Grafana, install the Infinity plugin and add a data source. Under Authentication choose Bearer token and paste the key.
- Under Allowed hosts add
https://rackatlas.dev. - Press Save & test. You can set the health check URL to
https://rackatlas.dev/api/v1/me.
A panel with the CPU of one server:
Type JSON, Parser Backend, Format Time series
URL https://rackatlas.dev/api/v1/servers/12/metrics?from=${__from:date:iso}&to=${__to:date:iso}
Rows / root data
Columns time (Timestamp), cpu_percent_avg (Number), memory_percent_avg (Number)
The from and to variables follow the time picker of the dashboard. Leave interval out and RackAtlas picks a bucket that suits the range. For tables of servers, services, backups or alerts use the endpoints above with the format Table. To draw one line per disk, use the disk history and add the mount column to Group by.
Questions or something missing? Back to RackAtlas