Skip to main content
Every swap on a pool, pushed the moment it settles, with both legs priced in USD before it reaches you. No node, no Swap event decoding, no ABI per DEX. This page is the working reference: how to subscribe, what each field means, what the error codes are, the three things that will trip you up, and what it costs. Endpoint reference is at GET /sse/transactions and POST /sse/transactions.
This feed requires an API key. Unlike /sse/prices, which streams 36 showcase assets keylessly, transactions and reserves are key-only. Without one you get 403 and a JSON body, on a connection you expected to stay open.A free key takes a minute, needs no card, and opens all three feeds on every token we index.

Before anything else: pick your method carefully

Two methods, and the difference between them is three orders of magnitude in cost. Both figures were measured on 2026-08-21 in the same hour. token on a major asset is 208 times the traffic of a single pool, because a major asset is in thousands of pools. Since each delivered event costs one credit, that rate is your bill:
method: "token" on a top-20 asset will exhaust a free key over lunch. Use it for long-tail tokens that trade in a handful of pools, or when you genuinely need cross-pool coverage and have budgeted for it. For one venue, use pool.

Subscribe

Both forms need Authorization carrying the key as the entire header value. No Bearer, no other scheme word.
request_id is echoed on every event so you can tell subscriptions apart on a multiplexed connection. It is optional and defaults to the array index, which is fine until you reorder the array. Set it explicitly. Add ?limit=N to close the connection after N events. It is the cheapest way to sample a feed before you commit to it.

What an event looks like

A real capture from the Orca SOL/USDC pool:
The event name is the method: pool or token. Direction is not a field. Derive it from the sign of amount_0: negative means token_0 left the pool, so the trader bought it. In the event above amount_0 is positive, so SOL went in and USDC came out: a sell. There are no symbols and no decimals. REST rows carry token_0_symbol; stream events do not. If you need “1.13 WETH” rather than a raw integer, fetch decimals once from the pool endpoint and cache them. The _usd fields are already computed, so most consumers never need the raw amounts at all.

Three pitfalls

These are the ones that cost real time. Each was measured, not inferred.

1. token_0 is not the same token in REST and in the stream

Same pool, same minute, on Ethereum:
Both surfaces are internally consistent, so nothing looks wrong. If you join stream events to REST rows by position, you read WETH amounts as USDC and price them at a dollar. It does not reproduce everywhere. On Solana and on Base the two agree. The stream follows the pool’s on-chain ordering; REST puts the major token first; they coincide only when those happen to match, which they do on Base (0x4200… sorts below 0x8335…) and on Solana, and do not on Ethereum (0xa0b8… sorts below 0xc02a…).
Resolve tokens by address, never by index. Do not assume _0 means the same thing across two DexPaprika surfaces, and do not verify this on Solana only: the bug hides there.

2. Raw amounts arrive as JSON numbers, and JavaScript will round them

amount_0 and amount_1 are JSON numbers, not strings. For an 18-decimal token they exceed Number.MAX_SAFE_INTEGER routinely:
The reserves feed sends reserve, delta and block as strings precisely to avoid this. The transactions feed does not. This also hides on Solana. Nine-decimal SOL and six-decimal USDC keep raw amounts small: across 93 captured Solana events the largest absolute amount was 90,504,206,102, four orders of magnitude below the limit. Test on an 18-decimal EVM token or you will not see it. If you need exact raw amounts, parse with a big-integer-aware reader rather than JSON.parse, or work from the _usd fields, which are ordinary floats and safe. block_number is already a string for the same reason. Use Number() on it only for comparisons, never for arithmetic you care about.

3. volume_usd is about half the notional

Measured across both chains, volume_usd sits at 0.4999 to 0.5057 of each leg’s USD value:
The ratio is stable within a pool and differs slightly between pools, so treat it as “one side of the trade” rather than a formula. The practical consequence: do not sum volume_usd and compare it against a REST 24h volume figure without checking the convention, or you will be out by a factor of two. When you want the size of a trade, amount_0_usd and amount_1_usd are unambiguous.

Error codes

Every one of these was produced against the live endpoint. They arrive as an HTTP status with a JSON body, before the stream opens.
403 and 401 mean different things and the difference is useful. 403 is “you sent no key”. 401 is “you sent a key and it was rejected”. Branch on them: the first needs registration, the second needs a look at the header.

In-stream events

Once the stream is open, HTTP status no longer applies. Watch the event name: Filter on the event name. A handler that parses every data: line will try to read {"time":1787305761} as a swap.

Good practices

Trust the heartbeat, not the socket. A TCP connection that has silently died looks identical to a quiet pool. Pings arrive every 15 seconds; if you have seen nothing for 45, reconnect. Do not use “no swaps” as a liveness signal, because on a slow pool that is the normal state. Reconnect with backoff, and never in a tight loop. A retry storm turns one rate limit into a persistent one. Start at a second, double up to a minute, and add jitter so a fleet does not resynchronise. Multiplex deliberately. 25 subscriptions per connection, 10 concurrent connections per IP, so 250 subscriptions is the ceiling on a free key. Group by how you will process the data, not by chain: request_id is what routes an event, and it is per subscription. Deduplicate on tx_hash plus pool_id. A reconnect can redeliver the block you were in the middle of. One swap that touches two subscribed pools also arrives twice, once per request_id, which is correct but is not what a naive counter expects. Size the cost before you leave it running. Use ?limit=100 and time it. A hundred events tells you the rate, the rate tells you the monthly bill, and both take a minute to find out. Plan your credit usage works through the arithmetic. Persist what you need at write time. The feed is a live tail, not a queryable history. For backfill and for anything older than your connection, use the REST transactions endpoint.

A working consumer

Handles the event names, the heartbeat, reconnection, and the two pitfalls that bite in JavaScript.

Where to go next

Endpoint reference

Parameters, schemas and every event type.

Plan your credit usage

What a continuous stream costs, with worked models.

Production streaming

Reconnection, missed blocks and error classification. Written for reserves, applies here.

Pool reserves

The other key-only feed: liquidity movement per block.