Skip to main content
New to Parsed Streams? Read the mental model first — it explains why filters look the way they do.

Quickstart

1

Get Access

Parsed Streams is in closed beta. The Helius team whitelists your project id and shares the connection endpoint with you. To join the closed beta, apply here.Authenticate with your project’s API key, passed as the api-key query parameter (or the x-api-key header).
2

Connect

wscat
A missing, invalid, or non-whitelisted key is rejected with HTTP 401. A project at its connection cap gets HTTP 429.
3

Subscribe with a Filter

Send parsedTransactionSubscribe with a filter and optional options:
The response result is an integer subscription id:
4

Read a Notification

Every matching transaction arrives as a parsedTransactionNotification, already decoded, with matchedIndexes pointing at the instructions your filter hit. See Notifications for the full shape.
5

Unsubscribe

Or just close the connection — it removes all its subscriptions.

Guides

Track Jupiter Swaps

Use describeProgram to build a filter you can trust before you subscribe.

Track Pump.fun Mints

A reconnect-safe listener that logs every new Pump.fun token deploy.

Handling Reconnects

Survive idle timeouts and deploys, then backfill exactly what you missed.

Protocol Reference

Parsed Streams uses JSON-RPC 2.0 over a single WebSocket connection. Each request receives a response with the same id. A subscription then pushes parsedTransactionNotification messages until you unsubscribe or disconnect.

Subscribe

Send parsedTransactionSubscribe with a filter and optional options. The response result is an integer subscription id.
Request
Response

Filter fields

At least one of programs or accounts.include is required. The fields you set combine with AND: an instruction must satisfy all of them to match.
string[]
Program IDs to match (base58 addresses, not names). An instruction matches if its program is in this list. OR within the list.
string[]
Decoded instruction names, such as route. Matched exactly first, then with a case and separator insensitive fallback, so sharedAccountsRoute also matches the wire name shared_accounts_route. OR within the list. Only instructions whose name the catalog could identify can match, so take names from describeProgram.
string[]
Account addresses. An instruction matches if any of these appears in its account list. OR within the list. Works for every instruction, decoded or not. The program id itself does not count as an account here.
object
A map of decoded account role name to address, such as { "user_transfer_authority": "<pubkey>" }. Every entry must hold (AND across entries), and the instruction must be decoded for this to apply. Role names match exactly, with no case folding, so copy them from describeProgram rather than guessing.
boolean
default:"false"
Include instructions from failed transactions.
boolean
default:"true"
Inner (CPI) instructions are eligible to match. Set false to match top-level instructions only.
Unknown fields anywhere in the filter or options are rejected with -32602 rather than silently ignored, so typos fail loudly instead of matching nothing.

Options

The second param is optional.
string
default:"confirmed"
Only confirmed is supported.
string
default:"full"
What each notification carries. full: the whole transaction, every instruction, plus matchedIndexes pointing at the filter hits. matched: only the instructions that matched, no index list. raw: matched instructions only, each reduced to its position, programId, and base58 data blob, with no decoded fields and no accountKeys array. Use matched when bandwidth matters more than context (full payloads average roughly three times the size), and raw when you decode instruction data yourself and only need the bytes.
A project may hold up to 100 concurrent connections, shared across all of its API keys.

Notifications

One notification per matching transaction per subscription. With the default details: "full":
Reading it:
  • transaction is the full context. fee is in lamports. accountKeys is the complete key list, including keys loaded from address lookup tables, in the same order the chain reports them. feePayer is always accountKeys[0]. error carries the transaction error as structured JSON, for example {"InstructionError": [2, {"Custom": 6001}]}, when status is "error".
  • summary has one shape everywhere it appears: a type (such as swap or transfer), a human-readable description, and a structured parsedData payload when the parser recognizes the action — for a swap: the protocol, amounts, and mints. transaction.summary labels the transaction’s headline action; each recognized instruction carries its own summary with the same shape. To collect every swap in a transaction, iterate instructions and read summary.parsedData where summary.type is "swap".
  • nativeTransfers and tokenTransfers list the SOL and token movements the parser extracted from the whole transaction, in the same shape the Parsed Events API returns, so stream and API consumers can share processing code. Both are always present, possibly empty.
  • instructions is every instruction of the transaction in execution order: each top-level instruction followed by its inner instructions. Each entry carries its own position: topIndex is which top-level instruction it belongs to (starting at 0), innerIndex is its position among that instruction’s inner calls (null means it is the top-level instruction itself), and stackHeight is the call depth (1 for top level). Use these, not the array position.
  • matchedIndexes are indices into instructions telling you which ones your filter actually hit. The rest are there for context. With details: "matched" the array contains only the hits and matchedIndexes is absent.
  • decoded names are snake_case (in_amount, user_transfer_authority), as published in the program’s IDL. Integer arguments are commonly strings ("1000000") because u64 values do not fit in JavaScript numbers.
  • blockTime is currently always null. Do not build on it.
  • Expect a mix of decoded and undecoded instructions inside one transaction: a fully decoded swap can sit next to an unrecognized memo. Branch on decoded: when it is null, the instruction carries rawData (base58 bytes) and rawAccounts (plain pubkey list) instead, so you always have something to work with.
With details: "raw" the value shrinks to transaction meta and blobs. accountKeys, nativeTransfers, tokenTransfers, matchedIndexes, and all decoded fields are gone (the transaction summary is still included); each matched instruction is its position, its program, and its data bytes in base58, exactly as they appear on chain (present even for instructions the catalog could have decoded):

Unsubscribe

Returns true if the subscription existed and was yours. Notifications stop immediately. Closing the connection removes all its subscriptions.

Discovery

The most common failure with this kind of API is a filter that is valid but matches nothing, usually a guessed instruction or role name. describeProgram prevents that by returning the exact names the matcher compares against:
Request
Response
You can pass a program address or a catalog name, but prefer the address: names can be ambiguous across program versions (more than one catalog entry is named jupiter, and a name lookup can resolve to the older one). If you do look up by name, check that result.id is the program you intend to subscribe to. Recommended flow: describeProgram to get the exact instruction and role names, build the filter with those names, then subscribe. The Track Jupiter Swaps guide walks through this end to end.

Limits

Errors

Errors follow JSON-RPC 2.0: { "error": { "code": <int>, "message": "<text>" }, "id": <id> }. Messages say exactly what was wrong and where. Connections can also close with a WebSocket close code — see Handling Reconnects for what each one means and how to recover.

Client Examples