Query and Stream
This guide reads data from Horizon: query an account’s payment history, page through large result sets, and stream new records as they happen. It is all read-only, so no keypairs or signing are involved.
Prerequisites
- An account ID (a
G...public key) with some history to look at. Any existing account works; to make one, see Connect and Fund an Account. - The examples use testnet.
Run a query
Horizon data is reached through call builders. You start one from the server
(for example server.payments()),
narrow it with filters, then call .call() to run the request. The result is a
page of records:
import { Horizon } from "@stellar/stellar-sdk";
const horizon = new Horizon.Server("https://horizon-testnet.stellar.org");
const page = await horizon.payments().forAccount(accountId).call();
for (const payment of page.records) { console.log(payment.type, payment.id);}The same pattern works for other endpoints on
Horizon.Server:
horizon.operations(), horizon.effects(), horizon.transactions(),
horizon.trades(), and more. Most expose .forAccount(id) and similar filters.
Page through results
Horizon returns results in pages. Control them with .limit() (page size, up to
200), .order() ("asc" or "desc"), and .cursor() (start after a given
paging token). Each page carries .next() and .prev() to fetch the adjacent
page using the embedded cursor:
let page = await horizon .payments() .forAccount(accountId) .order("desc") .limit(20) .call();
while (page.records.length > 0) { for (const payment of page.records) { console.log(payment.id); } // next() returns an empty page once history is exhausted, ending the loop. page = await page.next();}Each record’s paging_token is the cursor you would pass to .cursor() to
resume later.
Stream live updates
Instead of polling, open a
stream.
.stream() keeps a long-lived Server-Sent Events (SSE) connection open (the
server pushes records over it) and calls onmessage for each record. Existing
records are replayed first; pass cursor("now") to receive only new ones.
.stream() returns a function that closes the connection:
const close = horizon .payments() .forAccount(accountId) .cursor("now") .stream({ onmessage: (payment) => console.log("new payment:", payment.type), onerror: (e) => console.error("stream error:", e), });
// Later, stop listening:close();Put it together
A runnable script: fund a throwaway account (which gives it one payment record),
list its payments, then stream new ones for a while. Because the stream uses
cursor("now"), it prints only when a new payment arrives, so you may see no
stream output. To watch onmessage fire, send a payment to the account from
another terminal (see Send a Payment). Either way
the script exits after 30 seconds.
import { Keypair, Horizon } from "@stellar/stellar-sdk";
const horizon = new Horizon.Server("https://horizon-testnet.stellar.org");
async function main() { const account = Keypair.random(); await horizon.friendbot(account.publicKey()).call(); const accountId = account.publicKey();
// Read recent payments. const page = await horizon .payments() .forAccount(accountId) .order("desc") .limit(10) .call(); for (const payment of page.records) { console.log(payment.type, payment.id); }
// Watch for new payments; stop after 30 seconds. const close = horizon.payments().forAccount(accountId).cursor("now").stream({ onmessage: (payment) => console.log("new payment:", payment.type), onerror: (e) => console.error("stream error:", e), }); setTimeout(close, 30000);}
main().catch(console.error);You can now read history and react to it live. The same call builders back most of what an app needs to display from the network. Reads can still fail (a missing account, a malformed request); Handle Errors covers catching those.