Menu

Ingest API

Publish data with a plain HTTPS POST. No MQTT client, no library — if it can make a web request, it can put data on your dashboard.

The Ingest API is the write counterpart to the Data API. POST to a URL whose path is the topic you'd publish to, and the platform turns it into a single MQTT message for you. Live at https://ingest.ohioiot.com.

When to reach for this

MQTT is the right tool for a device that stays connected. But plenty of things that produce data aren't that: a shell script on a cron, a serverless function, a PLC that only speaks HTTP, a webhook from another service, a quick test from your laptop. For all of those, opening and maintaining an MQTT connection is overkill. One HTTPS POST does the job.

Auth keys

Every request carries an auth key you generate in your account settings. The same key works across the HTTP surfaces — ingest, data, and OTA. Pass it as a header (preferred):

x-auth-key: YOUR_AUTH_KEY

or as a query parameter, when setting a header is awkward:

https://ingest.ohioiot.com/dev01/numb/temperature/temp?auth_key=YOUR_AUTH_KEY

You can keep up to three keys active at once, which makes rotation painless: add a new one, move your clients over, delete the old one.

The key carries your account's authority. Don't commit it to git or ship it in client-side code. If one leaks, delete it from the dashboard — that revokes it immediately.

The path is the topic

The platform builds the MQTT topic from your account ID plus the path segments you POST to. You never type your account ID — it's derived from your auth key:

POST /dev01/numb/temperature/temp
  ->  topic:  <your_account_id>/dev01/numb/temperature/temp

That's the same five-segment shape used everywhere else on the platform: deviceID / type / graphName / seriesName, with the account prefix filled in for you. The type is one of numb, bool, status, or config (see Data Types & Routing). You can POST to shorter paths too, down to just a device ID, and move the rest of the structure into the body — see Topic Flexibility.

What goes in the body

The body is accepted as JSON, plain text, or form-encoded. How it's interpreted depends on how deep your path is:

  • Deepest path (down to a series/field name) — the body is a single scalar. Plain text and numbers pass through as-is; a JSON object is expected to carry a value field.
  • Shorter paths — the whole body becomes the payload. Strings pass through; objects are sent as JSON.

Examples

A single reading:

curl -H "x-auth-key: YOUR_KEY" -H "Content-Type: text/plain" \
     -d "23.5" \
     https://ingest.ohioiot.com/dev01/numb/temperature/temp

Several fields on one graph, in one request:

curl -H "x-auth-key: YOUR_KEY" -H "Content-Type: application/json" \
     -d '{"temp": 23.5, "hum": 61}' \
     https://ingest.ohioiot.com/dev01/numb/env

A status value for the device card:

curl -H "x-auth-key: YOUR_KEY" -H "Content-Type: text/plain" \
     -d "online" \
     https://ingest.ohioiot.com/dev01/status

From Python:

import os, requests

requests.post(
    "https://ingest.ohioiot.com/dev01/numb/temperature/temp",
    headers={"x-auth-key": os.environ["OHIOIOT_KEY"]},
    data="23.5",
)

Fire-and-forget

A successful response means the message was accepted and handed to the broker — not that a device received it. The publish is fire-and-forget, the same as any MQTT publish. If you need confirmation that a device acted on something, that's what command feedback is for (see Commands).

Errors

  • 200 — accepted.
  • 401 — auth key missing or invalid.
  • 404 — too many path segments (the topic can't be more than the five-segment shape).
  • 500 — backend error. Retry; if it persists, get in touch.
Every HTTP surface self-documents at /info. Hit https://ingest.ohioiot.com/info in a browser for the live endpoint list — no auth key required.