Documentation

The whole API on one page. Base URL: api.postdata.cloud — over https:// or, if your firmware prefers it, plain http://. (An OpenAPI spec exists too.)

The rules

  • Device names and metric names: letters, numbers, _, ., - (max 64 chars). First come, first served.
  • 1 to 10 metrics per message. Values are numbers, or short strings (strings keep only their last value — no history).
  • One message per device per 30 seconds. Faster posts get a 429 with a Retry-After header.
  • Numeric history is retained for ~92 days. Everything is public.
  • Keys named ts or timestamp are ignored — the server sets the timestamp on reception.

POST /add/{device} — send a reading

Body is a flat JSON object of metrics. Responds with the stored metrics.

$ curl -X POST http://api.postdata.cloud/add/home-geo-station \
    -d '{"temp": 21.5, "hum": 60, "firmware": "rc2.0.2"}'

{"temp":21.5,"hum":60,"firmware":"rc2.0.2"}

Too basic a firmware for POST bodies? The same endpoint accepts GET with query parameters:

$ curl "http://api.postdata.cloud/add/home-geo-station?temp=21.5&hum=60"

Errors: 400 invalid name/body · 429 rate limited (see Retry-After).

GET /last/{device} — latest reading

The most recent metrics plus ts, the server reception time (unix seconds).

$ curl http://api.postdata.cloud/last/home-geo-station

{"temp":21.5,"hum":60,"firmware":"rc2.0.2","ts":1785239565}

Errors: 404 unknown device.

GET /past/{device}/{hours} — history

Timestamp-ordered array of the numeric metrics. hours is optional (default 168 = one week, max 2208 ≈ 92 days). Text metrics carry no history.

$ curl http://api.postdata.cloud/past/home-geo-station/24

[{"ts":1785153165,"temp":20.1,"hum":63},
 {"ts":1785153195,"temp":20.4,"hum":62},
 ...]

Errors: 404 unknown device.

GET /live/{device} — live stream (WebSocket)

Connect a WebSocket and receive the current reading immediately, then every new reading as it arrives — each message a JSON object shaped like /last. Send ping and you get pong back if you need a keepalive.

$ websocat wss://api.postdata.cloud/live/home-geo-station

{"temp":21.5,"hum":60,"firmware":"rc2.0.2","ts":1785239565}
{"temp":21.7,"hum":59,"firmware":"rc2.0.2","ts":1785239595}
...

Or from a browser:

const ws = new WebSocket('wss://api.postdata.cloud/live/home-geo-station');
ws.onmessage = (e) => console.log(JSON.parse(e.data));

GET /explore — recent devices

The 20 most recently active devices with their latest data. Powers the explore page.

A note on plain HTTP

TLS on microcontrollers means certificate stores, expiring root CAs and multi-second handshakes. PostData deliberately accepts unencrypted HTTP on port 80 so constrained devices can publish with zero ceremony — everything on PostData is public anyway. Use https:// whenever your device can afford it.

TLS on microcontrollers, done right

Want encryption anyway? Great — just don't pin the server certificate. Public certificates are short-lived (90 days today, and industry rules are shrinking that further every year), so firmware that embeds the certificate it saw at build time is guaranteed to brick within months.

Embed the root CA instead. Roots live for decades, and every renewal underneath them keeps validating. PostData runs on Cloudflare, whose certificates are issued by one of three public CAs, so embed these roots (about 5 KB total, all available from each CA's site) and you are covered until well into the 2030s:

On ESP32 (Arduino core) it looks like this:

// roots.pem: concatenation of the root certificates above
extern const char roots_pem[] asm("_binary_roots_pem_start");

WiFiClientSecure client;
client.setCACert(roots_pem);   // trust the roots, not the leaf

HTTPClient http;
http.begin(client, "https://api.postdata.cloud/add/my-sensor");
http.POST("{\"temp\": 21.5}");

Two classic gotchas:

  • Set the clock first. Certificate validation compares dates, and a device with no RTC boots in 1970. Sync via SNTP once before the first TLS handshake or validation will fail.
  • Prefer ECDSA. Smaller certificates, faster handshakes, less RAM — most embedded TLS stacks handle it well.

And if even a root bundle is too much ceremony: some stacks offer "insecure" TLS (client.setInsecure()) — encrypted against passive sniffing but not authenticated, so a man-in-the-middle could tamper with it. For public sensor data that is sometimes a fair trade. Or just use plain http:// and move on with your life.