Type: reference · Imported reference (adopted from the clarity backend developer docs)
Reading time-series data back: numeric range queries (JSON and binary), latest values, blob reads, and the sensordata helpers. All routes require auth. Timestamps are epoch milliseconds. See Aggregation for the pipeline operators.
Three request structs are used across the query endpoints:
fast_query, fast_query_binary): organization/site/unit/grid are optional; if omitted, scope is resolved from the tags.fast_query_optimised): all four scope fields are required.tags[] only (no time range).{
"organization": "org1", "site": "s1", "unit": "u1", "grid": "default_grid",
"tags": ["temperature", "pressure"],
"start": 1700000000000,
"end": 1700003600000,
"pipeline": { "temperature": [ { "op": "mean", "bucket": 60000 } ] } // optional
}
GET routes take these as query-string parameters; POST routes take the same fields as a JSON body.
/exactapi/fast_query → JSONResponse — an object mapping each requested tag to [timestamp_ms, value] pairs; value is a float or null (gap). Tags with no data return [].
{
"temperature": [[1700000000000, 23.5], [1700000060000, 23.7]],
"pressure": [[1700000000000, 1.01], [1700000060000, null]]
}
/exactapi/fast_query_binary → basic binary (Format v2)Content-Type: application/octet-stream. Little-endian. As of c9fceb2 values are 8-byte f64 and format_version is 2 (the earlier f32 payload was v1; clients must branch on the version):
Header (12 bytes):
u32 num_tags
u32 format_version = 2 (was 1 for f32 payloads)
u32 reserved (bit 0 = contains_gaps flag)
Per tag:
u16 tag_name_length
u8[] tag_name (UTF-8)
u32 num_points
Per point:
u64 timestamp (ms)
f64 value (was f32 in v1)
By default missing (None) slots are skipped. If the pipeline contains the gaps operator, every grid slot is emitted with missing values as NaN (detect with isnan) and the header reserved bit 0 is set. Single-step aggregation pipelines are pushed down into the scan.
/exactapi/fast_query_optimised → optimized binary (Format v3)Scope fields required. Most compact when tags share a timestamp grid. Little-endian. As of c9fceb2 values are 8-byte f64 and format_version is 3 (the earlier f32 payload was v2):
Header (16 bytes):
u32 num_timestamps
u32 num_tags
u32 format_version = 3 (was 2 for f32 payloads)
u32 reserved = 0
Shared timestamps:
u64[num_timestamps] (ascending)
Per tag:
u16 tag_name_length
u8[] tag_name (UTF-8)
u8[] validity_bitmap (ceil(num_timestamps/8) bytes; bit set = present, LSB-first)
f64[] non_null_values (present values only, in timestamp order; was f32)
The SDKs decode both the old (f32) and new (f64) versions automatically, branching on format_version.
/exactapi/fast_query_tag_mapping → JSONResolve mapped-tag specs (by metricName) to ids, then behave like fast_query (JSON response). Pipelines are keyed by the resolved generatedDataTagId.
{
"organization":"org1","site":"s1","unit":"u1","grid":"g1",
"tags": [ {"metricName":"temperature"}, {"metricName":"pressure"} ],
"start": 1700000000000, "end": 1700003600000,
"pipeline": { "<generatedDataTagId>": [ {"op":"mean","bucket":60000} ] }
}
/exactapi/lastlist → JSONLatest point per tag (no time range). Response — { tag: [[ts_ms, value]] }, or [] for tags with no data.
{ "temperature": [[1700003599000, 23.9]], "pressure": [] }
For tags carrying string/array/JSON values (see Storage Engine → Blob store). All require the explicit scope (organization/site/unit/grid).
/exactapi/blob_queryBody: scope + tags: string[], start, end. Response — { tag: [[ts_ms, value], …] } (values round-trip as the exact JSON written).
{
"alarm_text": [[1700000000000, "OVERTEMP FAULT"]],
"spectrum": [[1700000000000, [0.1, 0.3, 0.7]]]
}
/exactapi/blob_lastlistBody: scope + tags: string[]. Response — { tag: [ts_ms, value] } (a single pair per tag).
{ "alarm_text": [1700003599000, "RUNNING"] }
/exactapi/blob_searchBody: scope + tags, start, end, exactly one of eq / contains / regex (strings), and optional limit (default 10000, cap 100000).
{ "organization":"org1","site":"s1","unit":"u1","grid":"g1",
"tags":["alarm_text"], "start":1700000000000, "end":1700003600000,
"contains":"FAULT", "limit":500 }
Response — { tag: [[ts_ms, value], …] } (matches only). Errors: 400 { "status": "error", "message": "exactly one of eq / contains / regex must be provided" } or "invalid regex: ...".
/exactapi/blob_state_durationsBody: scope + tag (single), start, end. Rolls up time-in-state.
{
"tag": "state",
"start": 1700000000000,
"end": 1700003600000,
"states": { "RUNNING": 180000, "FAULT": 60000, "STOPPED": 0 },
"change_count": 3,
"runs": [
{ "value": "RUNNING", "start": 1700000000000, "end": 1700000180000, "duration_ms": 180000 },
{ "value": "FAULT", "start": 1700000180000, "end": 1700000240000, "duration_ms": 60000 }
]
}
states maps each state to total ms; runs is capped at 10000 entries.
/exactapi)/sensordata/{id}/lastlistAuth required (the {id} path segment is ignored). Body:
{ "query": { "vars": ["temperature", "pressure"] } }
Response — one entry per requested var (order and duplicates preserved). data is [[ts_ms, value]] (both serialized as floats) or [[]] if no data; cached is always the string "true".
{
"data": [
{ "tag": "temperature", "cached": "true", "data": [[1700003599000.0, 23.9]] },
{ "tag": "pressure", "cached": "true", "data": [[]] }
]
}
/sensordata/shadowAuth required. Body is a JSON array of strings shaped "<unitId>-shadow" (unitId = SQLite unit row id).
["1-shadow", "2-shadow", "5-shadow"]
Response — each key mapped to the unit's last data timestamp (ms, as a float); units with no data are omitted.
{ "1-shadow": 1700003599000.0, "5-shadow": 1700003580000.0 }
pipeline operatorsAdopted from clarity backend developer docs (
docs/developer/api/queries.md), imported reference.
Primary handlers:clarity:backend/src-tauri/src/main.rs:4542-4552(fast_query),clarity:backend/src-tauri/src/api/binary_format.rs(wire formats: basic v2, optimized v3 — 8-byte f64 values).
Last updated: 2026-07-17 from commit 6800acc