TriportRPC

Heimdall CometBFT WebSocket fanout

GEThttps://api.triport.io/ws/polygon-heimdall

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

Polygonfeature polygon.heimdall_firehose (tier-gated)**pro or business** + per-key concurrent-stream cap

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 4000 after ~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.

Authorizationheaderrequired
Bearer $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.typestring
Event type, e.g. tendermint/event/NewBlock, tendermint/event/Tx.
result.data.value.block.header.heightstring
Heimdall block height (part of the dedup key).
result.data.value.block.header.chain_idstring
Consensus chain ID — always heimdallv2-137 for healthy upstreams.
result.events["tx.hash"]string[]
Transaction hash for Tx events (part of the dedup key).

Errors

Rejections before the WebSocket upgrade are returned as HTTP responses with the typed envelope {"error":{"code":"..."}}:

CodeHTTPMeaningWhen it happens
unauthorized401Missing/invalid keyNo resolvable user/plan from the Authorization header.
tier-locked403Plan not eligibleThe key's tier does not include the firehose feature (free/basic, or any tier while in preview).
stream-cap-exceeded429Too many streamsThe key already holds its maximum concurrent firehose streams.
no-upstream503No healthy nodeNo Heimdall consensus host is currently available to fan out from.

Rejections after the upgrade are signalled as WebSocket close codes:

Close codeMeaningWhen it happens
4000Stream TTL exceeded~1 hour elapsed; reconnect to continue.
4001Client goneThe connection was cancelled or the client disconnected.
1008Policy violationSlow consumer — server send-buffer overflowed (default 16 MiB).
4503Upstream unavailableEvery 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 4000 as a normal lifecycle event: reconnect immediately. For 4503, back off and retry — no upstream is currently serving. For 1008, 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.