# Get device health

> Heartbeat-derived health of one device. A device is declared offline after 3 × heartbeat_interval_s without a heartbeat; uptime = received heartbeats / expected heartbeats.

`GET https://api.cymatics.tech/v1/devices/{device}/health`

Scope: `results:read`

## Request

Send your key as `Authorization: Bearer cym_test_…`. Requires the `results:read` scope. Test keys read the simulator; live keys read your paired Cymos.

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `device` (SDK and CLI: `device_id`) | path | `string` | Yes | Device id |
| `from` | query | `string (date-time)` | No | From |
| `to` | query | `string (date-time)` | No | To |

## Response

`200 · application/json`

```json
{
  "data": {
    "object": "device.health",
    "device_id": "dev_bedroom",
    "timestamp": "2026-10-24T23:55:30.000Z",
    "connection_status": "online",
    "last_seen_at": "2026-10-24T23:55:30.000Z",
    "heartbeat_interval_s": 1,
    "uptime": {
      "ratio_24h": 0,
      "ratio_30d": 0,
      "since": "2026-10-24T23:55:30.000Z"
    },
    "health": {
      "status": "healthy",
      "reasons": [
        "reason"
      ]
    },
    "metrics": {
      "signal_quality": {
        "value": 0,
        "timestamp": "2026-10-24T23:55:30.000Z"
      },
      "dropped_frame_ratio": 0,
      "clock_offset_ms": 1,
      "frame_rate_hz": 0
    },
    "window_start": "2026-10-24T23:55:30.000Z",
    "window_end": "2026-10-24T23:55:30.000Z",
    "connection_history": [
      {
        "status": "online",
        "from": "2026-10-24T23:55:30.000Z",
        "to": "2026-10-24T23:55:30.000Z"
      }
    ]
  },
  "meta": {
    "request_id": "req_01",
    "project_id": "proj_demo",
    "livemode": false,
    "generated_at": "2026-10-24T23:55:30.000Z"
  }
}
```

## Errors

| Status | Description |
| --- | --- |
| `429` | `rate_limit_exceeded`: Too many requests this minute for the key (or twice that across the project), or too many open streams for the project. Retry after the seconds in Retry-After.<br>`quota_exceeded`: A live key's project has used its monthly live-hour cap. There is no Retry-After. Don't retry; raise the live-hour cap in the dashboard. Headers: Retry-After, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset. |
| `503` | `live_unavailable`: This operation has no live data for this project yet (for example, no paired Cymo covers it). Don't retry the same request; pair a Cymo that covers it, or build against a test key.<br>`service_unavailable`: A dependency, such as API key verification, is unreachable, or the project is busy with another write. Retry after the seconds in Retry-After, with backoff. |
| `4XX` | Invalid request, credential, permission or revision. |
| `default` | Error with a stable code and request ID. |

Every error is a `{ error, meta }` envelope with a stable `code`. See [Errors](https://docs.cymatics.tech/errors) for each code and whether to retry.

## Call it

**TypeScript · @cymos/sdk**

```ts
import { Cymo } from "@cymos/sdk";

const cymo = new Cymo({ apiKey: process.env.CYMO_API_KEY! });

const { data, meta } = await cymo.devices.health({ device_id: "dev_bedroom" });
```

**terminal · cymatics CLI**

```sh
$ cymatics devices health --device-id dev_bedroom --json
```

**curl**

```sh
curl "https://api.cymatics.tech/v1/devices/dev_bedroom/health" \
  -H "Authorization: Bearer $CYMO_API_KEY"
```

**MCP tool**

```
get_device_health({ device_id: "dev_bedroom" })
```

The MCP tool `get_device_health` calls this operation with your key.
