Ethereum Pub/Sub WebSocket — /ws/eth
Subscribe over WebSocket to real-time Ethereum events — pending transactions, filtered logs, new block headers, and node sync state — using the standard `eth_subscribe` / `eth_unsubscribe` JSON-RPC shape.
/ws/eth is the Ethereum JSON-RPC Pub/Sub channel. Open a single WebSocket
connection, send an eth_subscribe request naming one of four subscription
types, and the server replies with a hex-encoded subscription id. From then
on the server pushes eth_subscription notification frames carrying that id and
the event payload until you send eth_unsubscribe (or the socket closes).
You can hold multiple subscriptions on one connection; each notification frame
identifies its source via params.subscription, so match incoming events to the
id you stored at subscribe time.
The four subscription types unlock at different tiers. Attempting to subscribe
to a type above your plan returns a forbidden error frame followed by a WS
close 4003 — the frame includes current_tier, required_tier, and an
upgrade_url so you can prompt for an upgrade without a second call.
Parameters
The eth_subscribe request params array holds the subscription type in
params[0] and an optional, type-specific filter/option object in params[1].
newPendingTransactionsobjectincludeFullTxbooleanoptionaltrue, each notification carries the full transaction body. Defaults to false (tx hash only).logsobjectaddressstring | string[]optionaltopicsarrayoptionalnull (wildcard for that position).Response
Subscribe acknowledgement — result is the hex subscription id to keep:
Notification (newPendingTransactions, hash mode):
{
"jsonrpc": "2.0",
"method": "eth_subscription",
"params": {
"subscription": "0x9cef478923ff08bf67fde6c64013158d",
"result": "0x68e63ee5b6f7e2b2e9e1cf0d2f5cda0c4f3a9b1d7e8c6a5b4c3d2e1f0a9b8c7d"
}
}Notification (newHeads):
{
"jsonrpc": "2.0",
"method": "eth_subscription",
"params": {
"subscription": "0x1a2b3c4d5e6f70819a0b1c2d3e4f5061",
"result": {
"number": "0x12a4f8b",
"hash": "0x4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c",
"parentHash": "0x9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f",
"nonce": "0x0000000000000000",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"logsBloom": "0x00000000000000000000000000000000",
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot": "0xdeadbeef00000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"miner": "0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5",
"difficulty": "0x0",
"extraData": "0x6265617665726275696c642e6f7267",
"gasLimit": "0x1c9c380",
"gasUsed": "0x10a3b2f",
"timestamp": "0x66f4c3a8",
"baseFeePerGas": "0x53724e1a"
}
}
}Notification (syncing, false once fully synced):
{
"jsonrpc": "2.0",
"method": "eth_subscription",
"params": {
"subscription": "0x7f6e5d4c3b2a19080716253443526170",
"result": false
}
}jsonrpcstring"2.0".idinteger | stringid.resultstringeth_unsubscribe and match against incoming params.subscription.Errors
The server may send an application-level error frame immediately before closing the socket with one of the canonical close codes.
| Close code | error | When it happens |
|---|---|---|
4001 | unauthorized | Missing or invalid credentials on the upgrade / auth frame. |
4003 | forbidden | Subscription type requires a higher tier, or the method is unknown. Frame carries current_tier, required_tier, method, and upgrade_url. |
4029 | rate_limited | Per-tier RPS exceeded. Frame carries retry_after_sec and limit_rps. |
4030 | trial_expired | Trial period has ended; frame carries upgrade_url. |
Example error frame (sent just before a 4003 close):
{
"error": "forbidden",
"message": "subscription type 'newHeads' requires the pro tier",
"current_tier": "basic",
"required_tier": "pro",
"method": "newHeads",
"category": "eth_ws_pubsub",
"upgrade_url": "https://triport.io/upgrade?tier=pro"
}See errors.md for the full error envelope and shared close-code reference.
Examples
JavaScript (fetch)
WebSockets are not opened with fetch; use the browser/Node WebSocket API:
const ws = new WebSocket("wss://ws.triport.io/ws/eth", {
headers: { Authorization: `Bearer ${process.env.TRIPORT_API_KEY}` },
});
const subs = new Map(); // subscription id -> type
ws.addEventListener("open", () => {
ws.send(JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_subscribe",
params: ["newHeads"],
}));
});
ws.addEventListener("message", (ev) => {
const msg = JSON.parse(ev.data);
if (msg.id === 1 && msg.result) {
subs.set(msg.result, "newHeads"); // store the hex id
} else if (msg.method === "eth_subscription") {
console.log("block", msg.params.result.number, "from", msg.params.subscription);
}
});TypeScript SDK (@triport/sdk)
import { TriportWS } from "@triport/sdk";
const ws = new TriportWS({ apiKey: process.env.TRIPORT_API_KEY! });
const eth = await ws.eth.connect();
// logs for USDT Transfer events
const sub = await eth.subscribe("logs", {
address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
topics: ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
});
sub.on("data", (log) => console.log(log.transactionHash, log.data));
// later
await eth.unsubscribe(sub.id);Python (triport-sdk)
import os
from triport import TriportWS
ws = TriportWS(api_key=os.environ["TRIPORT_API_KEY"])
with ws.eth() as eth:
sub = eth.subscribe("newPendingTransactions", {"includeFullTx": False})
for tx_hash in sub:
print("pending:", tx_hash)
# eth.unsubscribe(sub.id) to stopNotes
- One id per subscription. Store the hex
resultfrom each acknowledgement; it is the only way to correlate notifications (params.subscription) and toeth_unsubscribelater. logstopic slots.topicsaccepts up to four positions. Usenullin a slot to wildcard it, or an array of strings to OR-match several values in that position.newPendingTransactionsvolume. Hash-only mode (the default) is far lighter thanincludeFullTx=true; enable full bodies only when you actually need the decoded transaction.- Tier gating is per subscription type, not per connection — a free key can
open
newPendingTransactionsbut will be closed with4003if it triesnewHeads. - Polygon exposes the same four subscription types on
/ws/polygon. Solana pub/sub lives at/ws/sol. See the streaming overview for all channels.