Skip to content

Venum SDK

@venumdev/sdk is the typed TypeScript client for the Venum Solana execution API. It covers every public endpoint, ships zero runtime dependencies, and works in Node 22+ and modern browsers.

Why use the SDK

  • Typed everything — response interfaces for every endpoint, discriminated unions for every SSE event
  • Two stream modes — callback (streamPrices(...)) and async iterator (for await (const msg of venum.iteratePrices(...)))
  • AbortSignal everywhere — every method, REST and SSE
  • Automatic retry with jittered backoff and Retry-After. Opt out globally or per-request. Never retries non-idempotent calls (submitSwap, send) or auth failures (401, 403)
  • One-shot swap helpervenum.swap({...signer, waitFor: 'confirmed'}) collapses build → sign → submit → wait into a single call
  • Amount helperstoBaseUnits, fromBaseUnits, solToLamports, etc.
  • Env fallback in Node — reads VENUM_API_KEY and VENUM_API_URL if omitted
  • Zero runtime deps — platform fetch and TextDecoder

Install

bash
pnpm add @venumdev/sdk
# or
npm install @venumdev/sdk

Quick start

ts
import { VenumClient, solToLamports } from '@venumdev/sdk'

const venum = new VenumClient({ apiKey: process.env.VENUM_API_KEY })

const sol = await venum.price('SOL')
console.log(sol.priceUsd, sol.bestBid, sol.bestAsk)

const quote = await venum.quote({
  inputMint: 'SOL',
  outputMint: 'USDC',
  amount: solToLamports(1),
  slippageBps: 50,
})
console.log(quote.topRoutes[0])

In Node, apiKey and baseUrl fall back to VENUM_API_KEY and VENUM_API_URL from process.env when omitted. Explicit options always win. In the browser, only explicit options are used.

Cancellation

Every method accepts an AbortSignal. Aborting cancels the in-flight request, any pending retry, and (for streams) the reader plus future reconnects.

ts
const ctrl = new AbortController()
setTimeout(() => ctrl.abort(), 2000)
await venum.quote(req, { signal: ctrl.signal })

// One-shot timeout
await venum.quote(req, { signal: AbortSignal.timeout(2000) })

Retry

Enabled by default with sensible values:

RuleBehavior
401, 403Never retried. Surfaces immediately so you can prompt sign-in / upgrade.
404, 400Never retried (resource / caller error).
429Retried, honoring Retry-After. After exhausting retries, throws VenumApiError with status: 429.
502, 503, 504Retried with jittered exponential backoff.
Network errorsRetried.
submitSwap, sendNever retried (non-idempotent — a double-submit could land twice).

Per-request override: await venum.quote(req, { retry: false }) or { retry: { retries: 5 } }.

Disable globally: new VenumClient({ retry: false }).

Streams

Both callback and async iterator modes deliver the same typed messages.

Callback mode

ts
const ctrl = new AbortController()

await venum.streamPrices(['SOL', 'JITOSOL'], {
  signal: ctrl.signal,
  onError: (msg) => console.warn('reconnecting:', msg),
  onEvent: (msg) => {
    switch (msg.type) {
      case 'ready':     console.log('ready at', msg.ts); break
      case 'price':     console.log(msg.price.token, msg.price.priceUsd); break
      case 'heartbeat': /* periodic keep-alive */ break
    }
  },
})

Iterator mode

ts
const ctrl = new AbortController()

for await (const msg of venum.iteratePrices(['SOL'], { signal: ctrl.signal })) {
  if (msg.type === 'price') console.log(msg.price.priceUsd)
}

Swap flow

One-shot helper

ts
import { VenumClient, solToLamports } from '@venumdev/sdk'
import { Keypair, VersionedTransaction } from '@solana/web3.js'
import { Buffer } from 'node:buffer'

const venum = new VenumClient()
const wallet = Keypair.fromSecretKey(/* ... */)

const result = await venum.swap({
  inputMint: 'SOL',
  outputMint: 'USDC',
  amount: solToLamports(0.1),
  slippageBps: 50,
  signer: {
    publicKey: wallet.publicKey.toBase58(),
    sign: (unsignedBase64) => {
      const tx = VersionedTransaction.deserialize(Buffer.from(unsignedBase64, 'base64'))
      tx.sign([wallet])
      return Buffer.from(tx.serialize()).toString('base64')
    },
  },
  waitFor: 'confirmed',
})

console.log(result.signature, result.landed?.slot)

Manual flow (full control)

ts
const build = await venum.buildSwap({
  inputMint: 'SOL',
  outputMint: 'USDC',
  amount: solToLamports(0.1),
  userPublicKey: wallet.publicKey.toBase58(),
  slippageBps: 50,
})

const tx = VersionedTransaction.deserialize(Buffer.from(build.transaction, 'base64'))
tx.sign([wallet])

const submit = await venum.submitSwap({
  quoteId: build.quoteId,
  signedTransaction: Buffer.from(tx.serialize()).toString('base64'),
})

const landed = await venum.waitForTx(submit.signature, { events: ['confirmed'] })

Compatibility

The SDK has no Solana SDK dependency. It only exchanges base64-encoded VersionedTransaction bytes, which makes it compatible with:

  • @solana/web3.js (classic) — VersionedTransaction.deserialize / .serialize + .sign([kp])
  • @solana/kit (new) — getTransactionDecoder / signTransaction / getBase64EncodedWireTransaction
  • wallet-standard adapters — wrap the wallet's signTransaction into a SwapSigner

Errors

ts
import { VenumApiError, VenumNetworkError } from '@venumdev/sdk'

try {
  await venum.quote(req)
} catch (err) {
  if (err instanceof VenumApiError) {
    if (err.status === 401) {
      // prompt sign-in
    } else if (err.status === 403) {
      // upgrade plan
    } else if (err.status === 429) {
      // rate-limited after retries
    }
  } else if (err instanceof VenumNetworkError) {
    // err.url, err.cause
  } else if ((err as Error).name === 'AbortError') {
    // caller cancelled
  }
}

Amount helpers

ts
import { toBaseUnits, fromBaseUnits, fromBaseUnitsString, solToLamports, lamportsToSol } from '@venumdev/sdk'

toBaseUnits(1.5, 9)                     // "1500000000"
toBaseUnits('0.000001', 9)              // "1000"
fromBaseUnitsString('1500000000', 9)    // "1.5" (exact, no precision loss)
fromBaseUnits('1500000000', 9)          // 1.5 (may lose precision for huge balances)
solToLamports(2.5)                      // "2500000000"
lamportsToSol(1_500_000_000)            // 1.5

Amounts up to Number.MAX_SAFE_INTEGER (2^53) fit in JS number, but a wallet with 9M+ SOL at 9 decimals overflows. Strings are lossless. Use BigInt(raw) when you need to do math.

Browser usage

The SDK works in any modern browser — same fetch, same streams. Do not ship an API key to clients. Set up a backend-for-frontend that proxies requests to api.venum.dev, injects the key server-side, and rate-limits per origin or IP. Background and rationale: venum.dev/solana-free-rpc.

See also