Developer Guide
How to Query Stellar Accounts, Ledgers, and Transactions with JavaScript
A hands-on tutorial for JavaScript developers who want to query the Stellar blockchain. We will fetch account balances, read ledger data, page through transaction history, and handle errors — all with working code.
Setup
You need Node.js 18+ (for built-in fetch). Optionally install the Stellar SDK:
npm install @stellar/stellar-sdkQuerying Accounts
Fetch Account Balances
const HORIZON = 'https://horizon.stellar.org';
async function getBalances(accountId) {
const res = await fetch(`${HORIZON}/accounts/${accountId}`);
if (!res.ok) throw new Error(`Account not found: ${res.status}`);
const account = await res.json();
return account.balances.map(b => ({
asset: b.asset_type === 'native' ? 'XLM' : b.asset_code,
balance: parseFloat(b.balance).toLocaleString(),
issuer: b.asset_issuer || 'native',
}));
}
const balances = await getBalances('GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7');
console.table(balances);Querying Ledgers
Get Latest Ledger
async function getLatestLedger() {
const res = await fetch(`${HORIZON}/ledgers?limit=1&order=desc`);
const data = await res.json();
const ledger = data._embedded.records[0];
return {
sequence: ledger.sequence,
closedAt: ledger.closed_at,
txCount: ledger.successful_transaction_count,
operationCount: ledger.operation_count,
baseFee: ledger.base_fee_in_stroops,
protocolVersion: ledger.protocol_version,
};
}Get Fee Statistics
async function getFeeStats() {
const res = await fetch(`${HORIZON}/fee_stats`);
const fees = await res.json();
return {
baseFee: fees.last_ledger_base_fee,
p50: fees.fee_charged.p50,
p95: fees.fee_charged.p95,
maxFee: fees.fee_charged.max,
};
}Querying Transactions
Recent Transactions
async function getRecentTransactions(limit = 10) {
const res = await fetch(`${HORIZON}/transactions?limit=${limit}&order=desc`);
const data = await res.json();
return data._embedded.records.map(tx => ({
hash: tx.hash.slice(0, 12) + '...',
ledger: tx.ledger,
sourceAccount: tx.source_account.slice(0, 8) + '...',
operationCount: tx.operation_count,
feeCharged: tx.fee_charged,
successful: tx.successful,
}));
}Transaction Operations
async function getTransactionOperations(txHash) {
const res = await fetch(`${HORIZON}/transactions/${txHash}/operations`);
const data = await res.json();
return data._embedded.records.map(op => ({
type: op.type,
sourceAccount: op.source_account.slice(0, 8) + '...',
...(op.type === 'payment' && {
to: op.to.slice(0, 8) + '...',
amount: op.amount,
asset: op.asset_type === 'native' ? 'XLM' : op.asset_code,
}),
}));
}Pagination
Horizon uses cursor-based pagination with HAL links:
async function getAllPayments(accountId, maxPages = 5) {
let url = `${HORIZON}/accounts/${accountId}/payments?limit=200&order=desc`;
const allPayments = [];
let page = 0;
while (url && page < maxPages) {
const res = await fetch(url);
const data = await res.json();
const records = data._embedded.records;
if (records.length === 0) break;
allPayments.push(...records);
page++;
url = data._links?.next?.href || null;
}
return allPayments;
}Error Handling
async function safeFetch(url) {
const res = await fetch(url);
if (res.status === 404) return { error: 'not_found' };
if (res.status === 429) {
const retryAfter = res.headers.get('Retry-After') || '5';
return { error: 'rate_limited', retryAfter: parseInt(retryAfter) };
}
if (!res.ok) return { error: 'api_error', status: res.status };
return { data: await res.json() };
}Retry with Backoff
async function fetchWithRetry(url, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const res = await fetch(url);
if (res.status === 429) {
await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
continue;
}
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
if (attempt === maxRetries - 1) throw err;
await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));
}
}
}Using the Stellar SDK
import { Horizon } from '@stellar/stellar-sdk';
const server = new Horizon.Server('https://horizon.stellar.org');
// Account
const account = await server.loadAccount('GABC...');
console.log('Balances:', account.balances);
// Transactions
const txs = await server.transactions()
.forAccount('GABC...')
.limit(10)
.order('desc')
.call();
// Streaming payments
server.payments()
.forAccount('GABC...')
.cursor('now')
.stream({ onmessage: (payment) => {
console.log('Payment:', payment.amount, payment.asset_type);
}});Complete Example: Account Dashboard
async function accountDashboard(accountId) {
const [account, payments] = await Promise.all([
fetch(`${HORIZON}/accounts/${accountId}`).then(r => r.json()),
fetch(`${HORIZON}/accounts/${accountId}/payments?limit=5&order=desc`).then(r => r.json()),
]);
console.log('=== Account Dashboard ===');
console.log('ID:', accountId.slice(0, 12) + '...');
account.balances.forEach(b => {
const asset = b.asset_type === 'native' ? 'XLM' : b.asset_code;
console.log(` ${asset}: ${parseFloat(b.balance).toLocaleString()}`);
});
console.log('Recent Payments:');
payments._embedded.records.forEach(p => {
const asset = p.asset_type === 'native' ? 'XLM' : p.asset_code;
console.log(` ${p.amount} ${asset} (${p.created_at})`);
});
}*Query the Stellar blockchain with reliable infrastructure. LumenQuery provides managed Horizon API with sub-100ms response times. Start free.*