Point anything that speaks JSON at one URL and get a live dashboard.
Microcontrollerβ JSONβ APIβ Dashboard
No account, no API key, no setup. Generate a device ID and a write key below, POST your first measurement, and every sensor you send becomes a chart automatically.
On this instance right now
The large number is the all-time total β it includes devices and measurements that have since expired, and only ever goes up. The line beneath it is what exists right now.
1. Generate your two identifiers
A device is identified by a device ID and owned by a write key. The first request to use an unclaimed device ID claims it; every later write to that device must present the same write key. Both are generated here in your browser and neither is sent anywhere until your device makes its first measurement.
β¦or generate them individually below. Already have a device? Type its ID in and the examples will use it.
Device ID
Public, like a username. It appears in your dashboard URL, so anyone who has it can read your charts. Use letters, digits, hyphens, and underscores β or take a random one.
Write key
Secret, like a password β it is the only thing stopping someone else
writing to your device. Generated locally with
crypto.getRandomValues, about 187 bits of entropy.
Save it now. We store only a hash of your write key, so it cannot be shown to you again, reset, or recovered. Lose it and your only options are a different device ID or waiting 48 hours for the device to expire.
2. Send a measurement
One POST to /sensor/measurement. Each
entry in sensors becomes a chart named after its key β send a
new one whenever you like and it appears on its own.
These examples now contain your write key. Treat them like a password from here on: don't paste them into a chat, an issue tracker, or a public repository.
curl -X POST https://diy-sensor.org/sensor/measurement \
-H 'content-type: application/json' \
-d '{
"device_id": "YOUR-DEVICE-ID",
"write_key": "YOUR-WRITE-KEY",
"project": "balcony",
"name": "Basil #3",
"sensors": {
"temperature": {"value": 21.4, "unit": "C"},
"humidity": 51,
"pump_running": false
}
}'
project groups devices onto a shared dashboard and
name is a display label; both are optional. A sensor value
may be a bare number or an object with value,
unit, and plot
(gauge or line).
import requests
requests.post(
"https://diy-sensor.org/sensor/measurement",
json={
"device_id": "YOUR-DEVICE-ID",
"write_key": "YOUR-WRITE-KEY",
"sensors": {"temperature": 21.4, "humidity": 51},
},
timeout=10,
).raise_for_status()
#include <WiFi.h>
#include <HTTPClient.h>
const char *INGEST_URL = "https://diy-sensor.org/sensor/measurement";
const char *DEVICE_ID = "YOUR-DEVICE-ID";
const char *WRITE_KEY = "YOUR-WRITE-KEY"; // keep out of public repos
void publish(float temperature, float humidity) {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
http.begin(INGEST_URL);
http.addHeader("Content-Type", "application/json");
char body[256];
snprintf(body, sizeof(body),
"{\"device_id\":\"%s\",\"write_key\":\"%s\","
"\"sensors\":{\"temperature\":%.2f,\"humidity\":%.2f}}",
DEVICE_ID, WRITE_KEY, temperature, humidity);
int status = http.POST(body);
// 201 = device claimed, 200 = measurement appended.
// 429/503 mean "slow down": back off, don't retry in a tight loop.
Serial.printf("ingest -> %d\n", status);
http.end();
}
Your dashboard is then at
/dashboard/device/YOUR-DEVICE-ID.
3. Look after your key
- Send the write key in the request body, over HTTPS β never in the URL. Query strings end up in proxy logs and browser history; the body does not.
- Keep it out of your repository. Put it in a config file you don't commit, or a build-time constant. A key pushed to a public repository should be treated as burnt: claim a new device ID.
- One key per device. Sharing a key between devices means that losing one exposes all of them.
- The device ID is not a secret and gives no write access β but knowing it is enough to read the dashboard.
-
API keys are for operators. If you were issued one,
send it as the
X-API-Keyheader: it raises your limits and makes your devices permanent. It does not override any device's write key.
What happens to your data
- A device with no successful write for 48 hours is deleted, along with all of its measurements and its stored key hash.
- The device ID then becomes free for anyone to claim again. A reclaimed ID starts empty β none of the previous owner's data is visible.
- Devices created with an API key are permanent and are not swept.
- Timestamps are assigned by the server when it receives a measurement; clients cannot backdate data.
Limits
Deliberately tight while this is in beta; they will be loosened as real
usage shows what normal looks like. Rate-limited responses carry a
Retry-After header.
| Limit | Anonymous |
|---|---|
| Data retention | 48 hours without a successful write |
| Request body | 16 KB |
| Sensor fields per request | 16 |
| Distinct sensors per device | 16 |
| Device ID length | 64 characters |
| Sensor name length | 64 characters |
| Requests per minute (per IP) | 30 |
| Requests per day (per IP) | 1,000 |
| Writes per minute (per device) | 12 |
| New devices per hour (per IP) | 5 |
| Active devices (per IP) | 10 |
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | missing_device_id / invalid_device_id | No device_id, or one with characters outside AβZ aβz 0β9 _ - |
| 400 | missing_sensors / empty_sensors | No sensors object, or an empty one |
| 400 | invalid_value | A value that is not a number, boolean, short string, or null β NaN and infinity included |
| 400 | unknown_field | A field the endpoint does not accept, including timestamp |
| 401 | missing_write_key | The device has a write key and the request did not present it |
| 401 | api_key_required | The device ID is claimed by a keyless API-key device |
| 403 | invalid_write_key | The write key was wrong |
| 413 | payload_too_large | The request body is over the size limit |
| 429 | β¦_rate_limited / β¦_limit | A rate or quota limit; retry after the seconds in Retry-After |
| 503 | storage_full / β¦_over_budget | The platform is shedding load or out of storage; retry later |
Every error response is JSON with an error message and a
stable code. A successful write returns
201 when it claimed the device and 200 when it
appended to one.