Type: how-to · Last reviewed: 2026-07-10
Applies to: Pulse Historian · edge/on-prem · managed cloud
Goal: read and write your tag data straight from a terminal or a script — no app, no UI — using Pulse Historian's REST API or an SDK.
Prerequisites:
https://<plant-server>:3030 on-prem, or your managed-cloud URL.curl (examples below) or Python 3.8+ / Node.js for the SDKs.-k to curl (or install the CA from GET /exactapi/ca-cert).All examples use the canonical collection acme-power / plant-1 / boiler-2 (see Data model & scope).
curl -k -X POST https://plant-server:3030/exactapi/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@acme-power.com","password":"•••"}'
// → 200
{ "token": "eyJhbGciOiJI…", "role": "read-write" }
Save the token; every call below sends it as Authorization: Bearer <token>. Tokens are valid 24h by default.
curl -k -X POST https://plant-server:3030/exactapi/write \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"organization":"acme-power","site":"plant-1","unit":"boiler-2","grid":"default_grid",
"data":[{"timestamps":[1720008000000],"tag_values":{"steam_pressure":78.4,"steam_temp":512.0}}]}'
// → 200
{ "status": "success", "written": 2 }
You don't need the full scope to read; the tag name resolves to its collection automatically.
curl -k -X POST https://plant-server:3030/exactapi/fast_query \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"tags":["steam_pressure"],"start":1720008000000,"end":1720008120000}'
// → 200
{ "steam_pressure": { "timestamps": [1720008000000], "values": [78.4] } }
Ask the engine to bucket/aggregate instead of returning raw points — e.g. 1-minute means:
"pipeline": { "steam_pressure": [ { "op": "mean", "bucket": 60000 } ] }
Full operator list (mean/percentile/rate/scale/…) is in the Aggregation pipeline reference.
For many tags or long ranges, the binary query formats + SDK decoders are far faster than JSON:
from clarity_sdk import ClarityClient
c = ClarityClient("https://plant-server:3030", verify_ssl=False)
c.login("you@acme-power.com", "•••")
df = c.query_dataframe(["steam_pressure","steam_temp"], start=1720008000000, end=1720011600000)
print(df.head())
steam_pressure steam_temp
2024-07-03 12:00:00 78.4 512.0
2024-07-03 12:01:00 78.1 511.6
Details: Python SDK · JavaScript SDK.
A non-empty values array (step 3) or a populated DataFrame (step 5) confirms you're reading live data. To check the newest timestamp per unit: POST /exactapi/sensordata/shadow.
| Symptom | Cause | Fix |
|---|---|---|
curl TLS / certificate error |
Self-signed cert on-prem | Add -k, or install the CA from GET /exactapi/ca-cert |
401 Unauthorized |
Missing/expired token | Repeat step 1; tokens last 24h |
429 on login |
Rate limit (30 logins / 60 s per email) → account lockout | Wait for the lockout window; don't loop logins |
Tag '<x>' not found in scope map |
Tag name unknown, or ambiguous | Pass the full organization/site/unit/grid, or confirm the tag exists |
Empty values |
No data in that time range | Widen start/end; check the unit is actually writing |