Skip to content

Errors, Limits & Stability

Every error response is JSON with a single detail field. It takes one of two forms.

An error the API raises itself (400, 401, 403, 404, 429, 5xx) carries a string:

{"detail": "..."}

A request that fails schema validation — a missing required field, a wrong type, or a value outside a field’s allowed range — returns 422 with detail as an array, one object per problem, each carrying loc, msg and type:

{
"detail": [
{
"type": "value_error",
"loc": ["body", "base_temperature"],
"msg": "Value error, base_temperature must be between 60 and 85F"
}
]
}

Read detail as a string when it is one, and otherwise join the msg values. The one 422 with a string detail is a password-policy rejection on POST /auth/signup.

Status Meaning Example
400 A request the API itself rejected {"detail": "hourly_high_temps_f and hourly_low_temps_f must each have exactly 24 values"}
401 Not authenticated / invalid token {"detail": "Not authenticated"} / {"detail": "Invalid token"} / {"detail": "Token expired"}
403 Forbidden {"detail": "Upgrade required"}
404 Resource not found {"detail": "Appliance not found"}
422 Unprocessable entity (schema validation) Array detail, as shown above
429 Rate limit exceeded {"detail": "Rate limit exceeded: ..."}
500 Server / upstream service error {"detail": "Payment service error"}
502 Upstream auth service unreachable {"detail": "Auth service unreachable"}
503 Dependency unavailable {"detail": "Database unavailable"}
  • HVAC readings (POST /api/v1/readings): up to 600 readings per active HVAC or dehumidifier appliance, per account, per UTC day (600 for an account with none registered yet). A single unit pushing every 5 minutes uses 288 of those — the cap is a circuit breaker for a runaway client, not a bound on normal use.
  • Other appliance readings (POST /api/v1/appliances/{id}/readings): up to 300 readings per active EV charger, home battery, water heater, solar array or robot, per account, per UTC day (300 for an account with none registered yet), across every non-HVAC appliance combined.

Exceeding any of these returns 429 with a detail naming the cap you hit.

  • 401 — refresh your access token once with the saved refresh token. If the refresh call itself fails, the refresh token has expired or been revoked; ask the person to sign in again rather than retrying.
  • 429 — back off and retry later rather than retrying in a tight loop.
  • 5xx — the API or one of its dependencies is temporarily unavailable. Back off and retry; this isn’t something your request caused.

A few fields in a schedule response describe what the API expects to happen — they aren’t commands to send to a device. Render them as-is; don’t branch on an exact value or try to reproduce how they’re computed.

Field What you may do with it
model_confidence Reserved. Currently always null; don’t build on it.
temp_trajectory Plot it as the predicted indoor-temperature line. On GET /api/v1/schedule it is null while source is "defaults" and on a warm-up day; on GET /api/v1/schedules the key is absent in those cases.
estimated_savings_pct Show it as an estimate, measured against a naive thermostat held at the middle of the comfort band — not against the home’s actual prior usage.
derived_rates An opaque diagnostic object. Render it generically, or skip it — its keys aren’t part of this contract.

Public response shapes only grow: a field or endpoint documented on this site won’t be removed or change meaning within a major version of this contract. New fields can appear at any time; ignore ones you don’t recognize.

Email info@hungrymachines.io with questions about the API or this contract.