Providers

Horizon

Stellar through the SDF's public Horizon. Balances and trustlines. Payments as history. Ledgers as blocks and fee stats as gas. Stroops. No key.
provider
create("horizon")
auth
none
chains
Stellar
capabilities
balance, history, tx detail, tokens, transfers, gas, block
endpoint
horizon.stellar.org
default chain
stellar

Address it

import { Horizon } from "@agntn/explorers/providers/horizon";

const horizon = new Horizon(); // or create("horizon"), or { baseUrl: "https://my.horizon" }
await horizon.getBalance("GAHK7EEG2WWHVKDNT4CEQFZGKF2LGDSW2IVM4S5DP42RBW3K6BTODB4A", "stellar");
await horizon.getGasData("stellar");

One chain, no key. baseUrl is the Horizon root and defaults to https://horizon.stellar.org, the instance the Stellar Development Foundation runs, which keeps one year of history and nothing older. Point it at any other Horizon if you need the rest. Any other chain is an UnsupportedChainError, and an account id that doesn't pass the Stellar checksum fails before a request goes out. That includes muxed M... addresses, which aren't accounts.

What it reads

OperationRoute
balance/accounts/{id}, the native entry
token holdings/accounts/{id}, every credit_alphanum4 and credit_alphanum12 trustline
history/accounts/{id}/payments?join=transactions, one row per operation
token transfersthe same payments, keeping the rows that moved an issued asset
one transaction/transactions/{hash} plus /transactions/{hash}/operations
gas/fee_stats, in stroops per operation
block/ledgers/{sequence}

No contracts and no unspent outputs. Soroban state lives behind RPC, not Horizon, and Stellar has accounts, not outputs.

Gotchas

  • Stroops, seven decimals, for XLM and for every issued asset alike. Horizon prints amounts as decimal strings like 12.8193804, and the provider turns them into stroops without a float in sight.
  • An issued asset is CODE:ISSUER, the SEP-11 spelling, in contract on holdings and transfers and as the token filter. Liquidity pool shares sit in the same balances array and stay out, because a pool share isn't a token anyone sends.
  • History is Horizon's payments view, not its transactions view, so every row has a sender, a recipient and an amount. That covers payments, path payments, account creations, merges and contract calls that moved an asset. A transaction with a hundred payments in it is a hundred rows sharing one hash, and only a single-operation transaction carries its fee on the row; the fee belongs to the transaction, and getTxDetail always has it. Failed operations come along with status: "failed" and no token transfers. raw keeps the operation record with its joined transaction, memo included.
  • An account merge is a row with value: "0", honestly. Horizon doesn't put the merged amount on the operation.
  • limit up to 200 a page, page up to 10. Horizon pages with cursors, so page 3 costs three requests, and startBlock or endBlock throw because there's no ledger filter on an account's payments. Token transfers have no asset filter on Horizon's side either, so getTokenTransfers scans up to five pages of 200 payments and keeps what moved an issued asset. On a busy account that's the last thousand payments, not all of them.
  • getTxDetail picks the first payment-like operation as the row and lists every issued-asset movement under tokenTransfers. Soroban operations set isContractInteraction. A transaction that only manages offers or trustlines has to: null and value: "0".
  • Gas is /fee_stats, and only two of its numbers survive the trip. baseFee is the ledger's base fee. safeGasPrice is the fee most operations were charged over the last five ledgers, 100 stroops until surge pricing kicks in, then whatever the marginal bid was. fastGasPrice is the 95th percentile, and that one is Soroban's: contract calls pay resource fees on top, so it reads as a cap that clears them, not as what a payment costs. There's no proposedGasPrice, because every percentile in between averages a payment with a contract call and means nothing.
  • A ledger is a block with txCount counting successful and failed transactions, baseFee in stroops, an empty miner and gas fields of "0", like every other chain without gas.
  • getBalance returns the whole balance, reserve and liabilities included, and the snapshot fields stay null because the account record doesn't say which ledger answered. An account the ledger never funded is a 404 and a NotFoundError, not a zero.
  • The Explorer has no live feed for Stellar. Search works, the hub says there's no tip.

Where it lives

src/providers/horizon.ts.

@agntn/explorers·MIT license· Read-only. Addresses you type go to a public explorer API, never to a wallet.