All endpoints are served under the /pth prefix (the Flask Blueprint's url_prefix), regardless of whether pth_server is run standalone or mounted inside another Flask app. Examples below assume a base URL like https://your-server.example.com.
Serves the browser-based dashboard (pth_analysis.html) — a Chart.js time-series view with date-range controls, per-channel toggles, point lookup, and CSV export. No parameters.
Stores one reading. Body is a flat JSON object: a time field (Unix epoch seconds) plus one numeric field per sensor channel. An optional device_id identifies which physical device sent the reading; if omitted, it's stored as "unknown".
Request body:
{
"time": 1750000000,
"device_id": "greenhouse-1",
"MS5611 Pressure": 100840.8,
"SHT31 Temperature": 20.58
}Response — 200 OK:
{ "status": "success" }Response — 400 Bad Request (empty/missing body):
{ "error": "No data provided" }Re-posting an identical (device_id, channel, time) triple is a no-op — duplicate inserts are silently ignored rather than erroring or creating a duplicate row.
All retrieval endpoints return a JSON array of flat records. Each record has a time field (Unix epoch seconds, integer), a device_id field, and one field per sensor channel present at that timestamp:
[
{
"time": 1750000000,
"device_id": "greenhouse-1",
"MS5611 Pressure": 100840.8,
"SHT31 Temperature": 20.58
}
]If two devices recorded a reading at the exact same time, each produces its own separate record (they are never merged into one row).
Anywhere a timestamp is accepted as a query parameter, it can be given as either a Unix epoch integer (or numeric string) or an ISO 8601 datetime string (e.g. 2025-02-19T14:30:00).
All three retrieval endpoints below also accept an optional device_id to restrict results to one device.
Returns all readings from the past N days.
| Param | Required | Description |
|---|---|---|
days |
no (default 1) |
Positive integer number of days to look back from now. |
device_id |
no | Restrict to readings from this device only. |
GET /pth/api/ndays?days=7
GET /pth/api/ndays?days=7&device_id=greenhouse-1
Response — 400 Bad Request if days is missing, non-numeric, or ≤ 0:
{ "error": "Invalid days parameter: <value>" }Returns all readings with time between start and end, inclusive.
| Param | Required | Description |
|---|---|---|
start |
yes | Range start — epoch int or ISO 8601 string. |
end |
yes | Range end — epoch int or ISO 8601 string. |
device_id |
no | Restrict to readings from this device only. |
GET /pth/api/range?start=1740000000&end=2025-03-01T00:00:00
GET /pth/api/range?start=1740000000&end=2025-03-01T00:00:00&device_id=greenhouse-1
Response — 400 Bad Request if either param is missing or unparseable:
{ "error": "Both start and end are required" }
{ "error": "Invalid start/end value: '<start>', '<end>'" }Returns the single reading whose time is closest to time.
| Param | Required | Description |
|---|---|---|
time |
yes | Target timestamp — epoch int or ISO 8601 string. |
device_id |
no | Restrict to readings from this device only. |
GET /pth/api/closest?time=1740000000
GET /pth/api/closest?time=2025-02-19T14:30:00&device_id=greenhouse-1
Unlike the other retrieval endpoints, this returns a single JSON object, not an array:
{
"time": 1750000000,
"device_id": "greenhouse-1",
"MS5611 Pressure": 100840.8,
"SHT31 Temperature": 20.58
}If multiple devices share the exact closest timestamp and no device_id is given, one is returned arbitrarily (whichever was inserted first). Pass device_id to get an unambiguous result.
Response — 400 Bad Request if time is missing:
{ "error": "No time provided" }Response — 404 Not Found if there is no data at all:
{ "error": "No matching data found" }import requests
BASE_URL = "https://your-server.example.com/pth"
response = requests.get(f"{BASE_URL}/api/closest", params={"time": 1740000000})
print(response.json())
## {'MS5611 Pressure': 100840.8, 'SHT31 Relative Humidity': 31.26, 'SHT31 Temperature': 20.58, 'device_id': 'greenhouse-1', 'time': 1762489563}
response = requests.get(f"{BASE_URL}/api/ndays", params={"days": 7})
data = response.json()
response = requests.get(f"{BASE_URL}/api/range", params={"start": 1740000000, "end": "2025-03-01T00:00:00"})
data = response.json()| Old route | New route |
|---|---|
/pth_analysis |
/pth/dashboard |
/store_pth_data |
/pth/api/store_data |
/pth/get_closest |
/pth/api/closest |
/api/pth/ndays |
/pth/api/ndays |
| (none) | /pth/api/range (new) |