# TypeScript client

A generated, strictly typed client for the public read APIs, with typed errors, pagination helpers, and no embedded secrets.

Page ID: reference/client-sdk
Applicability: universe-implementation
Authority: universe-implementation
Networks: mainnet
Verified: 2026-08-31
Locale: en
URL: https://bitcoinuniverseio.github.io/atomicals-and-arc-20/reference/client-sdk/

---
The client is generated from the OpenAPI documents and lives in `packages/client`. It is
regenerated by `npm run generate`, and CI fails if the committed output differs.

It does not sign, broadcast, request key material, or embed a credential. Those belong in a wallet
and in your own server, not in a generated read client.

## Install and configure

The base URL is required. There is no default, because a default would be a production host baked
into a package.

  

```ts

const client = new AtomicalsClient({
  baseUrl: process.env.ATOMICALS_API_URL!,
  // Optional. Only for surfaces that require it. Never ship this in a browser.
  bearerToken: process.env.ATOMICALS_API_TOKEN,
})

const asset = await client.getAsset({
  atomicalId: '4d0b1f1c9c53a5b1e0b1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e4f5061i0',
})

console.log(asset.data.atomicalId, asset.generationId, asset.requestId)
```

  
  

```js

const client = new AtomicalsClient({ baseUrl: process.env.ATOMICALS_API_URL })

const ready = await client.getReady()
if (!ready.data.ready) {
  console.error('not ready:', ready.data.reason)
  process.exit(1)
}
```

  

## Typed errors

Failures throw a typed error carrying the status, the machine readable code, and the request
identifier. Branch on the code, never on the message.

```ts

try {
  await client.getNft({ atomicalId })
} catch (error) {
  if (error instanceof AtomicalsApiError) {
    if (error.code === 'NFT_NOT_FOUND') {
      // Not in the active generation. Check readiness before concluding it is gone.
    }
    console.error(error.status, error.code, error.requestId)
  }
  throw error
}
```

## Pagination

The helper restarts the listing when the generation changes, rather than continuing with a cursor
that is no longer valid.

```ts
for await (const page of client.paginate.listNfts({ limit: 100 })) {
  for (const asset of page.items) {
    // page.generationId is stable across this loop.
  }
}
```

## Freshness

Every response exposes the generation identifier, the indexed height, and the request identifier
alongside the parsed body. Store them with anything you cache.

```ts
const page = await client.listRealms({ limit: 50 })
console.log(page.generationId, page.indexedHeight, page.requestId)
```

## What is included

| Included | Not included |
| --- | --- |
| Public read operations for both indexes | Anything that signs or broadcasts |
| Marketplace read operations and contract types | Marketplace mutation request signing |
| Typed errors and pagination helpers | Credential storage or management |
| Freshness metadata on every response | A default base URL |

Marketplace mutations require an HMAC over exact body bytes and an owner session. Those belong in
your server, where the secret lives. The client exposes the types so you can build the request, and
deliberately does not sign it for you.

## Source

[OpenAPI and downloads](/reference/openapi/) and [migration](/develop/migration/).
