Heimdall CometBFT WebSocket fanout
https://api.triport.io/ws/polygon-heimdallA single WebSocket that delivers a merged, de-duplicated stream of Polygon **Heimdall** consensus (CometBFT/Tendermint) events, fanned out across several upstream consensus nodes for resilience.
Polygon's consensus layer, Heimdall, runs on CometBFT (formerly Tendermint)
and exposes a /websocket event subscription on its RPC port. This endpoint is
a server-side fan-out: when you connect, the Triport edge opens streams to
several healthy Heimdall consensus nodes in parallel (default 3), merges
their event streams into one, and removes duplicates so you see each consensus
event exactly once.
The de-duplication key is (height, type, tx_hash), so the same new-block or
transaction event arriving from multiple upstreams is collapsed to a single
frame. Because more than one upstream is feeding the stream, a single node
falling behind or dropping out does not interrupt delivery — the merged stream
keeps flowing as long as at least one upstream is alive.
Use this when you need real-time consensus signals — new Heimdall blocks,
checkpoint and validator events, transaction events — pushed to you as they
happen. For one-off queries of the same consensus data (validator sets,
tx_search, /status, Cosmos LCD routes), use the request/response
Heimdall consensus RPC proxy instead. The rule of thumb:
streaming → this channel; point-in-time reads → the RPC proxy.
Gotchas
- One-hour stream TTL. Every connection is closed with code
4000after ~1 hour. Reconnect on that close — it is expected, not an error. - Slow consumers are dropped. If you stop reading and the server-side send
buffer exceeds its limit (default 16 MiB), the stream is force-closed with
code
1008. Keep up with the stream or apply backpressure on your side. - Concurrent-stream cap. Each API key may hold only a limited number of
simultaneous firehose streams; the limit scales with tier. Opening one beyond
the cap is rejected at the handshake (
stream-cap-exceeded).
Parameters
This is a WebSocket upgrade, not a JSON-RPC call — there are no query or body parameters. The connection itself is the request.
AuthorizationheaderrequiredBearer $TRIPORT_API_KEY. Identifies the key/plan for the tier-gate and concurrency cap.Response
After a successful upgrade, the server pushes merged CometBFT event frames. Each frame is a Tendermint RPC event-subscription result, for example a new Heimdall block:
result.data.typestringtendermint/event/NewBlock, tendermint/event/Tx.result.data.value.block.header.heightstringresult.data.value.block.header.chain_idstringheimdallv2-137 for healthy upstreams.result.events["tx.hash"]string[]Tx events (part of the dedup key).Errors
Rejections before the WebSocket upgrade are returned as HTTP responses with
the typed envelope {"error":{"code":"..."}}:
| Code | HTTP | Meaning | When it happens |
|---|---|---|---|
unauthorized | 401 | Missing/invalid key | No resolvable user/plan from the Authorization header. |
tier-locked | 403 | Plan not eligible | The key's tier does not include the firehose feature (free/basic, or any tier while in preview). |
stream-cap-exceeded | 429 | Too many streams | The key already holds its maximum concurrent firehose streams. |
no-upstream | 503 | No healthy node | No Heimdall consensus host is currently available to fan out from. |
Rejections after the upgrade are signalled as WebSocket close codes:
| Close code | Meaning | When it happens |
|---|---|---|
4000 | Stream TTL exceeded | ~1 hour elapsed; reconnect to continue. |
4001 | Client gone | The connection was cancelled or the client disconnected. |
1008 | Policy violation | Slow consumer — server send-buffer overflowed (default 16 MiB). |
4503 | Upstream unavailable | Every upstream consensus node failed to dial, or all merged upstreams disconnected. |
The shared firehose family also reserves 4403 (tier-locked) and 4429
(stream-cap-exceeded) for tier rejections; on this channel those checks run at
the handshake and surface as the HTTP 403/429 above. See the shared
errors reference for the full envelope. Rate limiting is
RPS-per-tier with burst plus the per-key concurrent-stream cap; there is no
daily quota.
Examples
JavaScript (fetch)
WebSocket connections use the WebSocket API rather than fetch. In the
browser, pass the key via a subprotocol or a short-lived connect token (browser
WebSocket cannot set arbitrary headers); on the server use the headers
option:
import WebSocket from "ws";
const ws = new WebSocket("wss://api.triport.io/ws/polygon-heimdall", {
headers: { Authorization: `Bearer ${process.env.TRIPORT_API_KEY}` },
});
ws.on("message", (buf) => {
const frame = JSON.parse(buf.toString());
if (frame.result?.data?.type === "tendermint/event/NewBlock") {
console.log("Heimdall height:", frame.result.data.value.block.header.height);
}
});
ws.on("close", (code) => {
// 4000 = TTL; reconnect to keep streaming.
if (code === 4000) reconnect();
});TypeScript SDK (@triport/sdk)
import { Triport } from "@triport/sdk";
const triport = new Triport({ apiKey: process.env.TRIPORT_API_KEY! });
const stream = triport.polygon.heimdall.firehose();
stream.on("event", (frame) => {
console.log(frame.result.data.type, frame.result.data.value);
});
// The SDK auto-reconnects on the 4000 TTL close by default.
stream.on("close", ({ code }) => console.log("closed", code));Python (triport-sdk)
import os
from triport import Triport
client = Triport(api_key=os.environ["TRIPORT_API_KEY"])
# Yields merged, de-duplicated CometBFT event frames until the stream closes.
for frame in client.polygon.heimdall.firehose():
data = frame["result"]["data"]
if data["type"] == "tendermint/event/NewBlock":
print("height:", data["value"]["block"]["header"]["height"])Notes
- De-duplication window. Events are de-duped by
(height, type, tx_hash)within a rolling window (default ~10 minutes), which is comfortably longer than the interval over which the same consensus event could re-arrive from different upstreams. - Reconnect strategy. Treat close
4000as a normal lifecycle event: reconnect immediately. For4503, back off and retry — no upstream is currently serving. For1008, fix consumer throughput before reconnecting. - Related endpoints. For request/response access to the same consensus
data (validator sets, checkpoints,
tx_search, Cosmos LCD), see the Heimdall consensus RPC proxy. For Bor execution-layer pending-transaction streaming, see the Bor firehose/ws/polygon-bor-firehose. - See the shared authentication and rate limits guides for how the Bearer key and per-tier limits apply across the platform.