Complete reference for writing data from devices, reading history, and querying your Virtuino Cloud account.
https://api.virtuino.com/api/data
Every request must include your API Key. You can pass it in any of the three ways below — choose whichever fits your client best.
Your API Key is found in Console → API & Connections → HTTP API Key.
Sends one or more field values to the platform. If publish: true is set, the value is also forwarded to the MQTT broker automatically (requires Essential+ plan).
https://api.virtuino.com/api/data/write
x-api-key: YOUR_API_KEY Content-Type: application/json
{
"device_name": "esp32",
"field": "temperature",
"value": 23.75
}
{
"success": true,
"count": 1,
"total": 1,
"device": "esp32"
}
https://api.virtuino.com/api/data/write
x-api-key: YOUR_API_KEY Content-Type: application/json
{
"field": "global_status",
"value": 1
}
// device_name omitted → stored under "unspecified"
{
"success": true,
"count": 1,
"total": 1,
"device": "unspecified"
}
https://api.virtuino.com/api/data/write
x-api-key: YOUR_API_KEY Content-Type: application/json
{
"device_name": "greenhouse_01",
"data": [
{ "field": "temperature", "value": 24.5 },
{ "field": "humidity", "value": 65 },
{ "field": "soil", "value": 42 }
]
}
{
"success": true,
"count": 3,
"total": 3,
"device": "greenhouse_01"
}
https://api.virtuino.com/api/data/write
x-api-key: YOUR_API_KEY Content-Type: application/json
{
"device_name": "esp32",
"time": "2026-06-12T10:00:00Z", // ISO 8601 UTC
"data": [
{ "field": "temperature", "value": 22.1 },
{ "field": "humidity", "value": 58.4 }
]
}
{
"success": true,
"count": 2,
"total": 2,
"device": "esp32"
}
https://api.virtuino.com/api/data/write
x-api-key: YOUR_API_KEY Content-Type: application/json
{
"device_name": "esp32",
"data": [
{ "field": "relay1", "value": 1, "publish": true },
{ "field": "relay2", "value": 0, "publish": true }
]
}
// publish:true → platform publishes to {sub_key}/out/device/esp32/relay1 etc.
// Requires Essential+ plan (api_gateway feature)
{
"success": true,
"count": 2,
"total": 2,
"device": "esp32"
}
{
"device_name": "esp32",
"data": [
{ "field": "temperature", "value": 23.5 },
{ "field": "nonexistent", "value": 99 }
]
}
{
"success": true,
"count": 1,
"total": 2,
"device": "esp32",
"skipped_fields": ["nonexistent"],
"note": "Some fields do not exist. Create them in the console first."
}
"publish": true to any field entry causes the platform to publish that value to the corresponding MQTT /out/ topic automatically — no MQTT client needed on the device. See the MQTT documentation for topic format details.
Returns historical records for all fields of a specific device, grouped by timestamp. Use unspecified as the device name to query standalone fields.
https://api.virtuino.com/api/data/device-history/esp32
api_key = YOUR_API_KEY
limit = 50 // max 5000, default 100
Full URL Example
https://api.virtuino.com/api/data/device-history/esp32?api_key=YOUR_API_KEY&limit=50
{
"success": true,
"device": "esp32",
"count": 50,
"history": [
{
"time": "2026-06-12T10:05:00Z",
"source": "HTTP",
"fields": [
{ "field": "temperature", "value": "23.75" },
{ "field": "humidity", "value": "61.2" }
]
},
// … more entries
]
}
https://api.virtuino.com/api/data/device-history/esp32
api_key = YOUR_API_KEY latest = trueFull URL Example
https://api.virtuino.com/api/data/device-history/esp32?api_key=YOUR_API_KEY&latest=true
{
"success": true,
"device": "esp32",
"time": "2026-06-12T10:05:00Z",
"source": "HTTP",
"fields": [
{ "field": "temperature", "value": "23.75" },
{ "field": "humidity", "value": "61.2" }
]
}
https://api.virtuino.com/api/data/device-history/esp32
api_key = YOUR_API_KEY
from = 2026-06-12T00:00:00Z // ISO 8601 UTC
to = 2026-06-12T23:59:59Z
limit = 100
Full URL Example
https://api.virtuino.com/api/data/device-history/esp32?api_key=YOUR_API_KEY&from=2026-06-12T00:00:00Z&to=2026-06-12T23:59:59Z&limit=100
{
"success": true,
"device": "esp32",
"count": 87,
"history": [ /* … */ ]
}
Returns records for a single field. Use the device-scoped URL when the field belongs to a device; use the short form for standalone (no-device) fields.
https://api.virtuino.com/api/data/device/esp32/field/temperature
api_key = YOUR_API_KEY limit = 100Full URL Example
https://api.virtuino.com/api/data/device/esp32/field/temperature?api_key=YOUR_API_KEY&limit=100
{
"success": true,
"field": "temperature",
"device": "esp32",
"count": 100,
"data": [
{ "time": "2026-06-12T10:05:00Z", "value": "23.75", "source": "HTTP" },
// …
]
}
https://api.virtuino.com/api/data/device/esp32/field/temperature
api_key = YOUR_API_KEY latest = trueFull URL Example
https://api.virtuino.com/api/data/device/esp32/field/temperature?api_key=YOUR_API_KEY&latest=true
{
"success": true,
"field": "temperature",
"device": "esp32",
"latest_entry": {
"time": "2026-06-12T10:05:00Z",
"value": "23.75",
"source": "HTTP"
}
}
https://api.virtuino.com/api/data/device/esp32/field/temperature
api_key = YOUR_API_KEY from = 2026-06-12T00:00:00Z to = 2026-06-12T23:59:59Z limit = 15Full URL Example
https://api.virtuino.com/api/data/device/esp32/field/temperature?api_key=YOUR_API_KEY&from=2026-06-12T00:00:00Z&to=2026-06-12T23:59:59Z&limit=15
{
"success": true,
"field": "temperature",
"device": "esp32",
"count": 15,
"data": [ /* … */ ]
}
https://api.virtuino.com/api/data/field/global_status
api_key = YOUR_API_KEY latest = trueFull URL Example
https://api.virtuino.com/api/data/field/global_status?api_key=YOUR_API_KEY&latest=true
{
"success": true,
"field": "global_status",
"device": "Standalone Field",
"latest_entry": {
"time": "2026-06-12T08:00:00Z",
"value": "1",
"source": "HTTP"
}
}
Returns records across all devices in your account, grouped by device name. Useful for building full-account dashboards.
https://api.virtuino.com/api/data/devices-history/
api_key = YOUR_API_KEY latest = trueFull URL Example
https://api.virtuino.com/api/data/devices-history/?api_key=YOUR_API_KEY&latest=true
{
"success": true,
"data": {
"esp32": {
"time": "2026-06-12T10:05:00Z",
"source": "HTTP",
"fields": [
{ "field": "temperature", "value": "23.75" },
{ "field": "humidity", "value": "61.2" }
]
},
"greenhouse_01": {
"time": "2026-06-12T09:55:00Z",
"source": "HTTP",
"fields": [ /* … */ ]
}
}
}
https://api.virtuino.com/api/data/devices-history/
api_key = YOUR_API_KEY
from = 2026-06-12T00:00:00Z
to = 2026-06-12T23:59:59Z
limit = 200 // max 5000, default 100
Full URL Example
https://api.virtuino.com/api/data/devices-history/?api_key=YOUR_API_KEY&from=2026-06-12T00:00:00Z&to=2026-06-12T23:59:59Z&limit=200
{
"success": true,
"data": { /* grouped by device name */ }
}
Returns a flat list of all records across all devices and fields, ordered by time descending. Each row includes device name, field name, value, timestamp, and source.
https://api.virtuino.com/api/data/history
api_key = YOUR_API_KEY
limit = 50 // default 50, max 5000
Full URL Example
https://api.virtuino.com/api/data/history?api_key=YOUR_API_KEY&limit=50
{
"success": true,
"count": 50,
"data": [
{
"time": "2026-06-12T10:05:00Z",
"device": "esp32",
"field": "temperature",
"value": "23.75",
"source": "HTTP"
},
// …
]
}
https://api.virtuino.com/api/data/history
api_key = YOUR_API_KEY from = 2026-06-12T00:00:00Z to = 2026-06-12T23:59:59Z limit = 100Full URL Example
https://api.virtuino.com/api/data/history?api_key=YOUR_API_KEY&from=2026-06-12T00:00:00Z&to=2026-06-12T23:59:59Z&limit=100
{
"success": true,
"count": 100,
"data": [ /* … */ ]
}
Returns metadata for all devices (or a specific one), including the list of fields associated with each. Use this to discover what devices and fields exist in your account.
https://api.virtuino.com/api/data/device-info
api_key = YOUR_API_KEYFull URL Example
https://api.virtuino.com/api/data/device-info?api_key=YOUR_API_KEY
{
"success": true,
"devices_count": 2,
"inventory": [
{
"device_name": "esp32",
"available_fields": ["temperature", "humidity", "relay1"]
},
{
"device_name": "greenhouse_01",
"available_fields": ["soil", "light"]
}
]
}
https://api.virtuino.com/api/data/device-info/esp32
api_key = YOUR_API_KEYFull URL Example
https://api.virtuino.com/api/data/device-info/esp32?api_key=YOUR_API_KEY
{
"success": true,
"device_name": "esp32",
"available_fields": ["temperature", "humidity", "relay1"]
}
Full working examples. Each example writes two fields and reads back the latest snapshot.
// Required libraries — install via Arduino IDE → Library Manager: // ArduinoJson (by Benoit Blanchon) // WiFi and HTTPClient are built-in for ESP32 — no installation needed. #include <WiFi.h> #include <HTTPClient.h> #include <ArduinoJson.h> // ── Configuration — replace with your own values ───────────────────── const char* ssid = "YOUR_WIFI_SSID"; const char* password = "YOUR_WIFI_PASSWORD"; const char* apiKey = "YOUR_API_KEY"; // Console → Keys & Sub Users const char* writeUrl = "https://api.virtuino.com/api/data/write"; const char* historyUrl = "https://api.virtuino.com/api/data/device-history/esp32?latest=true"; // Change "esp32" in historyUrl to match your device name in the Console. // ── Send two sensor readings to the platform ────────────────────────── void writeData(float temp, float hum) { if (WiFi.status() != WL_CONNECTED) return; HTTPClient http; http.begin(writeUrl); http.addHeader("Content-Type", "application/json"); // Build JSON body — "device_name" must match the device created in the Console StaticJsonDocument<256> doc; doc["api_key"] = apiKey; doc["device_name"] = "esp32"; JsonArray data = doc.createNestedArray("data"); JsonObject f1 = data.createNestedObject(); f1["field"] = "temperature"; f1["value"] = temp; f1["publish"] = true; // publish:true also sends to MQTT JsonObject f2 = data.createNestedObject(); f2["field"] = "humidity"; f2["value"] = hum; f2["publish"] = true; String body; serializeJson(doc, body); int code = http.POST(body); Serial.println("Write → HTTP " + String(code)); // 200 = success http.end(); } // ── Read the latest values stored for this device ───────────────────── void readLatest() { if (WiFi.status() != WL_CONNECTED) return; HTTPClient http; http.begin(historyUrl); http.addHeader("x-api-key", apiKey); // pass API key as header for GET requests int code = http.GET(); String payload = http.getString(); http.end(); Serial.println("Read → HTTP " + String(code)); if (code != 200) { Serial.println("Error: " + payload); // print server error message return; } // Parse the JSON response StaticJsonDocument<512> doc; DeserializationError err = deserializeJson(doc, payload); if (err) { Serial.println("JSON parse error: " + String(err.c_str())); return; } // Iterate the "fields" array and print each name/value pair // You can also read specific fields: doc["fields"][0]["value"].as<float>() JsonArray fields = doc["fields"]; for (JsonObject f : fields) { const char* name = f["field"]; const char* value = f["value"]; Serial.printf(" %s = %s\n", name, value); } } void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWi-Fi connected"); } void loop() { static unsigned long last = 0; if (millis() - last > 15000) { // send every 15 seconds last = millis(); writeData(random(200, 300) / 10.0f, random(400, 700) / 10.0f); readLatest(); } }
// Required libraries — install via Arduino IDE → Library Manager: // ArduinoJson (by Benoit Blanchon) // Board support: "esp8266 by ESP8266 Community" — Boards Manager → update to version 3.x // setInsecure() requires ESP8266 core ≥ 2.5.0. If you get a compile error, update the board package. // ESP8266WiFi, ESP8266HTTPClient, and WiFiClientSecure are part of the ESP8266 core — no separate install needed. #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> #include <WiFiClientSecure.h> // required for HTTPS on ESP8266 — ESP32 handles it natively #include <ArduinoJson.h> // ── Configuration — replace with your own values ───────────────────── const char* ssid = "YOUR_WIFI_SSID"; const char* password = "YOUR_WIFI_PASSWORD"; const char* apiKey = "YOUR_API_KEY"; // Console → API & Connections const char* writeUrl = "https://api.virtuino.com/api/data/write"; const char* historyUrl = "https://api.virtuino.com/api/data/device-history/esp8266?latest=true"; // Change "esp8266" in historyUrl to match your device name in the Console. // ESP8266 needs a WiFiClientSecure object passed to http.begin() for HTTPS WiFiClientSecure client; // ── Send two sensor readings to the platform ────────────────────────── void writeData(float temp, float hum) { if (WiFi.status() != WL_CONNECTED) return; client.setInsecure(); // skip TLS certificate verification — simplest setup for IoT HTTPClient http; http.begin(client, writeUrl); // ESP8266 requires the client object; ESP32 does not http.addHeader("Content-Type", "application/json"); // Build JSON body — "device_name" must match the device created in the Console StaticJsonDocument<256> doc; doc["api_key"] = apiKey; doc["device_name"] = "esp8266"; JsonArray data = doc.createNestedArray("data"); JsonObject f1 = data.createNestedObject(); f1["field"] = "temperature"; f1["value"] = temp; f1["publish"] = true; // publish:true also sends to MQTT JsonObject f2 = data.createNestedObject(); f2["field"] = "humidity"; f2["value"] = hum; f2["publish"] = true; String body; serializeJson(doc, body); int code = http.POST(body); Serial.println("Write → HTTP " + String(code)); // 200 = success http.end(); } // ── Read the latest values stored for this device ───────────────────── void readLatest() { if (WiFi.status() != WL_CONNECTED) return; client.setInsecure(); HTTPClient http; http.begin(client, historyUrl); http.addHeader("x-api-key", apiKey); // pass API key as header for GET requests int code = http.GET(); String payload = http.getString(); http.end(); Serial.println("Read → HTTP " + String(code)); if (code != 200) { Serial.println("Error: " + payload); // print server error message return; } // Parse the JSON response StaticJsonDocument<512> doc; DeserializationError err = deserializeJson(doc, payload); if (err) { Serial.println("JSON parse error: " + String(err.c_str())); return; } // Iterate the "fields" array and print each name/value pair JsonArray fields = doc["fields"]; for (JsonObject f : fields) { const char* name = f["field"]; const char* value = f["value"]; Serial.printf(" %s = %s\n", name, value); } } void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWi-Fi connected"); } void loop() { static unsigned long last = 0; if (millis() - last > 15000) { // send every 15 seconds last = millis(); writeData(random(200, 300) / 10.0f, random(400, 700) / 10.0f); readLatest(); } }
// Required libraries — install via Arduino IDE → Library Manager: // WiFiNINA (by Arduino) // ArduinoHttpClient (by Arduino) // ArduinoJson (by Benoit Blanchon) // Compatible boards: Arduino Uno WiFi Rev2, Arduino MKR WiFi 1010 #include <WiFiNINA.h> #include <ArduinoHttpClient.h> #include <ArduinoJson.h> // ── Configuration — replace with your own values ───────────────────── const char* ssid = "YOUR_WIFI_SSID"; const char* password = "YOUR_WIFI_PASSWORD"; const char* apiKey = "YOUR_API_KEY"; // Console → API & Connections const char* server = "api.virtuino.com"; // hostname only — port/path set below // WiFiSSLClient handles TLS; HttpClient wraps it with the hostname and port WiFiSSLClient wifiClient; HttpClient http(wifiClient, server, 443); // ── Send two sensor readings to the platform ────────────────────────── void writeData(float temp, float hum) { // Build JSON body — "device_name" must match the device created in the Console StaticJsonDocument<256> doc; doc["api_key"] = apiKey; doc["device_name"] = "arduino_uno"; JsonArray data = doc.createNestedArray("data"); JsonObject f1 = data.createNestedObject(); f1["field"] = "temperature"; f1["value"] = temp; f1["publish"] = true; // publish:true also sends to MQTT JsonObject f2 = data.createNestedObject(); f2["field"] = "humidity"; f2["value"] = hum; f2["publish"] = true; String body; serializeJson(doc, body); // ArduinoHttpClient requires an explicit beginRequest/endRequest sequence http.beginRequest(); http.post("/api/data/write"); http.sendHeader("Content-Type", "application/json"); http.sendHeader("Content-Length", body.length()); http.endRequest(); http.print(body); int code = http.responseStatusCode(); Serial.println("Write → HTTP " + String(code)); // 200 = success } // ── Read the latest values stored for this device ───────────────────── void readLatest() { // Pass API key as a query parameter — ArduinoHttpClient does not support custom GET headers String path = String("/api/data/device-history/arduino_uno?api_key=") + apiKey + "&latest=true"; http.get(path); int code = http.responseStatusCode(); String payload = http.responseBody(); Serial.println("Read → HTTP " + String(code)); if (code != 200) { Serial.println("Error: " + payload); return; } // Parse JSON and print each field/value pair StaticJsonDocument<512> doc; if (deserializeJson(doc, payload)) { Serial.println("JSON parse error"); return; } JsonArray fields = doc["fields"]; for (JsonObject f : fields) Serial.printf(" %s = %s\n", (const char*)f["field"], (const char*)f["value"]); } void setup() { Serial.begin(9600); while (!Serial); // wait for Serial Monitor to open (needed on Uno WiFi Rev2) while (WiFi.begin(ssid, password) != WL_CONNECTED) { delay(1000); } Serial.println("Wi-Fi connected"); } void loop() { static unsigned long last = 0; if (millis() - last > 15000) { // send every 15 seconds last = millis(); writeData(random(200, 300) / 10.0f, random(400, 700) / 10.0f); readLatest(); } }
# pip install requests import requests # ── Configuration — replace with your own values ────────────────────── API_KEY = "YOUR_API_KEY" # Console → API & Connections BASE_URL = "https://api.virtuino.com/api/data" DEVICE = "esp32" # must match a device name created in the Console # ── Write two fields ────────────────────────────────────────────────── # publish:True also pushes the value to the MQTT broker (Essential+ plan) r = requests.post(f"{BASE_URL}/write", json={ "api_key": API_KEY, "device_name": DEVICE, "data": [ { "field": "temperature", "value": 23.75, "publish": True }, { "field": "humidity", "value": 61.2, "publish": True } ] }) print("Write:", r.json()) # {"success": true, "message": "Stored 2 fields ..."} # ── Read latest snapshot for all fields of a device ─────────────────── # Returns {"success": true, "device": ..., "fields": [{"field": ..., "value": ...}, ...]} r = requests.get(f"{BASE_URL}/device-history/{DEVICE}", params={ "api_key": API_KEY, "latest": "true" }) print("Latest:", r.json()) # ── Read last 15 records for one field in a time range ──────────────── # Timestamps must be in ISO 8601 UTC format: YYYY-MM-DDTHH:MM:SSZ r = requests.get(f"{BASE_URL}/device/{DEVICE}/field/temperature", params={ "api_key": API_KEY, "from": "2026-06-12T00:00:00Z", "to": "2026-06-12T23:59:59Z", "limit": 15 }) print("History:", r.json())
// ── Configuration — replace with your own values ───────────────────── const API_KEY = "YOUR_API_KEY"; // Console → API & Connections const BASE_URL = "https://api.virtuino.com/api/data"; // ── Write two fields ────────────────────────────────────────────────── // api_key goes in the JSON body for POST requests async function writeData() { const res = await fetch(`${BASE_URL}/write`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ api_key: API_KEY, device_name: "esp32", // must match a device created in the Console data: [ { field: "temperature", value: 23.75, publish: true }, // publish:true also sends to MQTT { field: "humidity", value: 61.2, publish: true } ] }) }); const data = await res.json(); console.log("Write:", data); // {success: true, message: "Stored 2 fields ..."} } // ── Read latest snapshot for all fields of a device ─────────────────── // For GET requests, pass the API key as a query parameter async function readLatest() { const res = await fetch(`${BASE_URL}/device-history/esp32?api_key=${API_KEY}&latest=true`); const data = await res.json(); console.log("Latest:", data); // Access individual fields: data.fields[0].value, data.fields[1].value, etc. } writeData(); readLatest();
# ── Write two fields ────────────────────────────────────────────────── # API key goes in the x-api-key header for POST; "device_name" must match the Console # publish:true also pushes the value to the MQTT broker (Essential+ plan) curl -X POST https://api.virtuino.com/api/data/write \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "device_name": "esp32", "data": [ {"field":"temperature","value":23.75,"publish":true}, {"field":"humidity","value":61.2,"publish":true} ] }' # ── Read latest snapshot for all fields of a device ─────────────────── # For GET requests, pass the API key as a query parameter curl "https://api.virtuino.com/api/data/device-history/esp32?api_key=YOUR_API_KEY&latest=true" # ── Read last 15 temperature records in a time range ───────────────── # Timestamps in ISO 8601 UTC: YYYY-MM-DDTHH:MM:SSZ curl "https://api.virtuino.com/api/data/device/esp32/field/temperature?api_key=YOUR_API_KEY&from=2026-06-12T00:00:00Z&to=2026-06-12T23:59:59Z&limit=15" # ── Get full account inventory ──────────────────────────────────────── # Returns all devices, their online status, last-seen time, and available fields curl "https://api.virtuino.com/api/data/device-info?api_key=YOUR_API_KEY"
All error responses follow the format { "success": false, "error": "…" }.