> For the complete documentation index, see [llms.txt](https://emeditweb.gitbook.io/pulsar-stellar-sdk/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://emeditweb.gitbook.io/pulsar-stellar-sdk/getting-started.md).

# Getting Started

This page installs the SDK and reads events two ways: live from Stellar RPC, which works today with no infrastructure, and from an indexer, which needs a running indexer serving its event routes.

## Install

```bash
npm install @pulsar-stellar/sdk @stellar/stellar-sdk
```

`@stellar/stellar-sdk` is a **required peer dependency**, version `>=16.2.0 <17.0.0`. Install it whichever methods you use. The SDK imports it at module load to decode XDR, not lazily on the RPC path only, so a missing peer is a load-time failure rather than a surprise deep in a call.

## Prerequisites

* **Node 22.13** or newer.
* **TypeScript 5.6** or newer, if you compile against the bundled type declarations.
* ESM and CommonJS are both supported.

## Create a client

```ts
import { PulsarClient } from '@pulsar-stellar/sdk';

const client = new PulsarClient({
  indexerUrl: 'https://indexer.example.com', // <!-- VERIFY --> placeholder, see note
  rpcUrl: 'https://soroban-testnet.stellar.org',
  network: 'testnet',
});
```

The constructor validates the whole configuration and throws `PulsarValidationError` if anything is wrong, so a misconfiguration surfaces where you made it rather than at the first request.

`indexerUrl` is required by the constructor even if you only use the RPC path, which never calls it. There is no hosted public indexer yet, so pass your own indexer's URL, or a placeholder like the one above until you run one. `rpcUrl` is optional and only the RPC path uses it.

## Read live events from RPC (works today)

The RPC path reads the live network directly. It needs only a public Soroban RPC URL and reaches back about a week, the node's retention window.

```ts
import { fetchLiveEvents } from '@pulsar-stellar/sdk';

// Pick a recent ledger sequence. RPC keeps about a week of history, so a value
// older than that is rejected by RPC, not by the SDK.
const page = await fetchLiveEvents(client.config, {
  startLedger: 1_000_000,
  filter: { contractIds: ['CDNWTVUDKCCGW7GOC6SBLUFXXUCD2YDHWRDUSXZ6CYBQKQWLCUYYWI5L'] },
});

for (const event of page.events) {
  console.log(event.ledger, event.name, event.data);
}

console.log('resume from', page.cursor); // never null on the RPC path
```

To follow events continuously, use `liveEventStream`. It fetches pages as needed and never ends on its own, because the live network has no end. Break out of the loop to stop.

```ts
import { liveEventStream } from '@pulsar-stellar/sdk';

for await (const event of liveEventStream(client.config, { startLedger: 1_000_000 })) {
  console.log('new event:', event.name, event.eventIndex);
  break; // the stream polls forever, so decide when you are done
}
```

## Read history from the indexer

The indexer path reads stored, decoded history and pages backward through all of it, past the RPC retention wall. The method and its return shape are shipped and tested, and the indexer now serves the events route, so this call works against a running indexer. There is no hosted public instance yet, so self-host one and point `indexerUrl` at it; see the [Indexer Guide](/pulsar-stellar-sdk/indexer-guide.md).

```ts
const page = await client.events('CDNWTVUDKCCGW7GOC6SBLUFXXUCD2YDHWRDUSXZ6CYBQKQWLCUYYWI5L');

for (const event of page.items) {
  console.log(event.ledger, event.name, event.data);
}
```

Each item is a `DecodedEvent`. Its fields, exactly as the SDK's tests assert them:

```ts
{
  id: '9007199254740993',        // string, so ids past 2^53 stay exact
  contractId: 'CDNWTVUD...WI5L',
  ledger: 1234567,
  txHash: '...',
  eventIndex: 0,                 // ordinal within the ledger, not the transaction
  name: 'deposit',               // the leading topic Symbol, lowercase, as emitted
  topics: [/* DecodedValue[] */],
  data: {/* DecodedValue */},
  rawTopics: ['...'],            // base64 XDR, provenance for the decoding
  rawData: '...',
  emittedAt: '2026-01-01T00:00:00Z',
  inSuccessfulContractCall: true, // false is possible: reverted calls still emit
}
```

Page with the opaque cursor. Pass `nextCursor` back as `query.cursor`, and stop when it is null.

```ts
let cursor: string | undefined;
do {
  const p = await client.events(contractId, cursor === undefined ? undefined : { cursor });
  for (const event of p.items) handle(event);
  cursor = p.nextCursor ?? undefined;
} while (cursor !== undefined);
```

Or let the SDK walk the pages for you:

```ts
for await (const event of client.eventStream(contractId, { name: 'transfer' })) {
  console.log(event.name, event.topics);
}
```

## Handle failures

Everything the SDK throws descends from `PulsarError`, so you branch on the kind rather than match message text.

```ts
import { PulsarNetworkError, PulsarValidationError } from '@pulsar-stellar/sdk';

try {
  await client.events(contractId);
} catch (error) {
  if (error instanceof PulsarNetworkError) {
    console.error('unreachable or refused', error.status, error.url);
  } else if (error instanceof PulsarValidationError) {
    console.error('bad input or unexpected response', error.issues);
  }
}
```

## Next steps

* [Concepts](/pulsar-stellar-sdk/concepts.md): the two paths, wire-faithful decoding, and why an unknown value does not throw. Worth reading once.
* [SDK Reference](/pulsar-stellar-sdk/sdk-reference.md): every export, with signatures.
* [Track transfer events](https://github.com/pulsar-stellar/pulsar-docs/tree/main/docs/guides/track-transfer-events.md): a full worked example with a running balance.
* [Indexer Guide](/pulsar-stellar-sdk/indexer-guide.md): run your own indexer.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://emeditweb.gitbook.io/pulsar-stellar-sdk/getting-started.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
