HTTP API Reference
Send telemetry data to Plexus using HTTP or WebSocket. The HTTP API works from any language or platform that can make POST requests.
Authentication
All requests require an API key in the x-api-key header:
x-api-key: plx_xxxxxGet your API key from app.plexus.company (opens in a new tab) -> Settings -> Developer.
Send Data
POST https://app.plexus.company/api/ingest
{
"source_id": "sensor-001",
"points": [
{
"metric": "temperature",
"value": 72.5,
"timestamp": 1699900000.123,
"tags": { "location": "lab" },
"session_id": "test-001"
}
]
}The top-level source_id applies to all points in the request. Per-point source_id overrides the top-level default. At least one must be provided.
Top-Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
source_id | string | No* | Default source ID for all points in this request |
points | array | Yes | Array of data points |
sdk | string | No | SDK identifier (e.g., "c/0.5.4", "python/0.4.0"). Set automatically by the SDKs |
* Either the top-level or per-point source_id must be provided.
Point Fields
| Field | Type | Required | Description |
|---|---|---|---|
metric | string | Yes | Metric name (e.g., temperature, motor.rpm) |
value | any | Yes | See supported value types below |
timestamp | float | No | Unix timestamp in seconds or milliseconds (auto-detected: values < 10^12 are treated as seconds). Defaults to server time |
source_id | string | No* | Your device/source identifier. Lowercase [a-z0-9][a-z0-9._-]*. Overrides top-level source_id |
tags | object | No | Key-value labels for filtering |
session_id | string | No | Group data points into a session |
Supported Value Types
| Type | Example | Use Case |
|---|---|---|
| number | 72.5, -40, 3.14159 | Sensor readings (most common) |
| string | "error", "idle", "running" | Status, state, labels |
| boolean | true, false | On/off, enabled/disabled |
| object | {"x": 1.2, "y": 3.4, "z": 5.6} | Vector data, structured readings |
| array | [1.0, 2.0, 3.0, 4.0] | Waveforms, multiple values |
Sessions
Group related data for analysis and playback.
Create a Session
POST https://app.plexus.company/api/sessions
{
"session_id": "test-001",
"name": "Motor Test Run",
"source_id": "sensor-001",
"status": "active"
}End a Session
PATCH https://app.plexus.company/api/sessions/{session_id}
{
"status": "completed",
"ended_at": "2024-01-15T10:30:00Z"
}Code Examples
curl / Bash
#!/bin/bash
API_KEY="plx_xxxxx"
SOURCE_ID="sensor-001"
curl -X POST https://app.plexus.company/api/ingest \
-H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"points\": [{
\"metric\": \"temperature\",
\"value\": 72.5,
\"timestamp\": $(date +%s),
\"source_id\": \"$SOURCE_ID\"
}]
}"Python
import requests
import time
requests.post(
"https://app.plexus.company/api/ingest",
headers={"x-api-key": "plx_xxxxx"},
json={
"points": [{
"metric": "temperature",
"value": 72.5,
"timestamp": time.time(),
"source_id": "sensor-001"
}]
}
)JavaScript
await fetch("https://app.plexus.company/api/ingest", {
method: "POST",
headers: {
"x-api-key": "plx_xxxxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
points: [
{
metric: "temperature",
value: 72.5,
timestamp: Date.now() / 1000,
source_id: "sensor-001",
},
],
}),
});Go
package main
import (
"bytes"
"encoding/json"
"net/http"
"time"
)
func main() {
points := map[string]interface{}{
"points": []map[string]interface{}{{
"metric": "temperature",
"value": 72.5,
"timestamp": float64(time.Now().Unix()),
"source_id": "sensor-001",
}},
}
body, _ := json.Marshal(points)
req, _ := http.NewRequest("POST", "https://app.plexus.company/api/ingest", bytes.NewBuffer(body))
req.Header.Set("x-api-key", "plx_xxxxx")
req.Header.Set("Content-Type", "application/json")
http.DefaultClient.Do(req)
}Arduino / ESP32
#include <WiFi.h>
#include <HTTPClient.h>
void sendToPlexus(const char* metric, float value) {
HTTPClient http;
http.begin("https://app.plexus.company/api/ingest");
http.addHeader("Content-Type", "application/json");
http.addHeader("x-api-key", "plx_xxxxx");
String payload = "{\"points\":[{";
payload += "\"metric\":\"" + String(metric) + "\",";
payload += "\"value\":" + String(value) + ",";
payload += "\"timestamp\":" + String(millis() / 1000.0) + ","; // time since boot, not epoch — use NTP for wall-clock time
payload += "\"source_id\":\"esp32-001\"";
payload += "}]}";
http.POST(payload);
http.end();
}Limits
| Limit | Value |
|---|---|
| Max points per request | 10,000 |
| Max payload size | 5 MB |
Requests exceeding these limits are rejected before processing.
Response Format
A successful ingest returns:
{
"success": true,
"count": 3,
"persisted": true,
"persistedCount": 3
}| Field | Type | Description |
|---|---|---|
success | boolean | Whether the request was accepted |
count | number | Number of data points received |
persisted | boolean | Whether data was written to storage |
persistedCount | number | Number of data points persisted |
Error Codes
| Status | Meaning |
|---|---|
| 200 | Success |
| 400 | Bad request — check your JSON format and required fields |
| 401 | Invalid or missing API key |
| 402 | Billing limit exceeded — upgrade your plan or check usage |
| 403 | API key lacks required permissions |
| 404 | Resource not found |
| 410 | Resource expired (e.g., pairing code) |
| 413 | Payload too large — max 5 MB per request |
| 429 | Rate limited — back off and retry |
| 500 | Internal server error — retry with exponential backoff |
WebSocket API
For real-time, UI-controlled streaming, devices connect via WebSocket to the PartyKit server. This is what plexus run uses under the hood.
Connection Flow
- Device connects to the PartyKit server
- Device sends a
device_authmessage with its API key and available sensors - Server responds with
authenticated - Dashboard sends control messages (
start_stream,stop_stream, etc.) - Device streams
telemetrymessages back through the server to connected browsers
Message Types (Dashboard to Device)
| Type | Description |
|---|---|
start_stream | Start streaming sensor data |
stop_stream | Stop streaming |
start_session | Start recording to a session |
stop_session | Stop recording |
configure | Configure a sensor (e.g., sample rate) |
execute | Run a command on the device |
cancel | Cancel a running command |
ping | Keepalive request |
Message Types (Device to Dashboard)
| Type | Description |
|---|---|
telemetry | Sensor data points |
session_started | Confirm session started |
session_stopped | Confirm session stopped |
output | Command execution output |
pong | Keepalive response |
Best Practices
- Batch points — Send multiple points per request to reduce HTTP overhead (up to 10,000 per request).
- Include timestamps — Always send accurate timestamps. The server defaults to receive time if omitted, which adds network latency to your data.
- Use consistent source IDs — The same physical device should always use the same
source_id. - Tag your data — Use tags for filtering in the dashboard (e.g.,
{"location": "lab", "unit": "A"}). - Use sessions — Group related data for easier analysis and playback.
- Prefer the agent for real-time — For UI-controlled devices, use
plexus run(WebSocket) instead of direct HTTP.
Next Steps
- Python Agent Quickstart — Managed agent with auto-detection and CLI
- ESP32 / C SDK Quickstart — Minimal footprint telemetry from embedded devices