inndx/
GitHub

TypeScript SDK

Installation, configuration, and full reference for the TypeScript SDK.

The TypeScript SDK (@inndx-io/sdk) is ESM-only and targets Node 20 or newer. It handles MPP payment automatically for both per-request and session endpoints.

Installation

The package is not published to the npm registry. Install it directly from git:

pnpm add github:inndx-io/typescript-sdk

Pin a specific tag or commit for reproducible installs:

pnpm add github:inndx-io/typescript-sdk#v0.3.2
pnpm add github:inndx-io/typescript-sdk#<commit-sha>

Or declare it in package.json:

{
  "dependencies": {
    "@inndx-io/sdk": "github:inndx-io/typescript-sdk#main"
  }
}

pnpm is recommended as it runs the package's prepare build step on install, giving you the compiled dist/ automatically.

Configuration

import { InndxClient } from '@inndx-io/sdk'

const client = new InndxClient({
  baseUrl: 'https://api.inndx.io',
  walletKey: process.env.WALLET_PRIVATE_KEY as `0x${string}`,
})

new InndxClient(config) accepts:

OptionRequiredDescription
baseUrlyesBase URL of the inndx API.
walletKeyone signer requiredWallet private key in 0x... hex form. Server-side use only.
accountone signer requiredA prebuilt viem account for custom or WebCrypto signers.
getConnectorClientone signer requiredwagmi-style connector accessor for browser signing.
maxDepositfor sessionsDefault escrow cap per session in human units (e.g. "10"). Overridable per session.
chainIdfor reclaimChain id needed by reclaimSession when working without a server.
headersnoHeaders added to every request.
fetchnoCustom fetch implementation.
rpcUrlnoRPC endpoint applied to every chain.
rpcUrlsnoPer chain id RPC endpoints.
feePayerUrlnoFee-payer relay URL so a third party covers gas.
escrowContractnoAdvanced. Escrow contract override.
getClientnoAdvanced. Full control over the viem client per chain id.
clientnoAdvanced. A prebuilt viem client used for every chain id.

Supply exactly one signer: walletKey, account, or getConnectorClient.

Browser and wagmi

In the browser, sign through a connected wallet using wagmi rather than holding a private key:

import { InndxClient } from '@inndx-io/sdk'
import { getConnectorClient } from 'wagmi/actions'

const client = new InndxClient({
  baseUrl: 'https://api.inndx.io',
  getConnectorClient: (parameters) => getConnectorClient(wagmiConfig, parameters),
  maxDeposit: '10',
})

Charges, sessions, and reclaim will prompt the wallet to sign rather than signing locally.

Scrape

client.scrape exposes the scraping operations. All Scrape API endpoints are session-based.

scrapeUrlMarkdown

Fetches a URL and returns the page as a markdown string.

const session = client.scrape.scrapeUrlMarkdown({ maxDeposit: '5' })

const markdown = await session.call('https://example.com')

await session.close()

Pass scrape options as a second argument:

const markdown = await session.call('https://example.com', {
  proxy: 'isp',
  timeout_seconds: 30,
  locale: 'en-US',
})

scrapeUrl

Full control over output formats and options. Returns { url, results }.

const session = client.scrape.scrapeUrl({ maxDeposit: '5' })

const result = await session.call({
  url: 'https://example.com',
  formats: [{ kind: 'markdown' }],
  proxy: 'isp',
  timeout_seconds: 30,
  locale: 'en-US',
})

for (const item of result.results) {
  if (item.kind === 'markdown') console.log(item.content)
}

await session.close()

Sessions

Opening and closing

const session = client.scrape.scrapeUrlMarkdown({ maxDeposit: '5' })

const page1 = await session.call('https://example.com')
const page2 = await session.call('https://example.com/about')

console.log('spent so far:', session.cumulative)

await session.close()

Scoped sessions

Use scope to guarantee the session closes even if your code throws:

const result = await client.scrape.scrapeUrlMarkdown({ maxDeposit: '5' })
  .scope(async (session) => {
    return await session.call('https://example.com')
  })

await using

TypeScript 5.2+ resource disposal syntax settles the session automatically when the block exits:

await using session = client.scrape.scrapeUrlMarkdown({ maxDeposit: '5' })

const page = await session.call('https://example.com')

Session options

OptionDescription
maxDepositEscrow cap for this session in human units (e.g. "10").
escrowContractEscrow contract override for this session.

SessionScope members

MemberDescription
call(...args)Makes a request through the session.
close()Settles the channel on-chain and returns the receipt, or undefined if nothing was opened.
scope(cb)Runs cb(session) then settles regardless of outcome.
channelIdThe channel id once opened, otherwise undefined.
cumulativeCumulative amount spent so far.
openedWhether the channel has been opened.
escrowContractThe escrow contract this session targets.

Reclaiming a stranded channel

If a process exits before close() is called, the escrowed deposit stays on-chain. Persist the channel id as soon as the channel opens, then use reclaimSession to recover the funds later.

// Persist the channel id after the first call opens the channel.
if (session.channelId) {
  await saveChannelId(session.channelId)
}

Forced close is a two-step sequence with a roughly 15-minute grace period between steps:

const reclaim = client.reclaimSession({ channelId })

// Step 1: start the close timer.
await reclaim.requestClose()

// Step 2: after the grace period, withdraw.
const state = await reclaim.getState()
if (state.ready) {
  await reclaim.withdraw()
}

The two steps do not need to run in the same process. Both requestClose and withdraw are idempotent.

Calling withdraw before the grace period elapses throws ChannelNotReadyError, which carries a readyAt timestamp.

Error handling

Non-2xx responses throw ApiError, which carries the status, parsed error body, and raw Response:

import { ApiError } from '@inndx-io/sdk'

try {
  const result = await session.call('https://example.com')
} catch (err) {
  if (err instanceof ApiError) {
    console.error(err.status, err.body)
  } else {
    throw err
  }
}

Search docs

Search the Cloud documentation