<!-- tempo-docs-context -->
> Source: [/docs/sdk/typescript/api-client](https://tempo.xyz/developers/docs/sdk/typescript/api-client.md)
> Section: [Get Started](https://tempo.xyz/developers/get-started.md). Choose a starting point, send a test payment, get testnet funds, connect a coding agent, and find the SDKs and CLI.
> [Documentation index](https://tempo.xyz/developers/llms.txt) · [Docs skill](https://tempo.xyz/developers/SKILL.md)

> Tempo MCP: Use `search`, `find_pages`, `read_page`, and `code` at `https://mcp.tempo.xyz` for Tempo and related documentation.
> If an indexed page is missing or reflects an older site, read this site’s [documentation index](https://tempo.xyz/developers/llms.txt) and Markdown pages directly.

# Tempo API client for TypeScript

Use the Tempo API Typed Client for endpoint autocomplete and full end-to-end type-safety across API parameters, response bodies, statuses, and errors.

## Install Tempo API package

Install [`tapimo`](https://www.npmjs.com/package/tapimo) with pnpm. Release `0.18.2` needs the following dependency override because `accounts@0.18.5` was published with unresolved workspace dependencies.

Merge this entry into `pnpm-workspace.yaml` before installing:

```yaml [pnpm-workspace.yaml]
overrides:
  'tapimo>accounts': 0.18.4
```

```bash
pnpm add --save-exact tapimo@0.18.2
```

## Create typed Tempo API client

Create a Tempo API client with `Client.create`.

```ts twoslash [client.ts]
import { Client } from 'tapimo'

const client = Client.create({
  apiKey: process.env.TEMPO_API_KEY,
})
```

`Client.create()` also accepts Hono client options such as a custom `fetch` implementation. If you pass `apiKey`, the client merges it into the request headers as `tempo-api-key`.

## Make type-safe Tempo API requests

Requests are end-to-end type-safe across parameters, response bodies, statuses, and errors. For example, `GET /v1/tokens/:token` narrows the response body from the HTTP status:

```ts twoslash [tokens.ts]
import { Client } from 'tapimo'

const client = Client.create()

const response = await client.v1.tokens[':token'].$get({
  param: { token: '0x20c0000000000000000000006a37da5c996874be' },
})

// Narrow responses by status.
if (response.status !== 200) {
  response.status // status is now typed to non-200 series
  //       ^?
  if (response.status === 404) {
    // Handle 404 status specifically
    const json = await response.json()
    //    ^?
    json.error.code // which narrows to "token_not_found"
    //         ^?
  } else if (response.status === 402) {
    // Handle the payment challenge from `WWW-Authenticate`
    const challenge = response.headers.get('WWW-Authenticate')
    //    ^?
  } else {
    // Handle validation, auth, rate-limit, or upstream errors
    const json = await response.json()
    //    ^?
  }

  throw new Error('Request failed')
}

// Request succeeded
response.status // Response is narrowed to 200
//       ^?
// Get typed response body
const token = await response.json()
//    ^?
token.symbol
//    ^?
```

## Tempo API endpoint autocomplete

Routes autocomplete directly on the client, so you can discover endpoints without leaving your editor:

```ts twoslash [endpoints.ts]
// @noErrors
import { Client } from 'tapimo'

const client = Client.create()

const response1 = await client.v1.
//                                ^|
const response2 = await client.v1.transactions.
//                                             ^|
```

## Call Tempo JSON-RPC through Typed Client

The Typed Client exposes the raw JSON-RPC passthrough as a typed route under `client.rpc`. The route key includes Hono's path pattern. Select `mainnet`, `testnet`, or a numeric chain ID:

```ts twoslash [rpc.ts]
// @noErrors
import { Client } from 'tapimo'

const client = Client.create()

const response = await client.rpc[':chain{(mainnet|testnet|[0-9]+)}'].$post({
  param: { chain: 'testnet' },
  json: {
    jsonrpc: '2.0',
    id: 1,
    method: 'eth_blockNumber',
    params: [],
  },
})

if (response.status !== 200) {
  throw new Error(`RPC request failed: HTTP ${response.status}`)
}

const body = await response.json()
if (body === null || Array.isArray(body))
  throw new Error('Expected one JSON-RPC response')
if ('error' in body) throw new Error(body.error.message)

const blockNumber = body.result
```

Chain selectors map to these hosted endpoints:

| `chain` value | Hosted RPC path |
| --- | --- |
| No selector (`client.rpc.$post`) | `https://api.tempo.xyz/rpc` |
| `'mainnet'` | `https://api.tempo.xyz/rpc/mainnet` |
| `'testnet'` | `https://api.tempo.xyz/rpc/testnet` |
| `'4217'` | `https://api.tempo.xyz/rpc/4217` |

### Send batch JSON-RPC requests

The JSON-RPC body type accepts one request or an array of requests. The client narrows the success body to the same single-or-batch response union:

```ts twoslash [batch-rpc.ts]
// @noErrors
import { Client } from 'tapimo'

const client = Client.create()

const response = await client.rpc[':chain{(mainnet|testnet|[0-9]+)}'].$post({
  param: { chain: 'mainnet' },
  json: [
    { jsonrpc: '2.0', id: 1, method: 'eth_chainId', params: [] },
    { jsonrpc: '2.0', id: 2, method: 'eth_blockNumber', params: [] },
  ],
})

if (response.status === 200) {
  const results = await response.json()
  // results is typed as one JSON-RPC response or an array of JSON-RPC responses.
}
```
