At a glance
| Item | Detail |
|---|---|
| Address | wss://[stream hostname]/streams/v1/{record type} |
| Connection | WebSocket, one connection per record type |
| Authentication | API token, sent as Authorization: Bearer <token> |
| Record types | A, AAAA, CNAME, MX, NS, TXT |
| Data format | JSON records, several to a message, one per line (newline-delimited JSON) |
| Resume after a disconnect | Yes, from any position the stream still holds |
| Compression | Supported (permessage-deflate) |
Authentication
Real Time Streaming uses API tokens. Create one in the Spamhaus Customer Portal and send it in the Authorization header when you connect:
Authorization: Bearer <your API token>
The stream accepts API tokens only, not single sign-on. Your account also needs Real Time Streaming enabled. Tokens with an expiry date close the connection when they expire, so replace them before then and reconnect with the new token.
What you receive
Record types (one connection each)
| Connect to | To receive |
|---|---|
/streams/v1/ipv4 |
A records (IPv4 addresses) |
/streams/v1/ipv6 |
AAAA records (IPv6 addresses) |
/streams/v1/cname |
CNAME records (aliases) |
/streams/v1/mx |
MX records (mail servers) |
/streams/v1/ns |
NS records (name servers) |
/streams/v1/txt |
TXT records (text records) |
Each connection is independent and has its own position numbers. A position on one record type has no meaning on another.
What a record looks like
{"_idx":425679,"_ts":1758024000,"rrname":"example.com","rrtype":1,"rdata":"192.0.2.10"}
| Field | Meaning |
|---|---|
_idx |
The record's position in the stream. It only goes up. Save it, because it's what you resume from. |
_ts |
When the record was sent, in Unix time (seconds). |
rrname |
The DNS name observed. |
rrtype |
The record type as a number: 1 = A, 28 = AAAA, 5 = CNAME, 15 = MX, 2 = NS, 16 = TXT. |
rdata |
The record's value, such as an IP address or host name. |
New fields may be added over time, so build your client to ignore fields it doesn't recognise.
How it works
Once connected, your client controls the stream with four plain-text commands. Nothing is sent until your client sends one of them.
| Command | What it does |
|---|---|
start |
Starts sending new records from now. No history. |
resume <position> |
Sends records from that position onwards, then carries on with new records, with no gap and no repeat where the two meet. |
status |
Replies with the range of positions you can resume from, as status <oldest> <newest>. |
stop |
Stops sending and closes the connection. |
Picking up where you left off
Save the position of the last record you processed. When you reconnect, send resume with
that position plus one. Resuming at the same position sends that record again.
Positions don't last forever. Send status to see which positions you can still resume from,
and resume inside that range. If you've been disconnected for longer than that, send start
instead and accept the gap.
Connect
- Create an API token in the Spamhaus Customer Portal.
- Open a WebSocket connection to the record type you want, with the token in the Authorization header. Turn on compression if your WebSocket library supports it; it cuts the bandwidth used.
- Send
start, orresumewith your saved position plus one if you have one. - Split each message into lines and read each line as one JSON record. One message can hold many records.
- Save the
_idxof each record you process. - If the connection drops, wait a few seconds, reconnect and resume from your saved position plus one.
The technical reference includes a complete example client in Go that does all of this, including reconnecting automatically.
Good to know
- Read quickly. If your client falls behind, some records are dropped for that connection
so the stream doesn't stall. You can spot a drop because the
_idxnumbers skip. If you see gaps, read faster or do less work as each record arrives. - Expect the odd repeat. Reconnecting can send a record you've already seen. Remove
duplicates using
_idx. - Order is kept within each record type:
_idxonly goes up. - Keep the connection alive. The stream sends a ping every 30 seconds and expects a reply. Most WebSocket libraries reply automatically; check that yours does.
- One catch-up at a time. If a resume is still catching up, a second resume is ignored. Wait until new records are arriving before sending another.
Troubleshooting
| What you see | What to do |
|---|---|
| The connection is refused with HTTP 401 | Read the X-Auth-Error response header: it says whether the token is missing, wrongly formatted, invalid or expired, or whether the account doesn't have Real Time Streaming. Fix that before reconnecting; retrying with the same token won't help. |
| HTTP 404 | The record type in the address is wrong. Use one of the six in What you receive. |
| Connected, but nothing arrives | Send start or resume. The stream waits for a command. |
| Your parser fails when traffic is busy | A message can hold many records. Split it on new lines and read each line separately. |
Gaps in _idx |
Your client isn't reading fast enough. See "Read quickly" above. |
| The connection closes on its own after a long time | Your token may have expired. Reconnect with a current token and resume. |
Learn more
- Real Time Streaming technical reference
- Daily Downloads: archive files for your own systems
- API Reference
Want Real Time Streaming for your team? Talk to us about access.