Connection & errors
You never open or close a connection yourself — subscriptions do it for you.
One connection per network
All the subscriptions of a network created from the same ChainGate instance — from any explorer or connector — share a single connection. It opens with the first subscription and closes when the last one is removed, so a process that has unsubscribed from everything can exit normally.
const cg = new ChainGate({ apiKey: 'your-api-key' })
const btc = cg.explore(cg.networks.bitcoin)
const blocks = btc.onBlock(handleBlock) // opens the Bitcoin connection
const balance = btc.onBalance('bc1q...', handleBalance) // reuses it
blocks.unsubscribe()
balance.unsubscribe() // last one: the connection closes
Subscribing twice to the same thing — the same address on the same event, even written differently (checksummed or lowercase EVM address, CashAddr or legacy Bitcoin Cash address) — results in a single server-side subscription with two callbacks.
Automatic reconnection
If the connection drops — network hiccup, a laptop waking up, a server-side restart — it is re-established automatically with a growing delay (from one second up to thirty), and every active subscription is restored. Your callbacks keep working without any action on your side, and subscription.ready promises that were pending resolve once their subscription is back.
A connection that goes silent is checked with a lightweight ping (after five minutes of silence with an API key, one hour without) and re-established if it does not answer.
Events that happened while the connection was down are not replayed. If your application needs to stay exact after a gap, register an onError() listener — it is called when the connection drops — and refresh the state you display from the explorer or connector methods:
btc.onError(async (error) => {
if (error instanceof EventStreamError) {
// The connection dropped (it reconnects on its own). Catch up:
const { confirmed } = await btc.getAddressBalance(address)
render(confirmed)
}
})
Errors
onError() registers a listener for everything that goes wrong on a network's connection. It returns a function that removes the listener.
import { EventStreamError, EventSubscriptionError, RateLimitError, RateLimitQuotaError } from 'chaingate'
const remove = btc.onError((error) => {
if (error instanceof RateLimitError) {
// Keyless rate limit reached: get a free API key at https://api.chaingate.dev
} else if (error instanceof RateLimitQuotaError) {
// Your plan's WebSocket rate limit or daily quota was reached
} else if (error instanceof EventSubscriptionError) {
// A subscription was refused by the server
} else if (error instanceof EventStreamError) {
// The connection failed or was closed: error.code, error.reason
}
})
| Error | When |
|---|---|
EventSubscriptionError | Thrown immediately by onBalance() & co. when the address is not valid for the network; used to reject subscription.ready when the server refuses a subscription |
RateLimitError | The connection was closed because the keyless rate limit was reached |
RateLimitQuotaError | The connection was closed because your plan's rate limit or daily quota was reached |
EventStreamError | Any other failure: the connection dropped, was rejected (for example an API key that is not valid), or was closed because events were not being read fast enough |
Exceptions thrown by your own callbacks are reported here as well, so one failing callback never affects the others.
Rate-limit closures are followed by a longer pause before reconnecting. A rejected API key is final: the connection is not retried until you subscribe again.
Limits
- Subscriptions count, events don't. Each subscription and cancellation is one request against the WebSocket quota of your plan; the events you receive are free.
- Batches are paced. Subscriptions are sent at a steady pace — five right away, then one every few seconds — so a large batch never trips the rate limit.
subscription.readytells you when each one is confirmed. - Five connections per IP address. One connection serves a whole network, so this is the number of networks a single machine can follow at the same time.
- Read your events. A connection that does not keep up with the events it receives (for example a full-block subscription on a slow consumer) is closed by the server and reconnected; keep callbacks light.
Node.js and browsers
Events work wherever the library does: Node.js 22 or newer, Bun, and modern browsers — all of them provide the WebSocket implementation the library uses, no extra dependency required. The API key is passed in the connection URL, so events work from the browser too.