Tibber LogoData API

Live events

Stream real-time measurements from meters over Server-Sent Events

The live-events endpoint streams real-time measurements from a home's meters (Tibber Pulse, Watty) as Server-Sent Events. The connection stays open and pushes a new frame as measurements arrive.

Required scope

Streaming requires the data-api-meters-read scope. Without it the stream endpoint returns 403, and the discovery endpoint returns an empty list. See Scopes.

Discovering streamable devices

GET /v1/homes/{homeId}/live-events/devices

Returns the devices in the home that can be streamed, each with the opaque id you pass as a deviceId query parameter below. Not every meter is always streamable (for example, a Pulse CT is only listed when it measures the home itself, and bridge-attached Pulses require an active Tibber Bridge), so always use this endpoint to determine what you can stream rather than assuming. If your token lacks data-api-meters-read, this returns an empty list.

Opening a stream

GET /v1/homes/{homeId}/live-events?deviceId={a}&deviceId={b}
  • Pass one or more deviceId query parameters (the opaque ids from the discovery endpoint). At least one is required; the maximum is 5 per connection.
  • Each device must belong to the target home.
  • Only one live-events connection per home is allowed at a time; opening a second connection terminates the first (see the evicted error below).

The response is Content-Type: text/event-stream and stays open until the client disconnects.

Frame format

Each event is one SSE event with an event: type and a data: line. measurement events carry a LiveMeasurement JSON object; the deviceId field is always present:

event: measurement
data: {"deviceId":"...","timestamp":"2026-04-27T15:34:30+02:00","power":275.0,...}

event: measurement
data: {"deviceId":"...","timestamp":"2026-04-27T15:34:32+02:00","power":280.0,...}

Fields are nullable; missing values are expected and vary by device hardware and firmware.

Event types

  • measurement: a live LiveMeasurement payload tagged with deviceId.
  • ping: an empty keep-alive sent every ~20s of silence to keep proxies from dropping the connection.
  • stream_error: a terminal stream error. JSON payload of shape {"code":"...","transient":bool,"retryAfter":int?,"deviceId":string?}, where transient indicates whether the client should retry, retryAfter (when present) hints at a backoff in seconds, and deviceId (when present) names the offending device. Codes:
    • upstream_unavailable: transient backend error; may retry after retryAfter seconds.
    • device_not_streamable: the named device can't be streamed right now (e.g. still setting up or recently removed). Not transient; drop that deviceId or re-check the discovery endpoint.
    • evicted: a newer connection took the home's slot. Not transient; do not reconnect immediately or you will evict whoever takes the slot next, causing a reconnect loop.

EventSource.onmessage does not fire for events with explicit types, so subscribe per event type:

const es = new EventSource(url);
es.addEventListener('measurement', (e) => { /* ... */ });
es.addEventListener('stream_error', (e) => { /* ... */ });

Clients and browser usage

Most clients can use any HTTP library that supports streaming responses and set the Authorization header normally. The browser EventSource API cannot set custom headers, so browser clients should either use a fetch-based polyfill such as @microsoft/fetch-event-source, or proxy the stream through their own backend.

On this page