OddstrAPI

Oddstr API

JSON over HTTP. Amounts are whole sats. Odds are decimal and must sit on the price ladder from /api/config. Stakes are the backer's stake on both sides.

Logging in

Get a challenge, sign it as a Nostr event (kind 27235) with a challenge tag, and post it. Send the token as Authorization: Bearer <token>. Tokens last 30 days.

GET  /api/challenge                → { challenge }
POST /api/login  { event }         → { token, pub }
POST /api/logout

Markets

GET/api/config

Chain, ladder, minimum stake, bet delay, commission, withdrawal fee.

GET/api/markets

Every fixture with its Match Odds book (best price each side), live score, status and its other markets.

GET/api/markets/:id

One market: a three-deep book per selection (crossed levels marked virtual), recent trades, price history, near or actual SP, and, when logged in, your position and a cash-out quote. Match Odds has the fixture id; the others are <fixture>.ht, .ou15, .ou25, .ou35, .btts, .cs.

GET/api/stream

Server-sent events: a version number each time anything changes.

Orders

POST/api/orders

Place an order. 30 order actions per 10 s per user.

{ "market": "gw9-liv-ars", "runner": "H", "side": "back", "odds": 2.5, "stake": 1000,
  "persist": "lapse" | "keep" | "sp",   // what the unmatched part does at kick-off
  "tif": "fok" }                        // optional: match now or cancel the rest

{ "market": "gw9-liv-ars", "runner": "H", "side": "lay", "sp": true, "liability": 1000, "limit": 3 }
                                        // a starting price bet: back takes "stake", lay takes "liability"

Returns { order, bets }. In play, the order comes back delayed and reaches the book after the bet delay. SP orders come back as sp and match at kick-off.

PATCH/api/orders/:id { odds?, stake? }

A smaller stake keeps the order's place in the queue; a new price cancels it and places a fresh one.

DELETE/api/orders/:id

Cancel one order.

DELETE/api/orders?market=:id

Cancel all your unmatched orders, in one market or everywhere.

POST/api/cashout { market }

Hedge the market at the best prices so every outcome pays the same.

Account

GET/api/me

Balance, exposure, available, positions, orders, bets, P&L by market, statement, withdrawals, deposit address.

GET/api/me/statement.csv

The statement as CSV.

POST/api/withdraw { amount, to?, event }

event is a kind 27235 event signed by your key with tags challenge, withdraw (the amount) and to (the address). to is accepted on txbt4 only.

POST/api/faucet

Sidechain only: ask the faucet for test sats to your own address.

A bot in a few lines

const B = 'https://melvin.me/public/oddstr', h = { 'content-type': 'application/json', authorization: `Bearer ${token}` };
const m = await (await fetch(`${B}/api/markets/gw9-liv-ars`)).json();
const best = m.runners[0].back[0];          // best price to back the home win
await fetch(`${B}/api/orders`, { method: 'POST', headers: h,
  body: JSON.stringify({ market: m.id, runner: 'H', side: 'back', odds: best.odds, stake: 500, tif: 'fok' }) });

bots/marketmaker.mjs in the repo is a full market maker built on this API.