# Who's Offline Right Now?

**Level:** 15 minutes · the "hello world" of fleet automation

**APIs used:** GET /api/v2/routers/

---
Read https://cradlepoint.ericsson.com/llms.txt and the OpenAPI document at
https://cradlepoint.ericsson.com/developer/api/openapi.json so you know the
Ericsson NetCloud Manager (NCM) API.

Build me a single-file Python script (standard library only) that prints every
router in my account that is currently offline, sorted by how long it has been
down, longest first.

Requirements:
- Call GET https://www.cradlepointecm.com/api/v2/routers/ with state=offline and
  fields=id,name,state_updated_at, so the response only carries what you need.
  Keep the trailing slash: without it the API answers 301 and costs an extra
  call.
- v2 authenticates with four headers: X-CP-API-ID, X-CP-API-KEY, X-ECM-API-ID,
  X-ECM-API-KEY. Read them from the environment variables X_CP_API_ID,
  X_CP_API_KEY, X_ECM_API_ID and X_ECM_API_KEY. Never hardcode them, and exit
  with a clear message naming any that are missing. Also send a descriptive
  User-Agent header (for example ncm-offline-routers/1.0).
- Page with limit=500 and follow meta.next exactly as returned until it is null.
  It is an absolute URL and may point at a regional host such as
  www.us0.cradlepointecm.com. Send the same headers there, but only ever send
  credentials to https:// URLs on cradlepointecm.com or its subdomains.
- Don't follow redirects automatically; treat any 3xx response as an error (a
  301 usually means a missing trailing slash). HTTP clients re-send custom
  headers to wherever a redirect points, which would leak your keys.
- Print a clean table: router name, ID, and time since state_updated_at
  (for example "3d 4h 12m"). Put routers with no timestamp last.
- On HTTP 429, wait until the X-RateLimit-Reset header (a Unix epoch) if it is
  present; otherwise back off exponentially (1s, 2s, 4s, 8s). Make at most
  5 attempts per request in total, and never wait more than 60s at a time.
  On any other HTTP error, stop with a clear message. Use a 30s request timeout.

Then tell me exactly how to run it.
