stellar_subscribe
WebSocket · Send JSON-RPC messages after opening a connection.
- Stellar Mainnet:
wss://stellar-mainnet.g.alchemy.com/v2/YOUR_API_KEY - Stellar Testnet:
wss://stellar-testnet.g.alchemy.com/v2/YOUR_API_KEY
Receive new Stellar ledgers, contract events, transactions, and classic payments over one WebSocket connection. You can subscribe to mainnet or testnet and narrow notifications with topic-specific filters.
Create an API key in the Alchemy Dashboard. Choose the endpoint for your network and replace YOUR_API_KEY:
| Network | WebSocket URL |
|---|---|
| Mainnet | wss://stellar-mainnet.g.alchemy.com/v2/YOUR_API_KEY |
| Testnet | wss://stellar-testnet.g.alchemy.com/v2/YOUR_API_KEY |
Send the messages below over the WebSocket after it opens. These methods use positional JSON-RPC params arrays. They are WebSocket methods, not HTTP JSON-RPC calls, and do not use eth_subscribe.
This JavaScript example uses the built-in WebSocket client in Node.js 22 or later. Set ALCHEMY_API_KEY in your environment before running it:
const apiKey = process.env.ALCHEMY_API_KEY;
if (!apiKey) throw new Error('Set ALCHEMY_API_KEY');
const ws = new WebSocket(
`wss://stellar-mainnet.g.alchemy.com/v2/${encodeURIComponent(apiKey)}`,
);
ws.addEventListener('open', () => {
ws.send(JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'stellar_subscribe',
params: ['newLedgers'],
}));
});
let subscriptionId;
ws.addEventListener('message', ({ data }) => {
const message = JSON.parse(data);
if (message.error) {
console.error(message.error);
} else if (message.id === 1) {
subscriptionId = message.result;
console.log('Subscribed:', subscriptionId);
} else if (message.method === 'stellar_subscription') {
console.log(message.params.subscription, message.params.result);
}
});
ws.addEventListener('error', () => console.error('WebSocket connection failed'));
ws.addEventListener('close', ({ code, reason }) => console.log('Closed:', code, reason));
// When finished, call this while the connection is still open.
function unsubscribe() {
if (subscriptionId && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
jsonrpc: '2.0', id: 2, method: 'stellar_unsubscribe',
params: [subscriptionId],
}));
}
}The example shows one connection lifecycle. Add reconnect, backfill, and deduplication for applications that need complete records; see Recover after a disconnect.
Send stellar_subscribe with [topic] or [topic, filter]. An omitted filter or {} matches all eligible records. null is not a filter object. Use a unique request ID to associate each acknowledgement with its request.
{"jsonrpc":"2.0","id":1,"method":"stellar_subscribe","params":["newLedgers"]}The response contains an opaque subscription ID. Keep it to route notifications and unsubscribe on the same connection:
{"jsonrpc":"2.0","id":1,"result":"0x0123456789abcdef0123456789abcdef"}Notifications have no request id. params.result contains one record, not an array or an RPC pagination envelope:
{
"jsonrpc": "2.0",
"method": "stellar_subscription",
"params": {
"subscription": "0x0123456789abcdef0123456789abcdef",
"result": {
"ledger": 5193687,
"txHash": "de3197681ae894f6256ec97c9b2e472bd139f59194c40de0b4b92de603369a84",
"createdAt": 1791992022,
"operationIndex": 0,
"type": "payment",
"from": "GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST",
"to": "GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR",
"operation": {
"destination": "GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR",
"asset": "native",
"amount": "1"
}
}
}
}This is a payments notification. Keep the mapping between each subscription ID and its topic; the notification envelope does not repeat the topic name.
| Topic | One notification contains | Optional filters |
|---|---|---|
newLedgers | One closed ledger | None |
events | One Stellar event, including Soroban events | type, contractIds, topics |
transactions | One included transaction, successful or failed | status, accounts |
payments | One classic payment or path-payment operation from a successful transaction | accounts, from, to |
Different subscriptions can overlap. The same transaction can produce transaction, event, and payment notifications. Do not treat notifications across subscriptions as a globally ordered stream.
{"jsonrpc":"2.0","id":10,"method":"stellar_subscribe","params":["newLedgers"]}The result fields are:
| Field | JSON type | Meaning |
|---|---|---|
sequence | integer | Ledger sequence number |
hash | string | Ledger hash as hexadecimal |
ledgerCloseTime | string | Unix timestamp in seconds, encoded as a decimal string |
headerXdr | string | Base64-encoded ledger header XDR |
metadataXdr | string | Base64-encoded ledger metadata XDR |
Example result (XDR strings are abbreviated placeholders):
{
"sequence": 5193684,
"hash": "0255a52580756425bdf01702907832873c1afdb574f843411dd31429e329540a",
"ledgerCloseTime": "1791992007",
"headerXdr": "<base64 ledger header XDR>",
"metadataXdr": "<base64 ledger metadata XDR>"
}There is no ledger-range, starting-ledger, or replay filter. Ledger payloads can be large; configure your WebSocket client's incoming message size appropriately.
Use events for contract events, including Soroban token transfers:
{"jsonrpc":"2.0","id":20,"method":"stellar_subscribe","params":["events",{"type":"contract","contractIds":["CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"]}]}| Filter | Shape | Meaning |
|---|---|---|
type | "contract" or "system" | Match the exact event type; omit to receive all returned event types |
contractIds | Array of 1–100 C StrKeys | Match any listed contract |
topics | Array of 1–5 topic patterns | Match any pattern; each pattern contains 1–4 segments |
Within a pattern, each position must match. "*" matches exactly one segment. Other segments are base64 XDR values, not decoded text or addresses. Each encoded segment is limited to 4,096 characters. A pattern must have the same number of segments as the event.
To match common three-segment and four-segment transfer event formats, include both patterns:
{
"jsonrpc": "2.0",
"id": 21,
"method": "stellar_subscribe",
"params": ["events", {
"type": "contract",
"topics": [
["AAAADwAAAAh0cmFuc2Zlcg==", "*", "*"],
["AAAADwAAAAh0cmFuc2Zlcg==", "*", "*", "*"]
]
}]
}AAAADwAAAAh0cmFuc2Zlcg== encodes the XDR symbol transfer. Custom contracts can emit different event formats. Add contractIds to limit the stream to the token contracts you want. The stream does not normalize token transfers or convert their amounts for you.
Example result:
{
"type": "contract",
"ledger": 5193684,
"ledgerClosedAt": "2026-10-14T15:33:27Z",
"contractId": "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC",
"id": "0022306702925758464-0000000000",
"operationIndex": 0,
"transactionIndex": 0,
"txHash": "57d3b82f30a8d85c9937a163264e278db814fbb9f5b4bbe7464f82d883a5f783",
"inSuccessfulContractCall": false,
"topic": ["AAAADwAAAANmZWUA", "AAAAEgAAAAAAAAAAA+0oimKONzC713z+Cl8orRHR8/s0JY+JMothA0fY7bY="],
"value": "AAAACgAAAAAAAAAAAAAAAAAAAGQ="
}This example is a fee event, not a transfer. The result field is singular topic; the filter field is plural topics. topic entries and value remain base64 XDR. ledgerClosedAt is an ISO-8601 timestamp. contractId can be absent or empty for events without an associated contract; tolerate additional fields and event types.
A contract event is not by itself proof that a transfer succeeded. Check inSuccessfulContractCall and, when your application requires transaction success, the transaction result. There is no subscription filter for inSuccessfulContractCall, transfer amount, or decoded event value; apply those checks in your client.
{"jsonrpc":"2.0","id":30,"method":"stellar_subscribe","params":["transactions",{"status":"SUCCESS","accounts":["GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST"]}]}status accepts SUCCESS or FAILED. Omit it for both. accounts accepts 1–100 G or M StrKeys and matches any listed account in the transaction source, fee-bump fee payer, operation sources, or direct operation counterparties: destination, trustor, source (from), sponsored account, and account-merge destination.
This is not an account balance or full state-change subscription. It does not match asset issuers, claimants, memo text, or arbitrary smart-contract arguments. There is no pending-transaction subscription.
| Field | JSON type | Meaning |
|---|---|---|
status | string | SUCCESS or FAILED |
txHash | string | Transaction hash as hexadecimal |
ledger | integer | Ledger containing the transaction |
createdAt | integer | Ledger close time as Unix seconds |
applicationOrder | integer | Transaction application order within the ledger |
feeBump | boolean | Whether the transaction uses a fee-bump envelope |
envelopeJson | object | Transaction envelope in Stellar XDR-JSON format |
resultJson | object | Transaction result in XDR-JSON format |
resultMetaJson | object | Transaction result metadata in XDR-JSON format |
events | object | Transaction event data, when provided |
Example selected fields (the three XDR-JSON objects and events are omitted here):
{
"status": "FAILED",
"txHash": "57d3b82f30a8d85c9937a163264e278db814fbb9f5b4bbe7464f82d883a5f783",
"applicationOrder": 1,
"feeBump": false,
"ledger": 5193684,
"createdAt": 1791992007
}The full envelope uses a tagged union: regular transactions are under envelopeJson.tx.tx, v0 transactions under envelopeJson.tx_v0.tx, and fee-bump inner transactions under envelopeJson.tx_fee_bump.tx.inner_tx.tx.tx. These objects use Stellar's XDR-JSON format (SEP-0051), including decimal strings for 64-bit integers. Keep those values as strings or use arbitrary-precision integers; do not convert them to JavaScript Number indiscriminately.
{"jsonrpc":"2.0","id":40,"method":"stellar_subscribe","params":["payments",{"to":["GAZEXNDQ2WYQOQRCCDJ5WXCTEB3XTRNBUVCIDC6UJMPHOFV3K2IGENKR"]}]}accounts matches a payment's sender or recipient. from matches its sender; to matches its recipient. Each filter accepts an array of 1–100 G or M StrKeys. Combine filters to require all of their conditions.
A result contains ledger (integer), txHash (string), createdAt (Unix seconds as an integer), operationIndex (zero-based integer), type, from, to, and an operation object. The notification example above shows a complete classic payment result. If an operation has its own source account, from is that account.
type | operation fields | Amount semantics |
|---|---|---|
payment | destination, asset, amount | Amount transferred |
path_payment_strict_receive | send_asset, send_max, destination, dest_asset, dest_amount, path | Exact destination amount; send_max is an upper bound, not the actual amount spent |
path_payment_strict_send | send_asset, send_amount, destination, dest_asset, dest_min, path | Exact source amount; dest_min is a lower bound, not the actual amount received |
Amounts are decimal strings in Stellar integer units, not human-readable token balances. For classic Stellar assets, divide by 10^7 to obtain standard units: "1" is 0.0000001 XLM when the asset is "native". Assets retain their XDR-JSON representation: native XLM is "native"; issued assets use credit_alphanum4 or credit_alphanum12 objects containing asset_code and issuer.
Only successful transactions produce payment notifications. A transaction with multiple payment operations produces multiple records. Account creation, account merge, and Soroban contract transfers do not produce payments records. Use events for Soroban transfers. Use transaction results if you need the actual executed amounts of path payments.
Filter fields are ANDed together; addresses within each array are ORed. Addresses are case-sensitive and must be valid StrKeys, including checksums. M addresses match exactly; they do not automatically match the underlying G address. Account filters do not accept C contract addresses.
Unknown topics or filter fields, empty address arrays, malformed addresses, invalid enum values, and unsupported filter shapes are rejected. For example:
{"jsonrpc":"2.0","id":50,"method":"stellar_subscribe","params":["payments",{"unknown":true}]}returns a JSON-RPC error with code -32602 (Invalid params). Inspect the response's error object instead of treating its result as a subscription ID. Authentication and access errors can also prevent the WebSocket connection from opening.
Send the ID returned by stellar_subscribe over the same connection:
{"jsonrpc":"2.0","id":60,"method":"stellar_unsubscribe","params":["0x0123456789abcdef0123456789abcdef"]}{"jsonrpc":"2.0","id":60,"result":true}The result is true when the subscription was removed and false if the ID was not found on that connection. Already-in-flight notifications may arrive while you unsubscribe. Other subscriptions on the connection remain active. Closing the connection ends all of its subscriptions; IDs cannot be reused after reconnecting.
Subscriptions deliver new activity after initialization. The acknowledgement confirms that the subscription was accepted; it does not mean that a record is immediately available or guarantee delivery from the instant you sent the request. There is no startLedger, historical replay, resume token, or acknowledgement-based delivery option.
Delivery is best effort. Disconnections and interruptions can cause gaps; there is no durable exactly-once or at-least-once guarantee. There is no fixed notification latency guarantee. A quiet subscription can mean that no new records match its filters.
If your application needs complete history:
- Persist the last processed ledger and record IDs for each network.
- Reconnect with backoff and jitter, then recreate your subscriptions with new IDs.
- Buffer new live notifications while you retrieve an overlapping historical range through the corresponding HTTP RPC methods:
getLedgers,getEvents, orgetTransactions. Recover classic payments from successful transaction operations. - Merge historical and live records and deduplicate by network plus ledger hash/sequence, event
id, transactiontxHash, or payment(txHash, operationIndex).
Historical RPC availability is bounded by retained history. Recover promptly; a gap older than retained history may need another archive source. Do not infer that an empty filtered stream is fully caught up or that the last notification's ledger marks the end of all records for that ledger.
Close code 1011 can indicate a service interruption or an unrecoverable delivery gap. Reconnect and recover missing records. Close code 1008 can indicate a policy or slow-consumer limit; address the cause before reconnecting. Process notifications promptly, bound your client queues, and reduce subscription scope if your client cannot keep up. Your plan's connection, subscription, and usage limits still apply.
Use the OpenRPC reference for subscribe/unsubscribe method contracts and the message JSON Schema for positional parameters and notification shapes. Result records may include additional fields; clients should ignore unknown fields for forward compatibility.
Message parameters
Select the result record type.
Optional topic-specific filter. Omit or use {} for all records. Only fields for the selected topic are accepted.
Result
Opaque ID scoped to this WebSocket connection. Later records arrive as stellar_subscription notifications.
Ledgers
Send on the open WebSocket:
{
"jsonrpc": "2.0",
"id": 1,
"method": "stellar_subscribe",
"params": [
"newLedgers"
]
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x0123456789abcdef0123456789abcdef"
}All events
Send on the open WebSocket:
{
"jsonrpc": "2.0",
"id": 1,
"method": "stellar_subscribe",
"params": [
"events"
]
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x0123456789abcdef0123456789abcdef"
}Soroban transfers
Send on the open WebSocket:
{
"jsonrpc": "2.0",
"id": 1,
"method": "stellar_subscribe",
"params": [
"events",
{
"type": "contract",
"topics": [
[
"AAAADwAAAAh0cmFuc2Zlcg==",
"*",
"*"
],
[
"AAAADwAAAAh0cmFuc2Zlcg==",
"*",
"*",
"*"
]
]
}
]
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x0123456789abcdef0123456789abcdef"
}Successful transactions
Send on the open WebSocket:
{
"jsonrpc": "2.0",
"id": 1,
"method": "stellar_subscribe",
"params": [
"transactions",
{
"status": "SUCCESS",
"accounts": [
"GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST"
]
}
]
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x0123456789abcdef0123456789abcdef"
}Classic payments
Send on the open WebSocket:
{
"jsonrpc": "2.0",
"id": 1,
"method": "stellar_subscribe",
"params": [
"payments",
{
"accounts": [
"GBM2QDS4TARZZJ7LLFP74UMMALNRD7GHO4UXTM3KERGANOWFHBVUV3ST"
]
}
]
}Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x0123456789abcdef0123456789abcdef"
}JSON-RPC errors
-32602: Invalid params