How-to guide · for Developers, Administrators
Historical import
Send older readings of a device to the Proxus historian in small batches over HTTP, with a key that makes every batch safe to retry.
At the end of this guide, your program sends readings that a device recorded in the past (from a data logger, a CSV export or a machine's own memory) to the Proxus historian. They are stored next to the device's live values and appear in trends, reports and the Telemetry API.
Before you start
You need:
- The device in Proxus, enabled, on a running edge gateway. Note its ID (the
Oidin the REST API) and its Token (shown on the device page). - Network access to the gateway's HTTP port,
8083by default (Server-Portin[Service]). - Historical import turned on. It is off in the delivered configuration; see the next section.
Turn historical import on
Set these keys, then restart the hub and the gateways:
| Where | Key | Value |
|---|---|---|
| Hub and every gateway that receives imports | HistoricalImport__Enabled | true |
| The gateway that writes the Proxus historian (the hub's SYSTEM gateway in a standard installation) | HistoricalImport__StorageEnabled | true |
While import is off, the endpoints answer 503.
Step 1: Send a batch
POST http://<gateway>:8083/api/v1/devices/<device ID>/historical/batches
X-IoT-Token: <device token>
Idempotency-Key: lys-packer-04-2026-10-01-0001
Content-Type: application/json{
"records": [
{ "key": "Temperature", "time": "2026-10-01T06:00:00.000Z", "type": "Float", "value": 24.6, "unit": "°C" },
{ "key": "Speed", "time": "2026-10-01T06:00:00.000Z", "type": "UShort", "value": 82, "unit": "rpm" },
{ "key": "Running", "time": "2026-10-01T06:00:00.000Z", "type": "Bool", "value": true }
]
}| Field | Rules |
|---|---|
records | 1 to 50 records. |
key | The tag name, up to 256 characters. |
time | ISO 8601 with Z or an offset such as +02:00, whole milliseconds at most, not in the future, not before 1900. |
type | Exactly one of Bool, Short, UShort, Int, UInt, Float, Long, ULong, Double, String, Bytes, DateTime (case matters). |
value | A JSON value that fits type: true/false for Bool, a number for the numeric types, a string for String (up to 4096 characters), Base64 text for Bytes, an ISO 8601 string for DateTime. Long and ULong must be within ±9007199254740991. |
unit | Optional, up to 64 characters. |
Other fields in the body are refused. The whole body may be at most 256 KiB.
The Idempotency-Key names the batch: 8 to 128 letters, digits, -, _ or .. Sending the same key with the same body again is safe and returns the first answer. Sending the same key with a different body is refused. Use one key per batch, for example the source file name and a batch number.
Step 2: Read the answer
A new batch is answered with 202 Accepted, a repeated one with 200. The Location header points at the batch.
{
"jobId": "5b0e6a5c-6f4e-4a0e-9a59-3f8f0f8c2d11",
"state": "Pending",
"results": [
{ "index": 0, "state": "Pending" },
{ "index": 1, "state": "Pending" },
{ "index": 2, "state": "Rejected", "error": "InvalidTypeOrValue" }
],
"storageDataset": "DeviceRawData"
}Each record has its own result, in the order you sent them. Rejected records are not stored; the others are.
| Record error | Cause |
|---|---|
InvalidRecord | Missing or too long key, too long unit, or no value. |
InvalidTimestamp | time missing, in the wrong format, in the future, before 1900 or finer than a millisecond. |
InvalidTypeOrValue | Unknown type, or a value that does not fit it. |
MeasurementConflict | The batch has the same tag and time twice with different values. The same tag, time and value twice is marked Duplicate instead. |
Step 3: Follow the batch
GET http://<gateway>:8083/api/v1/devices/<device ID>/historical/batches/<jobId>
X-IoT-Token: <device token>Batch state | Meaning |
|---|---|
Pending | Accepted, being written. |
Completed | Every accepted record is stored. |
CompletedWithErrors | Done, and at least one record was rejected. |
NeedsReconciliation | The storage did not confirm some records. Their result is Uncertain. Send the same batch again later with the same key. |
Record states after writing: Stored, Duplicate (the same value for that tag and time is already stored), Rejected or Uncertain.
Answers without a batch
| Status | Body or header | Meaning |
|---|---|---|
400 | {"error":"InvalidIdempotencyKey"} | The Idempotency-Key header is missing or not in the allowed form. |
400 | {"error":"InvalidJson"} or {"error":"InvalidBatch"} | The body is not JSON, has unknown fields, or has no records or more than 50. |
401 | Missing X-IoT-Token, or the token does not belong to this device ID, or the device is disabled. | |
404 | No batch with this ID for this device. | |
409 | {"error":"IdempotencyConflict"} | This key was used before with a different body. |
413 | The body is larger than 256 KiB. | |
415 | Content-Type is not JSON. | |
429 | Retry-After: 5 | The gateway is busy (8 requests at a time), or too many batches are still being written (8 per device, 64 in total). Wait and send again with the same key. |
503 | Import is turned off, or the hub cannot be reached. Send again later with the same key. |
Example in Python
import time
import requests
GATEWAY = "http://localhost:8083"
DEVICE_ID = 69
DEVICE_TOKEN = "<the device's token>"
def send_batch(key, records):
while True:
response = requests.post(
f"{GATEWAY}/api/v1/devices/{DEVICE_ID}/historical/batches",
json={"records": records},
headers={"X-IoT-Token": DEVICE_TOKEN, "Idempotency-Key": key},
timeout=30,
)
if response.status_code in (429, 503):
time.sleep(int(response.headers.get("Retry-After", "5")))
continue
response.raise_for_status()
return response.json()
batch = [
{"key": "Temperature", "time": "2026-10-01T06:00:00.000Z", "type": "Float", "value": 24.6, "unit": "°C"},
{"key": "Temperature", "time": "2026-10-01T06:01:00.000Z", "type": "Float", "value": 24.9, "unit": "°C"},
]
job = send_batch("lys-packer-04-2026-10-01-0001", batch)
print(job["state"], [r["state"] for r in job["results"]])What's next
- Telemetry API: check what was imported.
- Trend Workspace: see the imported values next to live ones.
- Network ports and endpoints.
Was this page helpful?
Evaluating Proxus? See it on your own machines in a 30-minute technical session.