Names. Devices and metrics use 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, with no history.
One message per device per 30 seconds, whichever way you
send it — HTTP, WebSocket and MQTT all share the one limit. 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 stamps the reception time.
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.
The same socket also sends. Push a flat JSON object of
metrics — the identical body /add takes — and it is stored
exactly as an HTTP reading would be. One connection, both directions, no
reconnect per reading.
publish over the same socket
ws.send(JSON.stringify({ temp:21.5, hum:60}));
// Success arrives as the broadcast itself, stamped by the server:// {"temp":21.5,"hum":60,"ts":1785239625}// Failures answer back instead, and never close the socket:// {"error":"rate limited: retry in 24s","retryAfter":24}
WS/mqtt/{device} — MQTT (3.1.1 and 5.0)
Already speak MQTT? Point your client at /mqtt/{device} and keep
your existing code. The device name lives in the URL, not the topic,
and one connection carries one device.
Two topic shapes, whichever your firmware already produces:
postdata/{device} — payload is a flat JSON
object, exactly like /add.
postdata/{device}/{metric} — payload is one
bare value, e.g. 21.5.
Subscribe to postdata/{device} (or
postdata/{device}/#) for live readings, delivered as the
same JSON /live sends.
Use MQTT 5 if your client supports it. MQTT 3.1.1's
PUBACK has no reason-code field, so a rejected publish is
indistinguishable from an accepted one — your reading silently vanishes. On
MQTT 5 you get told: 0x97 when the 30-second rate limit turned
you down, 0x87 for a topic naming another device,
0x99 when the payload failed validation.
What is deliberately missing, because a public last-value store has no use for it:
QoS 2. QoS 0 and 1 only; MQTT 5 clients are told this up
front via Maximum QoS.
Retained messages, wills, persistent sessions.
Subscribing already delivers the current reading immediately, which is
what retain would have bought you.
Credentials.CONNECT's username and
password are accepted and ignored. Every device here is public.
We answer PINGREQ, but MQTT 5 clients are handed a Server Keep
Alive of 240 s that overrides whatever they configured — long enough to
stay cheap, short enough to survive the NAT timeouts that quietly kill idle
connections.
GET/explore — recent devices
The 20 most recently active devices with their latest data. Powers the
explore page.
03
coming from dweet.io?
dweet.io is gone, but your things don't need to know. PostData answers dweet's
classic URLs with dweet's classic envelopes — point your firmware at
api.postdata.cloud instead of dweet.io and it keeps
dweeting:
GET/POST/dweet/for/{thing}
— query parameters or a JSON body, exactly like /add.
/dweet/quietly/for/{thing} works too and answers 204
with no body.
GET/get/latest/dweet/for/{thing}
— the latest reading as a one-dweet array (dweet's /last).
GET/get/dweets/for/{thing}
— up to the 5 most recent readings of the past 24 hours, newest first.
PostData's rules still apply: flat metrics only (1–10 keys), one message per 30
seconds (a 429 failure envelope with Retry-After), names
limited to [A-Za-z0-9_.-]. Locks, alerts and stored dweets are not
emulated — everything here is public. For new firmware, prefer the native
/add family above.
04
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.
05
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:
// roots.pem: concatenation of the root certificates aboveextern 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}");
Three 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. An RSA chain is served
automatically to clients that don't offer ECDSA, so older stacks still
connect.
ESP8266 needs a big TLS buffer. Cloudflare does not
negotiate the max_fragment_length extension, so BearSSL cannot
shrink its receive buffer to 512 or 2048 bytes the way
setBufferSizes() invites you to. Give it the full 16 KB or
the handshake fails with no useful error. ESP32 (mbedTLS) is unaffected.
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.
websockets and mqtt
All of the above applies unchanged to /live and /mqtt:
wss:// and ws:// are the same two doors as
https:// and http://, on the same ports, presenting the
same certificate. Nothing extra to configure.
One thing to know if you are used to a normal broker: there is no port
8883, and no 1883. MQTT here is always MQTT-over-WebSocket, because
PostData runs on Workers, which accept HTTP and HTTPS only. Clients that can only
open a raw MQTT socket cannot connect at all; clients that support a WebSocket
transport need it switched on, and that is the whole migration. In practice that
means a URL scheme of ws:// or wss:// rather than
mqtt:// or mqtts://.
Client certificates are not supported either, so don't reach for mutual TLS to
identify a device. There is nothing to authenticate against — every device on
PostData is public, and CONNECT's username and password fields are
accepted and thrown away.