Public REST API

Pull your sensor data programmatically — perfect for Jupyter notebooks, MATLAB scripts, or any dashboard you're building on your own infrastructure.

Stable since v1. We won't break the shape of these responses; new fields may be added.

Authentication

Generate a personal API token at /settings/api. Pass it as a bearer header:

Authorization: Bearer srms_v1_a1b2c3d4e5f6...

Tokens are shown once. Treat them like passwords — store in an env var, never commit to git. Revoke any time at /settings/api.

Rate limits

60 requests per minute per token. Exceed it and you'll get a 429 response with a Retry-After header. Be polite — your sensors update every 5 min; you almost never need to poll faster than that.

GET /api/v1/devices

List every device the token's owner can access.

curl https://YOUR_HOST/api/v1/devices \
  -H "Authorization: Bearer $LvWinSys_TOKEN"
{
  "devices": [
    {
      "id": "clxxxxxxxx",
      "productId": "SRMS-M-0001",
      "type": "MASTER",
      "name": "Tengeh Reservoir Master",
      "latitude": 1.3445,
      "longitude": 103.6685,
      "lastSeenAt": "2026-05-30T12:34:56.000Z",
      "firmwareVersion": "1.0.3",
      "online": true
    }
  ]
}

GET /api/v1/devices/{id}/readings

Stream the device's sensor readings, oldest first.

Query paramDefaultNotes
fromaccount creationISO 8601. Clamped to your signup date (free tier)
tonowISO 8601
limit200Max 1000 per page
cursorPass nextCursor from previous response

curl

curl "https://YOUR_HOST/api/v1/devices/clxxx/readings?from=2026-05-01T00:00:00Z&limit=500" \
  -H "Authorization: Bearer $LvWinSys_TOKEN"

Python

import os, requests

token = os.environ["LvWinSys_TOKEN"]
host = "https://YOUR_HOST"
headers = {"Authorization": f"Bearer {token}"}

def paginate(device_id, *, frm=None, to=None):
    cursor = None
    while True:
        params = {"limit": 1000}
        if frm: params["from"] = frm
        if to:  params["to"] = to
        if cursor: params["cursor"] = cursor
        r = requests.get(f"{host}/api/v1/devices/{device_id}/readings",
                         headers=headers, params=params, timeout=15)
        r.raise_for_status()
        page = r.json()
        for reading in page["readings"]:
            yield reading
        cursor = page.get("nextCursor")
        if not cursor:
            return

readings = list(paginate("clxxx", frm="2026-05-01T00:00:00Z"))
print(len(readings), "readings")

JavaScript

const res = await fetch(
  "https://YOUR_HOST/api/v1/devices/clxxx/readings",
  { headers: { Authorization: `Bearer ${process.env.LvWinSys_TOKEN}` } }
);
const { readings, nextCursor } = await res.json();

Response shape

{
  "readings": [
    {
      "id": "clxxxxxxxx",
      "recordedAt": "2026-05-30T12:34:56.000Z",
      "irradiance": 856.4,
      "uvIndex": 6.2,
      "par": 1500,
      "temperature": 32.1,
      "humidity": 74.0,
      "pressure": 1009.5,
      "rainfall": 0,
      "batteryVoltage": 12.6,
      "latitude": 1.3445,
      "longitude": 103.6685
    }
  ],
  "nextCursor": "clyyy...",
  "freeAccessFrom": "2026-04-15T00:00:00.000Z"
}

Errors

StatusWhen
401Missing / invalid / revoked / expired token
403Token is valid, but you have no access to that device
404Device or user not found
429Rate limit exceeded — wait Retry-After seconds
5xxOur side — retry with exponential backoff

Stability promise

Endpoints under /api/v1/* are stable. We will only add fields to responses, never remove or rename them. Breaking changes ship under a new version path (/api/v2/*) with 6 months overlap.

Need an endpoint not listed here? Email support@solar.io — we're happy to add it.