Developers

Companies House Streaming API: streams, stream keys, timepoints and a reconnecting client

Updated By
Short answerThe Companies House Streaming API pushes changes to company profiles, filings, officers, PSCs, charges, insolvency cases, disqualified officers, exemptions and PSC statements over a long-running HTTPS connection to stream.companieshouse.gov.uk. It needs its own stream key, allows two connections per account and lets you resume from a timepoint after a disconnect.

What the Streaming API is

The Companies House Streaming API is a set of long-running HTTP endpoints that push register changes to your client as they happen, instead of you polling the REST API. In the words of the overview guide, it "delivers the same information that is available through the on-demand REST API GET requests, but instead pushes data to your client as it changes". Conceptually it is like downloading an infinitely long file: one JSON document per line, for as long as the connection stays open.

Use it when you keep your own copy of the register, or a large part of it, and need it current within minutes. If you only care about a few dozen companies, a scheduled REST check or a watchlist is simpler (see the last section).

The nine streams

Each stream has its own path on https://stream.companieshouse.gov.uk and returns the same resource as the matching REST endpoint. The reference lists nine:

StreamPathWhat changes it carries
Company information/companiesCompany profile: name, status, registered office, accounts and confirmation statement dates
Filing history/filingsNew filing history items and their document links
Officers/officersDirector and secretary appointments and changes
Persons with significant control/persons-with-significant-controlPSC notifications and changes
PSC statements/persons-with-significant-control-statementsStatements such as "no registrable person"
Charges/chargesMortgages and charges, satisfactions
Insolvency cases/insolvency-casesInsolvency case records
Disqualified officers/disqualified-officersDisqualification records
Company exemptions/company-exemptionsExemptions, for example from PSC requirements

There is no filter parameter. The only query parameter is timepoint, so every client receives every event on that stream and filters on its side.

The event envelope

Every line on every stream uses the same envelope, and the resource itself sits in data:

{
  "resource_kind": "company-profile#company-profile",
  "resource_uri": "/company/00445790",
  "resource_id": "00445790",
  "data": { "company_number": "00445790", "company_status": "active" },
  "event": {
    "type": "changed",
    "timepoint": 123456789,
    "published_at": "2026-10-21T10:52:22",
    "fields_changed": ["accounts.next_due"]
  }
}

The example is abridged and the values are illustrative. event.type is changed or deleted, and data may be absent on a deleted event. fields_changed lists changed fields in dot notation, which is the cheapest way to react only to what matters, for example company_status.

Authentication: a separate stream key

The Streaming API uses HTTP Basic auth with a stream key as the username and an empty password, exactly like the REST API, but the keys are different. The authentication guide says applications "must be registered as such, the REST API and streaming API keys are not interchangable". On the Developer Hub, create an application and choose the stream key client type.

curl -u "$CH_STREAM_KEY:" "https://stream.companieshouse.gov.uk/companies"

The colon after the key matters: it marks an empty password before base64 encoding. We checked on 21 October 2026 that a REST key gets a 401 from stream.companieshouse.gov.uk. Other 401 and 403 causes, including Restricted IPs on the key, are in Companies House API 401 and 403 errors.

Timepoints: resuming after a disconnect

Every event carries a timepoint, and reconnecting with ?timepoint=<last processed value> replays everything after it. Each stream is backed by a queue so that clients can disconnect and come back later. Without a timepoint, you only receive events that happen after you connect.

Three rules from the overview guide:

  • Store the timepoint of the last successfully processed event, not the last received one.
  • If the timepoint is too far in the past, or you consume too slowly, you get 416. Companies House does not publish how far back the queue goes, so treat 416 as "resync from a snapshot or the REST API".
  • Companies House produces snapshot datasets that include the timepoint at which they were taken, so you can load a snapshot and start the stream from there. The free monthly bulk files are described in Companies House bulk data; check that the snapshot you load states a timepoint before you rely on it for a seamless start.

A reconnecting client in Python

The client below follows the documented rules: it skips heartbeats, saves the timepoint after each processed event, waits a minute on 429, resets on 416 and backs off on network errors.

Companies House Streaming API: what a reconnecting client does on each response COMPANIES HOUSE STREAMING API CLIENT Connect, read, save the timepoint, repeat Connect with a stream key GET stream.companieshouse.gov.uk /companies?timepoint=<last> 200 Read line by line no buffering: one JSON document per line Blank line heartbeat: skip it JSON event handle(), filter Save event.timepoint of the last processed event, not received 401 Stop: wrong key type REST and stream keys are not interchangeable 429 Wait one minute, then reconnect required: ignoring it extends the limit 416 Drop the timepoint, wait 10 s too old or too slow: resync from a snapshot or REST network Wait 5 s, then reconnect connection error or a silent socket past the timeout closed Wait 10 s, reconnect with the timepoint the queue replays every event after it 9 streams no filter: every client gets every event on its stream 2 connections per account: a third one closes the oldest Idempotent after a reconnect you may see the same event twice
What a reconnecting stream client does on each response, and why it saves the timepoint only after processing an event.
import json
import os
import time
from pathlib import Path

import requests

STREAM_KEY = os.environ["CH_STREAM_KEY"]  # a stream key, not a REST key
URL = "https://stream.companieshouse.gov.uk/companies"
STATE = Path("companies.timepoint")
WATCH = {"00445790", "00519500"}  # company numbers you care about


def handle(msg):
    data = msg.get("data") or {}
    number = data.get("company_number") or msg["resource_uri"].rsplit("/", 1)[-1]
    if number not in WATCH:
        return
    event = msg["event"]
    print(event["published_at"], number, event["type"],
          data.get("company_status"), event.get("fields_changed"))


def run():
    while True:
        params = {}
        if STATE.exists():
            params["timepoint"] = STATE.read_text().strip()
        try:
            with requests.get(URL, auth=(STREAM_KEY, ""), params=params,
                              stream=True, timeout=(10, 300)) as r:
                if r.status_code == 401:
                    raise SystemExit("401: check you are using a stream key")
                if r.status_code == 429:
                    time.sleep(60)  # required: wait one minute
                    continue
                if r.status_code == 416:
                    STATE.unlink(missing_ok=True)  # timepoint too old: resync
                    time.sleep(10)
                    continue
                r.raise_for_status()
                for line in r.iter_lines(chunk_size=None):
                    if not line.strip():
                        continue  # heartbeat: a blank line
                    msg = json.loads(line)
                    handle(msg)
                    STATE.write_text(str(msg["event"]["timepoint"]))
        except (requests.ConnectionError, requests.Timeout,
                requests.exceptions.ChunkedEncodingError):
            time.sleep(5)  # transient network error
            continue
        except requests.HTTPError:
            pass
        time.sleep(10)  # stream closed or retryable error


if __name__ == "__main__":
    run()

Run it with CH_STREAM_KEY=... python stream.py. chunk_size=None makes requests hand over data as it arrives instead of waiting to fill a buffer. The 300-second read timeout is a safety net: heartbeats keep a healthy connection busy, and a silent socket is treated as dead.

What we tested. We do not hold a stream key, so we have not run this client against a live stream. On 21 October 2026 we compiled it, ran handle() on sample envelopes and confirmed that the endpoint returns 401 without a valid stream key. The reconnect logic follows the documented status codes.

For production, write the timepoint to your database in the same transaction as the change, or every few hundred events, rather than to a file on each line, and make handle() idempotent: after a reconnect you may see an event twice.

Pitfalls

Most broken stream clients fail on one of these:

  • Heartbeats. The API sends a blank line periodically to keep the connection alive. Parsing it as JSON throws; skip it.
  • Buffering. A proxy or HTTP client that buffers the response until the connection closes will never deliver anything. Read line by line.
  • Two connections per account. A third connection closes the oldest one. Two reconnecting workers on the same credentials can keep disconnecting each other.
  • Reconnect storms. Reconnecting is expensive for Companies House. On 429 you must wait one minute; ignoring it extends the limit, and clients that never back off can have their IP blocked. Retry other retryable errors after 10 seconds.
  • Slow consumers. If your handler is slower than the stream, the server-side queue grows and the connection is closed. Push events onto your own queue and process them elsewhere.
  • Wrong key type. REST keys and stream keys are not interchangeable, and REST rate limits (600 requests per five minutes per key) are a separate matter: see rate limits.

What the stream does not carry

The stream tells you that something changed, not the balance sheet behind it. The company stream carries the profile, including accounts due dates and the overdue flag, and the filings stream tells you that new accounts were filed with a link to the document. Neither carries net assets, cash or creditors as data. To get figures you fetch the document and parse iXBRL yourself, and large groups often file PDF only. Field coverage for parsed figures is in Companies House financial data as JSON.

A lighter option for a list of companies

If you are monitoring tens of companies rather than the whole register, a stream is a lot of infrastructure: a stream key, a process that never stops, timepoint storage and filtering of every event in the UK. Overdue accounts are a common trigger, and the volume is real: on 1 October 2026, 93,785 active companies were past their accounts due date (overdue accounts statistics).

Our watchlist does the filtering and the accounts parsing for you. Add company numbers with POST /v1/watchlist/add and poll GET /v1/alerts?format=json once a day, or use the watch_add and get_alerts tools on our MCP server. Alerts cover status changes, overdue accounts, newly published accounts, new charges, negative net assets and falls of 30% or more in net assets or cash. The free plan watches 5 companies and API Pro watches 50; the quickstart has working examples. Our register snapshot is monthly, so for minute-by-minute status changes the stream is still the right tool. Get a free key on the developer docs page.

Sources

FAQ

Do I need a separate key for the Companies House Streaming API?

Yes. Register an application on the Developer Hub with the stream key client type. Companies House says REST API and streaming API keys are not interchangeable, and a REST key returns 401 on stream.companieshouse.gov.uk. Authentication is otherwise the same: HTTP Basic with the key as username and an empty password.

Can I stream changes for only my companies?

No. The streams take no filter apart from a timepoint, so you receive every event on that stream and filter by company number in your own code. For a short list of companies, a scheduled REST check or a watchlist service is usually cheaper to run than a permanent stream connection.

What does a 416 error from the Streaming API mean?

The timepoint you sent is invalid or too old for the queue behind the stream, or your client consumed events too slowly. Reconnect without a timepoint, then fill the gap from a snapshot or the REST API. Companies House does not publish how far back timepoints remain valid, so store yours often.

How many connections can I open to the Streaming API?

Two concurrent connections per account. A further connection closes the oldest one, so several workers sharing one key will disconnect each other. Repeated reconnecting also triggers rate limiting with a 429, after which you must wait one minute before trying again.

Does the Streaming API include company accounts figures?

No. The filing history stream tells you that accounts were filed and links to the document, and the company stream updates due dates and the overdue flag. Balance sheet numbers are not sent as data; you have to download the filing and parse the iXBRL, or use a service that has parsed it already.

Try it with a free key. 50 requests a month, REST and MCP, no card. Company register, iXBRL financial history since 2008 and watchlist alerts.

Get a free API key