Skip to content

Platform

Data Buckets

Store time series data from your devices, read it back with filters and aggregations, and export it to a file.

Feature8 min read

A data bucket stores time series: every record has a timestamp and a JSON value with your fields, such as temperature and humidity. Dashboards, alarms, the REST API and agents read the stored data with time ranges, aggregations and filters.

When to use it ​

  • To keep the history of what your devices report, for charts and reports.
  • To collect data written by an MQTT topic, an HTTP client or a third-party service.
  • To feed alarms that check aggregated values, such as the mean temperature of the last five minutes.
  • To export data to a CSV, ARFF or JSON file for analysis outside Thinger.io.

Data sources ​

The data source sets how records reach the bucket. You choose it when you create the bucket.

Sourceconfig.sourceHow records arrive
From Device ResourcedeviceThe platform reads an output resource of a device every Sampling Interval ("update": "interval"), or stores each value the device streams (Stream by Device, "update": "events").
From Device Write CallwriteDevices write records themselves, for example with write_bucket in the Arduino library.
From API RequestapiAny HTTP client writes records with the REST API and an access token.
From MQTT TopicmqttEvery message published on a topic of the MQTT broker is stored.
DATA SOURCEDATA BUCKETWHO READS ITFrom Device Resourcethe platform reads it every interval,or the device calls thing.stream()From Device Write Callthe device callsthing.write_bucket()From API Requestan HTTP client with a token,a script, a service or an agentFrom MQTT Topicevery message publishedon the topicData Bucketone record per readingts1790842200000val{ "temperature": 21.5,"humidity": 40 }tagsdevice: greenhouse_01timestamp, JSON value, optional tagsDashboardscharts over a time range,with aggregationsAlarmsrules that check the valuesat their check intervalAPI, CLI and agentsreads with filters,aggregations and tagsExportsCSV, ARFF or JSON file, witha notice by email or endpoint
How data reaches a Data Bucket and who reads it

A bucket can also keep tags: fields that identify each record, such as device or site, so that one bucket holds the series of many devices and you can read them one by one.

Stream from the device ​

With From Device Resource and the Stream by Device refresh mode, the device decides when a record is stored: the bucket keeps each value the device sends with thing.stream(). Use it to store readings only when something changes, instead of at a fixed interval. This sketch stores the temperature and humidity of a DHT22 sensor when the temperature changes by half a degree:

main.cpp
cpp
#include <ThingerESP32.h>
#include <DHT.h>

#define DHT_PIN 4

ThingerESP32 thing("YOUR_USER", "YOUR_DEVICE", "YOUR_TOKEN");
DHT dht(DHT_PIN, DHT22);

float lastTemperature = 0;

void setup() {
  thing.add_wifi("wifi-network", "wifi-password");
  dht.begin();

  thing["TempHum"] >> [](pson& out) {
    out["temperature"] = dht.readTemperature();
    out["humidity"] = dht.readHumidity();
  };
}

void loop() {
  thing.handle();

  // store a record only when the temperature changes by 0.5 °C or more
  float temperature = dht.readTemperature();
  if (!isnan(temperature) && fabs(temperature - lastTemperature) >= 0.5) {
    lastTemperature = temperature;
    thing.stream("TempHum");
  }
}

Create the bucket with From Device Resource, choose the device in Select Device, TempHum in Select Resource and Stream by Device in Refresh Mode. The same stream also updates any open dashboard of the device. Stream when the data changes, not in every loop: see Call the platform at a safe rate.

Create a Data Bucket ​

YOUR_SERVER is iot.thinger.io on the free Cloud account, or the domain of your private instance.

Console

  1. Open Data Buckets

    Go to Data Buckets and click Add Bucket (or Create a Data Bucket if you have none yet).

  2. Fill in the form

    Set the Bucket Id, a Bucket Name and an optional Bucket Description, and choose the Data Source. For From Device Resource, choose the device in Select Device, the resource in Select Resource and the Refresh Mode: Sampling Interval or Stream by Device. For From MQTT Topic, set the MQTT Topic. Under Database Options, the instance may let you choose the Backend and a Retention period.

    Add Bucket form with the bucket ID, the name and From API Request as data source
    Add a Data Bucket
  3. Save the bucket

    Click Add Bucket. The bucket opens on its Data tab, still empty.

API

POST/v1/users/{user}/bucketsAPI reference
bash
curl -X POST "https://YOUR_SERVER/v1/users/YOUR_USER/buckets" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "bucket": "climate",
    "name": "Climate",
    "description": "Temperature and humidity",
    "enabled": true,
    "config": { "source": "api" }
  }'

YOUR_TOKEN is an access token with permission to create buckets. Other sources need more fields in config: device, resource, update and interval for a device resource, or topic for MQTT. Optional fields set the storage backend and the retention period; see the REST API reference for the full body.

CLI

The CLI cannot create buckets: thinger bucket only lists and reads them. Use the console, the API or an agent.

AI

Ask your AI agent

Create a data bucket called climate that stores the records I send through the API, and describe the fields it accepts.

The agent uses thinger_resource_create with the bucket type. See MCP tools.

Write data ​

Buckets with the From API Request source accept records over HTTP. Each record is a JSON object, or an array of objects to write several at once. The server sets the timestamp when it receives the record; to set your own, add ?ts=<unix ms> to the call or a numeric ts field to the record.

Console

The console does not write single records. To load existing data, upload a CSV file to a File Storage, open the bucket, go to the Import tab, choose the File Storage and the File Name, and click Import Data.

API

POST/v1/users/{user}/buckets/{bucket}/dataAPI reference
bash
curl -X POST "https://YOUR_SERVER/v1/users/YOUR_USER/buckets/climate/data" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "temperature": 21.5, "humidity": 40 }'

CLI

The CLI cannot write bucket records. Use the API or an agent.

AI

Ask your AI agent

Write a record with temperature 21.5 and humidity 40 to my climate bucket.

The agent uses thinger_bucket_data_write.

Import a CSV file ​

An import loads the records of a CSV file into a bucket, whatever its data source: the way to load a long history at once. The file must be in a File Storage first.

The file follows these rules:

PartRule
Extension.csv. Other files are refused with invalid import file, only csv is supported.
First lineThe header, with the name of each column. Up to 99 columns.
Separator; if the header contains one, otherwise ,. Values can be quoted with ".
TimestampA column named ts, date or timestamp (in any case), with an ISO 8601 date in UTC, such as 2026-10-01T08:00:00.000Z, or a unix timestamp in milliseconds. Without one, every record gets the time of the import.
ValuesNumbers are stored as numbers, any other text as a string. Empty cells and null are left out of the record.
RowsA row with more values than the header stops the import.
climate.csv
csv
ts;temperature;humidity;status
2026-10-01T08:00:00.000Z;21.5;40;ok
2026-10-01T08:05:00.000Z;21.7;41;
1790842200000;22.1;39;"fan on"

This file creates three records; the second one has no status field. A CSV file exported from a bucket imports as it is when you export it with the ISO or milliseconds timestamp format: a timestamp in seconds would be read as milliseconds.

Console

Upload the file to a File Storage. Open the bucket, go to the Import tab, choose the File Storage and the File Name, and click Import Data. Import Progress shows the percentage of the file imported.

Import tab of the climate bucket with the documents File Storage and the file reports/2026-09-office-climate.csv, before importing
Import a CSV file into a Data Bucket

API

POST/v1/users/{user}/buckets/{bucket}/importsAPI reference
bash
curl -X POST "https://YOUR_SERVER/v1/users/YOUR_USER/buckets/climate/imports" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "source": "storage", "storage": "imports", "file": "climate.csv" }'

The response arrives line by line while the import runs, one JSON object per block of records with the percentage of the file read: {"progress":42}, and {"progress":100} at the end. A file that cannot be read fails at once with an error. An import that fails halfway ends without {"progress":100}, and the records imported before stay in the bucket. While an import runs, the bucket refuses exports, clearing and deletion.

AI

Ask your AI agent

Import the file climate.csv from my imports file storage into the climate bucket.

The agent uses thinger_resource_call with the import action of the bucket type.

Read data ​

Reads return the most recent records first. Narrow them with these query parameters:

ParameterEffect
itemsNumber of records, between 1 and 1000.
min_ts, max_tsTime range, as unix timestamps in milliseconds.
sortdesc (newest first, the default) or asc.
agg, agg_typeAggregate the records in windows (5m, 1h, 1d…) with a reducer such as mean, min, max, sum or count.
fieldsComma-separated list of the fields to return.
group_byGroup the results by a tag, such as device.
tzTime zone of the aggregation windows.

A parameter with an invalid value, such as items=0 or sort=up, is refused with 400 Bad Request and a message that names it; the read never falls back to the whole bucket.

Console

  1. Open the bucket

    Go to Data Buckets and click the bucket. The Data tab shows the latest records.

  2. Change the time range

    Choose a date range above the table to see older data, and use Tags Filtering to show the records of one tag value, such as one device.

    Data table of a bucket with temperature and humidity records, filtered by the device tag
    Records of a Data Bucket

API

GET/v1/users/{user}/buckets/{bucket}/dataAPI reference
bash
curl "https://YOUR_SERVER/v1/users/YOUR_USER/buckets/climate/data?items=10&sort=desc" \
  -H "Authorization: Bearer YOUR_TOKEN"
json
[
  { "ts": 1792253184387, "val": { "humidity": 41, "temperature": 22.1 } },
  { "ts": 1792253184214, "val": { "humidity": 40, "temperature": 21.5 } }
]

With agg=1h&agg_type=mean, each item holds the window start in ts and the mean of each field.

CLI

bash
thinger bucket read climate --items 10

Aggregate with --agg and --agg-type, for example thinger bucket read climate --minutes 360 --agg 1h --agg-type mean. The CLI selects time with --minutes (the last N minutes) instead of min_ts and max_ts. See the CLI reference.

AI

Ask your AI agent

Show me the hourly mean temperature of my climate bucket over the last 24 hours.

The agent uses thinger_bucket_data_read, which accepts the same parameters as the API.

Export data ​

An export writes the records of a time range to a file that you download later. The export runs in the background; you can be notified by email or by calling an endpoint when it finishes.

Console

Open the bucket and go to the Export tab. Choose the Export Format (CSV, ARFF or JSON), the Timestamp format and the Export Range, optionally an endpoint to call in Callback, and click Export Data. When the file is ready, download it from the same tab.

Export tab of a bucket with the CSV format and a custom range
Export tab of a Data Bucket

API

POST/v1/users/{user}/buckets/{bucket}/exportsAPI reference
bash
curl -X POST "https://YOUR_SERVER/v1/users/YOUR_USER/buckets/climate/exports" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "data_type": "csv", "timestamp_format": "iso" }'

data_type is csv, arff or json. Without min_ts and max_ts the export includes all the data. GET /v1/users/{user}/buckets/{bucket}/exports lists the files with their download URL.

CLI

The CLI cannot export buckets. Use the console, the API or an agent.

AI

Ask your AI agent

Export last month's data of my climate bucket as a CSV file and give me the download link when it is ready.

Limits ​

  • Reserved fields: on buckets stored in MongoDB (the default backend), a record with a field named tags or _id is refused with 400 Bad Request. In an array write, one such record refuses the whole array. Rename the field before writing.
  • Invalid read parameters return 400 Bad Request instead of data.
  • Clearing data (the Clear tab, for all the data or a range) or deleting the bucket removes the stored data, and it cannot be undone.
  • In a project with limit_bucket_access, members read only the data of the devices of the project.
  • Storage and retention depend on your plan: see Plans and limits.