Live3 · docs
API access
Authenticate against and call the Data API.
The Live3 Data API is a read-only, server-to-server REST interface for pulling your organization’s weather and sensor-station data into your own systems (SCADA, BI tools, custom dashboards) using an API key, no browser login. Every request is authenticated with the key and returns JSON.
| Base URL | https://live3.aeronsystems.com/api/v1 |
| Protocol | HTTPS, REST, JSON responses |
| Auth | Authorization: Bearer API key (organization-scoped) |
| Version | v1 |
The API exposes three endpoints:
| Endpoint | Purpose |
|---|---|
GET /stations | List the stations this key can access. |
GET /parameters | List the parameter codes a station reports, with units and human-readable names. |
GET /timeseries | Retrieve the data: the latest packet, or a paginated window of history. |
A typical integration calls /stations once to discover station IDs, /parameters to learn what each station measures, then polls or back-fills /timeseries for the data.
Getting an API key
API keys are created from the Live3 dashboard. They are owned by your organization, not by an individual user, so a key keeps working regardless of who created it.
- 1
Open the API Keys page.
Sign in to the Live3 dashboard, open the account menu in the top-right, and choose API Keys. This is an admin-only area; if you do not see it, ask an organization admin.
- 2
Create a key.
Click Create key, give it a descriptive name (e.g.
scada-prodorgrafana-readonly), and choose its station scope:- All stations — the key reaches every station your organization can access.
- Specific stations — narrow the key to a chosen subset.
- 3
Copy the secret now.
The full key (
live3_sk_…) is shown exactly once, at creation, with a copy button. Store it somewhere safe immediately, it cannot be retrieved again. If you lose it, revoke the key and create a new one.
Treat the key like a password. Use it only from a server or trusted backend, never in public or browser-side code. You can revoke or rotate a key at any time from the same API Keys page; a revoked or unknown key immediately returns 401 invalid_api_key.
Authentication
Every request must carry your API key as a Bearer token in the Authorization header. There are no other credentials, cookies, or sessions.
curl -H "Authorization: Bearer live3_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
"https://live3.aeronsystems.com/api/v1/stations"- Organization-scoped. A key reaches every station your organization can access, unless it was narrowed to specific stations at creation.
- Secret. Shown only once, at creation. Store it securely.
- Revocable. Revoke or rotate from the dashboard at any time.
Conventions
Timestamps
Every reading carries its time in two forms: timestamp, an ISO 8601 string in UTC (e.g. 2026-06-22T10:38:00.000Z), and epoch_ms, the same instant as integer milliseconds since the Unix epoch. The from, to, and cursor query parameters accept either form.
Errors
Any non-2xx response uses a single, stable JSON shape:
{
"error": {
"code": "forbidden_station",
"message": "The requested station is not accessible with this API key.",
"request_id": "a772dc54-6060-42d6-a1e3-87c9ecf7556f"
}
}Branch your code on the machine-readable code, not on message (which may be reworded). Include request_id when contacting support so the request can be found in the logs.
Rate limits
Requests are metered per key in three independent cost buckets. Every response includes the current budget in headers:
X-RateLimit-Bucket: range_raw
X-RateLimit-Limit-Minute: 5
X-RateLimit-Remaining-Minute: 4
X-RateLimit-Limit-Day: 500
X-RateLimit-Remaining-Day: 498
X-RateLimit-Reset: 1782124740Exceeding a limit returns 429 rate_limited with a Retry-After header (seconds to wait). A robust client reads Retry-After and pauses. The buckets and ceilings:
| Bucket | Applies to | Per minute | Per day |
|---|---|---|---|
cheap | /stations, /parameters, /timeseries (latest) | 60 | 10,000 |
range_1min | /timeseries range, resolution=1min | 20 | 2,000 |
range_raw | /timeseries range, resolution=raw | 5 | 500 |
Endpoints
GET /stations
Lists the stations the key can access. No query parameters.
{
"stations": [
{
"id": "50eae27f-1231-4393-a0b5-91dbd3d42983",
"imei": "220011572929158",
"name": "WMS_AERON 2_5TH FLOOR",
"type": "Data logger (Standard)",
"location": null,
"latitude": null,
"longitude": null,
"timezone": "Asia/Kolkata",
"status": "active",
"lastDataAt": "2026-06-22T10:38:00.000Z",
"expiryDate": "2027-04-23T19:09:02.274Z",
"region": "india",
"pinned": false
}
]
}The id is what you pass as the station parameter everywhere else. status is the live state (active / offline). lastDataAt is the most recent packet time.
GET /parameters
Lists the parameter codes reported by your stations, each with a unit and a human-readable description. The optional query parameter station=<id> scopes the catalog to one station; without it, the catalog is the union across all accessible stations.
GET /api/v1/parameters?station=50eae27f-1231-4393-a0b5-91dbd3d42983{
"parameters": [
{ "code": "6a01dccd88500", "unit": "m/s", "description": "Wind Speed" },
{ "code": "6a01dd8a5c599", "unit": "°", "description": "Wind Direction" },
{ "code": "6a02fad23fa0d", "unit": "mm", "description": "RAIN DAILY CUMMULTIV" },
{ "code": "6a02fb5ce224e", "unit": "W/m²", "description": "RaZon+_DHI" }
]
}Parameter codes are device-specific identifiers (for example 6a01dccd88500), not friendly names. The data returned by /timeseries is keyed by these same codes, so use /parameters to map a code to its description and unit.
GET /timeseries
The data endpoint. It has two modes, selected by whether you pass a time range:
- Latest — pass only
station. Returns the single most recent packet. - Range — pass
station,from, andto. Returns packets in that window, oldest first, paginated.
| Parameter | Required | Description |
|---|---|---|
station | yes | Station id (from /stations). Must be reachable by the key. |
from | range | Start of window, inclusive. ISO 8601 or epoch ms. |
to | range | End of window, inclusive. ISO 8601 or epoch ms. |
resolution | no | raw = every packet; 1min = one row per minute. Default 1min. |
parameters | no | Comma-separated codes to return. Omit for all. |
limit | no | Page size, 1 to 5000. Default 1000. |
cursor | no | Pagination token: pass the previous response's next_cursor to get the next page. |
Latest mode
GET /api/v1/timeseries?station=50eae27f-1231-4393-a0b5-91dbd3d42983{
"station": "50eae27f-1231-4393-a0b5-91dbd3d42983",
"resolution": "latest",
"reading": {
"timestamp": "2026-06-22T10:38:00.000Z",
"epoch_ms": 1782124680000,
"values": {
"6a01dccd88500": 4.025274,
"6a01dd8a5c599": 203.144996,
"6a02fad23fa0d": 0.254
}
}
}"values" has one entry per parameter code; resolve each code to its name and unit via /parameters. If the station has never reported, reading is null.
Range mode
GET /api/v1/timeseries?station=50eae27f-1231-4393-a0b5-91dbd3d42983
&from=2026-06-22T08:40:00Z&to=2026-06-22T10:40:00Z
&resolution=1min&limit=3¶meters=6a01dccd88500,6a01dd8a5c599{
"station": "50eae27f-1231-4393-a0b5-91dbd3d42983",
"resolution": "1min",
"from": "2026-06-22T08:40:00.000Z",
"to": "2026-06-22T10:40:00.000Z",
"count": 3,
"has_more": true,
"next_cursor": 1782117720000,
"readings": [
{ "timestamp": "2026-06-22T08:40:00.000Z", "epoch_ms": 1782117600000,
"values": { "6a01dccd88500": 2.079987, "6a01dd8a5c599": 227.103155 } },
{ "timestamp": "2026-06-22T08:41:00.000Z", "epoch_ms": 1782117660000,
"values": { "6a01dccd88500": 2.253820, "6a01dd8a5c599": 248.702174 } },
{ "timestamp": "2026-06-22T08:42:00.000Z", "epoch_ms": 1782117720000,
"values": { "6a01dccd88500": 2.859851, "6a01dd8a5c599": 250.531489 } }
]
}| Field | Meaning |
|---|---|
count | Number of readings in this page. |
has_more | true if more packets remain in the window after this page. |
next_cursor | Pass as cursor on the next call. null when done. |
readings[].timestamp | Packet time, ISO 8601 UTC. |
readings[].epoch_ms | Packet time, epoch milliseconds (this is the cursor value). |
readings[].values | Object of parameter code → value for that packet. |
Window caps. A single range request may span at most 30 days at resolution=raw and 90 days at resolution=1min. A larger from..to returns 400 range_too_large. For longer history, advance the window (see the recipe below).
Common recipes
Get the current snapshot
One call, no range. Returns the latest packet for a station.
curl -H "Authorization: Bearer $LIVE3_KEY" \
"https://live3.aeronsystems.com/api/v1/timeseries?station=$STATION"Get all data packets for a period
The canonical "give me everything" pattern: request a window, then keep calling with cursor=next_cursor until has_more is false. Each packet appears exactly once, in time order. The helpers below also advance the window so you can pull spans longer than the per-request cap.
import requests, time
BASE = "https://live3.aeronsystems.com/api/v1"
KEY = "live3_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
HEAD = {"Authorization": f"Bearer {KEY}"}
DAY = 86_400_000
CAP = {"raw": 30 * DAY, "1min": 90 * DAY}
def fetch_history(station, start_ms, end_ms, resolution="1min", parameters=None):
"""Yield every packet in [start_ms, end_ms], oldest first."""
win_start = start_ms
while win_start <= end_ms:
win_end = min(win_start + CAP[resolution], end_ms)
cursor = None
while True:
q = {"station": station, "to": win_end,
"resolution": resolution, "limit": 5000}
if parameters: q["parameters"] = parameters
if cursor is None: q["from"] = win_start
else: q["cursor"] = cursor
r = requests.get(f"{BASE}/timeseries", headers=HEAD, params=q)
if r.status_code == 429: # respect rate limits
time.sleep(int(r.headers.get("Retry-After", "5"))); continue
r.raise_for_status()
body = r.json()
for row in body["readings"]:
yield row
if not body["has_more"]:
break
cursor = body["next_cursor"]
win_start = win_end + 1 # next window
# Example: pull a full day of raw packets
rows = list(fetch_history(STATION, 1782086400000, 1782172800000, "raw"))
print(len(rows), "packets")Resolutions: raw vs 1min
raw returns every ingested packet exactly as the station sent it, at the station’s own reporting interval. Use it when you need full fidelity. 1min returns one pre-aggregated row per minute and is lighter and faster for charts and long spans. Both share the same values shape, so you can switch with a single parameter.
Only the station’s catalog parameters, those listed by /parameters, are returned. Calculated/derived and unregistered channels are not exposed.
Error reference
| HTTP | code | When |
|---|---|---|
| 401 | invalid_api_key | Missing, malformed, unknown, or revoked key. |
| 400 | invalid_request | Missing station, or an unrecognized resolution value. |
| 403 | forbidden_station | The station is not reachable by this key. |
| 400 | incomplete_range | A range is missing from/to (and no cursor was given). |
| 400 | invalid_range | from/to/cursor not parseable, or from is after to. |
| 400 | range_too_large | Window exceeds the cap (30d raw, 90d 1min). Narrow it or page. |
| 400 | unknown_parameter | A requested parameter code is not in the station's catalog. |
| 429 | rate_limited | Bucket limit exceeded. Wait Retry-After seconds and retry. |
| 500 | internal_error | Unexpected server error. Retry; if it persists, send the request_id to support. |
Notes on values
- Values are numbers in the parameter's native unit (see
/parameters). A value may be absent from a packet if the sensor did not report it. - A sentinel of
999999.999999means the sensor reported no usable value for that parameter at that time; treat it as missing, not a real reading. - Every parameter code returned by
/timeseriesalso appears in/parametersfor that station, so you can always resolve it to a name and unit. Codes are stable per device but differ between devices, always resolve through/parametersrather than hard-coding names.

