# Errors, Limits & Stability

The error shape, common error codes, documented rate limits, display-only fields, and what we promise not to change without warning.

## Error shape

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:

```json
{"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`:

```json
{
  "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`.

## Common error responses

| 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"}` |

## Rate limits

- **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.

## Handling errors

- **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.

## Display-only fields

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. |

## Stability

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.

> **Caution**
>
> This stability wording is the default pending the API owner's confirmation — treat it as directional, not yet final.

## Getting help

Email [info@hungrymachines.io](mailto:info@hungrymachines.io) with questions about the API or this contract.

## Next steps

- [API Reference](/api-reference/readings/) — per-endpoint request/response shapes
- [OpenAPI Description](/api-reference/openapi/) — the machine-readable spec
- [Authentication](/getting-started/authentication/) — a worked example of 401 handling

Canonical page: https://docs.hungrymachines.io/api-reference/errors-and-limits/
