Aquairi Public API
Connect your IoT devices to the Aquairi platform. Send sensor data, water parameters, and photos from any internet-connected aquarium hardware.
Getting Started
Create an Aquairi Account
Sign up at aquairi.app and create your aquarium profile.
Register a Device
In the Aquairi mobile app, go to your aquarium settings and add a new device. Choose the device type that matches your hardware (thermometer, pH meter, camera, etc.).
Get Your Device Token
After registering the device, you will receive a device API token. This token is tied to a specific device and aquarium. Store it securely.
aqk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0...Start Sending Data
Use the token in the Authorization header of every request:
curl -X POST https://iot.aquairi.app/sensor-data \
-H "Authorization: Bearer aqk_your_device_token" \
-H "Content-Type: application/json" \
-d '{"temperature": 25.5, "ph": 7.2}'https://iot.aquairi.app|Protocol: HTTPS (REST)|Content-Type: application/jsonAuthentication
All API endpoints (except /health) require a valid device token in the Authorization header.
Token Format
Authorization: Bearer <device_token>How It Works
- Your device sends the token in the
Authorizationheader. - The API validates the token against the Aquairi backend.
- Valid tokens are cached for 5 minutes to reduce latency.
- The API checks that the device has the required capability for the endpoint.
- If the token is invalid or the device lacks the capability, the request is rejected.
Device Payload
When a token is validated, the following information is extracted:
interface DevicePayload {
deviceId: string; // Unique device identifier
aquariumId: string; // Aquarium this device belongs to
userId: string; // Owner of the device
deviceType: string; // e.g., "thermometer", "ph_meter"
capabilities: string[]; // e.g., ["send_parameters"]
}Capabilities
| Capability | Required For | Description |
|---|---|---|
send_parameters | POST /parameters, GET /parameters/latest | Send and read water parameters |
send_sensor_data | POST /sensor-data | Submit sensor readings to event history |
upload_photo | POST /photos | Upload aquarium photos |
Endpoints Reference
/healthCheck that the API is running. No authentication required.
Response
{
"status": "ok",
"service": "public-bff",
"timestamp": "2026-03-01T12:00:00.000Z"
}Code Examples
curl https://iot.aquairi.app/health/parametersLog water parameters for the aquarium linked to the device.
send_parametersRequest Body
{
"temperature": 25.5,
"humidity": 60
}Response
{ "success": true }Code Examples
curl -X POST https://iot.aquairi.app/parameters \
-H "Authorization: Bearer aqk_your_token" \
-H "Content-Type: application/json" \
-d '{"temperature": 25.5, "humidity": 60}'/parameters/latestRetrieve the most recent water parameters for the device's aquarium.
send_parametersResponse
{
"temperature": 25.5,
"ph": 7.2,
"tds": 300,
"timestamp": "2026-03-01T12:00:00.000Z"
}Code Examples
curl https://iot.aquairi.app/parameters/latest \
-H "Authorization: Bearer aqk_your_token"/sensor-dataSubmit a sensor reading to the aquarium's event history. The data is automatically enriched with the device's ID and type.
send_sensor_dataRequest Body
{
"temperature": 25.5
}Response
{ "success": true }Code Examples
curl -X POST https://iot.aquairi.app/sensor-data \
-H "Authorization: Bearer aqk_your_token" \
-H "Content-Type: application/json" \
-d '{"temperature": 25.5}'/photosUpload an aquarium photo from a camera device. Files are sent as multipart form data. Maximum file size: 10 MB.
upload_photoParameters
| Field | Type | Required | Description |
|---|---|---|---|
file | File | Yes | Image file (JPEG, PNG). Max 10 MB. |
Response
{
"url": "https://cdn.aquairi.app/photos/aqua-abc123/photo.jpg"
}Code Examples
curl -X POST https://iot.aquairi.app/photos \
-H "Authorization: Bearer aqk_your_token" \
-F "file=@/path/to/photo.jpg"Device Types & Data Schemas
Each device type has a specific data schema that defines which fields are required and which are optional. Unknown fields are silently stripped.
Thermometer
thermometerTemperature sensor with optional humidity
temperaturehumiditypH Meter
ph_meterpH sensor with optional temperature
phtemperatureTDS Meter
tds_meterTotal Dissolved Solids sensor
tdsconductivitysalinityLight Sensor
light_sensorLight intensity sensor
lightuvIndexCamera
cameraAquarium camera for photo uploads
No sensor data fields. Use POST /photos to upload images.
Multi-Sensor
multi_sensorCombined sensor board with multiple readings
temperaturephtdslightammonianitritenitrateGeneric
genericCustom / DIY device (up to 20 fields)
temperaturephtdslighthumidityammonianitritenitratesalinityconductivityuvIndexdissolvedOxygenco2pressureflowRatewaterLevelturbidityorpalkalinitycalciumError Handling
All error responses follow a consistent format:
interface ErrorResponse {
success: false;
statusCode: number;
code?: string; // Machine-readable error code
message: string; // Human-readable description
errors?: string[]; // Validation error details
timestamp: string; // ISO 8601 timestamp
path: string; // Request path
}Example Error Response
{
"success": false,
"statusCode": 400,
"code": "DEVICE-VAL-001",
"message": "Invalid device data",
"errors": [
"Missing required field: temperature"
],
"timestamp": "2026-03-01T12:00:00.000Z",
"path": "/parameters"
}Error Code Reference
| Code | Status | Description |
|---|---|---|
DEVICE-AUTH-001 | 401 | Authorization token is missing |
DEVICE-AUTH-002 | 401 | Token is invalid or expired |
DEVICE-AUTH-003 | 401 | Token has been revoked |
DEVICE-AUTH-004 | 403 | Device lacks required capability |
DEVICE-VAL-001 | 400 | Sensor data validation failed |
HTTP-VAL-400 | 400 | Generic validation error |
HTTP-RATE-429 | 429 | Rate limit exceeded |
HTTP-THROTTLE-409 | 409 | Write interval too frequent (min 5 minutes) |
HTTP-SRV-500 | 500 | Internal server error |
Rate Limits & Write Interval
5-Minute Write Interval
All data write operations are throttled to a minimum interval of 5 minutes per device per channel. This applies to both REST API and MQTT.
| Operation | Min Interval | Rejection |
|---|---|---|
POST /parameters | 5 minutes | 409 Conflict |
POST /sensor-data | 5 minutes | 409 Conflict |
MQTT parameters | 5 minutes | Silently dropped |
MQTT telemetry | 5 minutes | Silently dropped |
GET /parameters/latest | No limit | — |
POST /photos | No limit | — |
Why 5 Minutes?
Aquarium parameters change slowly. A 5-minute interval provides optimal data granularity while keeping storage costs low and dashboards responsive. For most setups this means ~288 readings per day per device.
Handling 409 Responses
If you send data more frequently than every 5 minutes via REST, the API returns 409 Conflict. Your device should handle this gracefully:
delay(300000) (5 min)Read Endpoints
Read endpoints (GET /parameters/latest, GET /health) have no throttle. Poll as needed, though we recommend aligning reads with the 5-minute write cadence.
MQTT Protocol
The Aquairi IoT platform supports MQTT for low-overhead, persistent communication. Ideal for microcontrollers (ESP32, Arduino) and constrained devices.
Connection Details
| Setting | Value |
|---|---|
| Broker | iot.aquairi.app |
| TCP Port | 1883 |
| WebSocket Port | 9001 |
| Protocol | MQTT 3.1.1 |
| QoS | 1 (at least once) |
| Client ID | Your device token |
Topic Structure
All topics follow the format:
aquairi/devices/{device_token}/{channel}| Channel | Direction | Description | Destination |
|---|---|---|---|
parameters | Device → Cloud | Water quality data (pH, temp, ammonia, etc.) | Aquariums Service → parameters collection |
telemetry | Device → Cloud | Raw sensor readings (light, CO₂, flow, etc.) | History Service → sensor_reading event |
Authentication
The device token (aqk_...) is embedded in the topic path. The broker validates it against the device registry with a 5-minute validation cache. No username/password needed — the token IS the auth.
Write Interval
Minimum interval: 5 minutes. Messages published more frequently than every 5 minutes per device per channel are silently dropped. This protects the platform and ensures data quality.
Examples
Publish Parameters (mosquitto_pub)
mosquitto_pub -h iot.aquairi.app -p 1883 \
-t "aquairi/devices/aqk_your_token/parameters" \
-m '{"temperature": 25.5, "ph": 7.2}' \
-q 1Publish Telemetry (mosquitto_pub)
mosquitto_pub -h iot.aquairi.app -p 1883 \
-t "aquairi/devices/aqk_your_token/telemetry" \
-m '{"temperature": 25.5, "light": 1250}' \
-q 1Arduino / ESP32
#include <WiFi.h>
#include <PubSubClient.h>
const char* BROKER = "iot.aquairi.app";
const char* TOKEN = "aqk_your_token";
WiFiClient wifi;
PubSubClient mqtt(wifi);
void setup() {
mqtt.setServer(BROKER, 1883);
}
void loop() {
if (!mqtt.connected()) mqtt.connect(TOKEN);
mqtt.loop();
String topic = String("aquairi/devices/")
+ TOKEN + "/parameters";
String payload = "{\"temperature\":" +
String(readTemp()) + "}";
mqtt.publish(topic.c_str(), payload.c_str());
delay(300000); // 5 minutes
}Python (paho-mqtt)
import paho.mqtt.client as mqtt
import json, time
TOKEN = "aqk_your_token"
BROKER = "iot.aquairi.app"
TOPIC = f"aquairi/devices/{TOKEN}/parameters"
client = mqtt.Client(client_id=TOKEN)
client.connect(BROKER, 1883)
while True:
payload = json.dumps({
"temperature": read_temp(),
"ph": read_ph(),
})
client.publish(TOPIC, payload, qos=1)
time.sleep(300) # 5 minutesPayload Format
JSON object with sensor fields matching the device type schema (see Device Types). Unknown fields are ignored. Values are validated server-side before storage.
// parameters channel
{ "temperature": 25.5, "ph": 7.2, "ammonia": 0.02 }
// telemetry channel
{ "temperature": 25.5, "light": 1250, "uvIndex": 3 }Best Practices
Send Data at Reasonable Intervals
For most aquarium monitoring, sending data every 1-5 minutes is sufficient. Do not flood the API with sub-second requests.
Implement Retry with Exponential Backoff
Retry transient errors (5xx, 429) with increasing delays. Never retry auth errors.
async function sendWithRetry(
url: string,
data: Record<string, number>,
token: string,
maxRetries = 3,
): Promise<void> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const res = await fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
if (res.ok) return;
// Don't retry auth errors
if (res.status === 401 || res.status === 403) {
throw new Error("Auth failed");
}
// Retry server errors with backoff
if (res.status >= 500 || res.status === 429) {
const delay = Math.pow(2, attempt) * 1000;
await new Promise(r => setTimeout(r, delay));
continue;
}
throw new Error(`Request failed: ${res.status}`);
}
throw new Error("Max retries exceeded");
}Store Tokens Securely
On ESP32/Arduino, use the Preferences library or encrypted NVS. On Raspberry Pi, use environment variables. Never hardcode tokens in public repositories.
Validate Data Before Sending
Check that sensor readings are within reasonable ranges. This prevents corrupted data from entering the system.
Handle Network Failures
Buffer sensor readings locally when the network is unavailable. Send buffered data when connectivity is restored.
Use the Correct Device Type
Register your device with the most specific type that matches your hardware. This enables proper validation and better analytics in the app.