BentoSDK

Quickstart

Make your first Bento API call in a few minutes, then add a login to place a bet.

Install

npm install @bento.fun/sdk

Node.js 18+.

Get a Builder API key

createBentoSdk requires a Builder API key (apiKey). Catalog reads are open without it on the wire, but login / register / weblink and authenticated calls need x-builder-api-key so your app is attributed.

Generate a testnet key (hackathon / docs form), then:

export BENTO_BUILDER_API_KEY='bnt_…'   # from the form
export BENTO_URL='https://internal-server.bento.fun'

Your first call

Public reads (catalog, prices, analytics) need no user login. Pass apiKey anyway; you'll need it for login next:

import { createBentoSdk, walletAuthProvider } from '@bento.fun/sdk';

// createBentoSdk requires apiKey + auth; public reads ignore the user JWT slot
const sdk = createBentoSdk({
  baseUrl: 'https://internal-server.bento.fun',
  apiKey: process.env.BENTO_BUILDER_API_KEY!,
  auth: walletAuthProvider(() => ({})),
});

const { data } = await sdk.public.listDuels({ page: 1, limit: 10 });
console.log(`${data.length} live markets`);

// open one: use duelId (the on-chain id), NOT id (the database row id)
const market = await sdk.public.getDuelById({ duelId: data[0].duelId });

That's your first call. duelId vs id trips up everyone, so always pass duelId.

Log in (to act as a user)

Reads need only the Builder key; placing bets, creating markets, entering tournaments also need a user JWT. You sign a short message with your wallet and trade it for a JWT:

const ts = String(Date.now());
const signature = await account.signMessage({
  message: `Bento.fun Login\nTimestamp: ${ts}\nWallet: ${account.address}`,
});
// new wallet → eoaRegister({ ..., username }); existing wallet → eoaLogin
const { token } = (await sdk.public.auth.eoaLogin({
  address: account.address,
  signature,
  timestamp: ts,
})) as { token: string };

// authenticated client: markets auth is a Bearer JWT
const authed = createBentoSdk({
  baseUrl: 'https://internal-server.bento.fun',
  apiKey: process.env.BENTO_BUILDER_API_KEY!,
  auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })),
});

account is your wallet (e.g. a viem account). The login response includes your managed account address (where balances live). On testnet, fund it with the auto-mint faucet before betting; register alone does not credit USDC or credits. → Authentication · Money

Place a bet

Estimate first, then place. Amounts are wei (18 decimals); '1000000000000000000' is 1 unit:

const duelId = data[0].duelId; // from your first call above
const idempotencyKey = crypto.randomUUID(); // dedupes retries, optional; the SDK generates one if omitted

const est = await authed.user.bets.estimateBuy({
  duelId,
  optionIndex: 0, // 0 or 1, not a 'YES' / 'NO' label
  betAmountUsdc: '1000000000000000000',
  slippageBps: 100,
});
if (!est.success) throw new Error('estimate rejected');

await authed.user.placeBet(
  {
    duelId,
    duelType: 'prediction',
    bet: 'optionA',
    optionIndex: 0,
    betAmount: '1000000000000000000',
    betAmountUsdc: '1000000000000000000',
    sharesOut: est.estimate.shares_out,
    minSharesOut: est.estimate.min_shares_out,
    slippageBps: 100,
    quoteId: est.estimate.quote_id,
  },
  { idempotencyKey },
);

A write returns when the server accepted it, not when the chain settled. Poll a read to confirm. → Place a bet

Tournaments (second host)

Bracket tournaments and F1 live on a second host. Add tournamentsBaseUrl when you need them:

import { jwtAuthProvider } from '@bento.fun/sdk';

const sdk = createBentoSdk({
  baseUrl: 'https://internal-server.bento.fun',
  apiKey: process.env.BENTO_BUILDER_API_KEY!,
  tournamentsBaseUrl: process.env.PARLAY_TOURNAMENT_URL, // tournaments host URL
  auth: walletAuthProvider(() => ({ Authorization: `Bearer ${token}` })),
  tournamentsAuth: jwtAuthProvider({ getAccessToken: () => token }),
});

const { tournaments } = (await sdk.tournaments!.tournaments.list({
  limit: 5,
})) as { tournaments: unknown[] };

Two API hosts · Enter a tournament (buy-in, then chip picks on matches)

Next

On this page