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 param | Default | Notes |
|---|---|---|
from | account creation | ISO 8601. Clamped to your signup date (free tier) |
to | now | ISO 8601 |
limit | 200 | Max 1000 per page |
cursor | — | Pass 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
| Status | When |
|---|---|
401 | Missing / invalid / revoked / expired token |
403 | Token is valid, but you have no access to that device |
404 | Device or user not found |
429 | Rate limit exceeded — wait Retry-After seconds |
5xx | Our 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.