> Feedback: If these docs are stale, missing, or confusing, post sanitized feedback to `https://tempo.xyz/developers/api/feedback` with `source: "mcp"`, a short `message`, and any relevant `toolName`, `relatedResource`, or `client`.
> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://tempo.xyz/developers/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Tempo API Typed Client

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 the [`tapimo`](https://www.npmjs.com/package/tapimo) package with your package manager.

:::code-group
```bash [npm]
npm install tapimo
```

```bash [pnpm]
pnpm add tapimo
```

```bash [bun]
bun add tapimo
```
:::

## 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({
  // API key sugar for the canonical `tempo-api-key` header.
  apiKey: 'tempo_api_key',
  // Override the API URL. Defaults to `Client.defaultUrl`.
  url: 'https://api.tempo.xyz',
  // Or pass custom headers directly.
  headers: { authorization: 'Bearer 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: '0x20c0000000000000000000000000000000000000' },
})

// 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 = await response.text()
    //    ^?
  } else {
    // Handle validation, auth, rate-limit, or upstream errors
    const json = await response.json()
    //    ^?
  }

  throw new Error('Request failed')
}

// Request is successfull
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 because the chain selector is optional:

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

const client = Client.create()

const response = await client.rpc[':chain{mainnet|testnet|[0-9]+}?'].$post({
  // Use `undefined` for `/rpc`, or pass `mainnet`, `testnet`, or a numeric chain id.
  param: { chain: 'testnet' },
  json: {
    jsonrpc: '2.0',
    id: 1,
    method: 'eth_blockNumber',
    params: [],
  },
})

if (response.status !== 200) {
  const error = await response.json()
  throw new Error(error.error.message)
}

const body = await response.json()
if (!Array.isArray(body) && body.error)
  throw new Error(body.error.message)

const blockNumber = Array.isArray(body) ? body[0]?.result : body.result
```

Chain selectors map to these hosted endpoints:

| `chain` value | Hosted RPC path |
| --- | --- |
| `undefined` | `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: undefined },
  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.
}
```
