← SideBet.vip

API documentation

Build bots and integrations on top of SideBet.vip. Create manual or automatically settled sports bets, join offers, read your history and receive signed webhooks. Machine-readable version for LLMs and scripts: /docs/api.md.

Environments: FUN and REAL

SideBet.vip runs two fully separated environments. FUN uses demo credits (1000 on signup) and is the default. REAL uses a real-money balance that starts at zero and requires a one-off acceptance of the real-money terms in the web app. Balances, offers, bets, history, rankings and referral commissions never cross between them, and a FUN offer can only be matched by a FUN bet.

An API key is bound to one environment when you create it and always acts there. OAuth tokens default to FUN; send X-SideBet-Mode: REAL to act in real money. Every bet, transaction and account response carries a mode field, and listings only return objects from the caller's environment.

Authentication

Generate a key in your account page. The secret is shown once — store it safely. Send it as a bearer token on every request. Keys are either read (GET only) or full (create, join, cancel, confirm). Rate limit: 60 requests per minute per key.

curl https://sidebet.vip/api/public/v1/me \
  -H "Authorization: Bearer sbv_live_..."

OAuth 2.0 for third-party apps

Building an app that acts on behalf of other users? Use OAuth 2.0 with PKCE instead of asking users for their API keys. Register your client through the dynamic client registration endpoint, send users through the SideBet consent screen, and call the API with the resulting access token as a bearer token — OAuth tokens have full API scope.

curl https://sidebet.vip/api/public/v1/me \
  -H "Authorization: Bearer <oauth_access_token>"

Endpoints

GET/api/public/v1/mescope: read

Your profile and wallet balance (available and locked credits).

GET/api/public/v1/categoriesscope: read

Bet categories with commission and confirmation window.

GET/api/public/v1/fixtures?league=Ekstraklasascope: read

Upcoming matches available for automatically settled bets.

GET/api/public/v1/weather-markets?city=Warsawscope: read

Open weather markets (METAR station, local day, temperature line) for auto-settled bets.

GET/api/public/v1/bets?status=OPEN&mine=false&limit=50scope: read

Open marketplace bets plus every bet you take part in.

GET/api/public/v1/bets/{betId}scope: read

A single bet with positions and confirmations.

POST/api/public/v1/betsscope: full

Create a manual bet. Your stake is locked immediately.

POST/api/public/v1/bets/matchscope: full

Create a sports bet settled automatically from the official full-time result.

POST/api/public/v1/bets/weatherscope: full

Create a weather bet settled automatically from the station's daily maximum.

POST/api/public/v1/bets/{betId}/joinscope: full

Join an open bet by matching its stake.

POST/api/public/v1/bets/{betId}/cancelscope: full

Cancel your own bet while it is still unmatched; the stake is refunded.

POST/api/public/v1/bets/{betId}/confirmscope: full

Confirm the outcome of a manual bet: { "outcome": "WON" | "LOST" }.

GET/api/public/v1/transactions?limit=100scope: read

Your credit history: stakes, wins, losses, refunds and commissions.

Response shapes

All responses are JSON with camelCase keys; collections come wrapped in a named array. Always pass the id you received back as marketId / fixtureId — never rebuild it from the station code.

GET /api/public/v1/weather-markets?city=Warsaw

{
  "markets": [
    {
      "id": "wx:warsaw:2026-09-10",      // pass this as marketId
      "city": "Warsaw",
      "country": "Poland",
      "timezone": "Europe/Warsaw",
      "marketDate": "2026-09-10",         // local calendar day covered
      "thresholdC": 19.5,                 // the line, always in Celsius
      "forecastTmax": 21.3,               // forecast only, never settles
      "closesAt": "2026-09-09T22:00:00.000Z",   // local midnight, betting stops
      "dayEndAt": "2026-09-10T22:00:00.000Z",   // next local midnight
      "resolvesAt": "2026-09-10T22:10:00.000Z", // settlement, +10 minutes
      "stationIcao": "EPWA",
      "stationName": "Warsaw Chopin",
      "unit": "C",                        // "C" or "F" - the station's unit
      "bandLabel": "19°C",                // outcome band shown to users
      "lineLabel": "19–20°C",             // whole degrees around the line
      "aboveLabel": "20°C and above",     // wins for pick "ABOVE"
      "belowLabel": "19°C or lower"       // wins for pick "BELOW"
    }
  ]
}
GET /api/public/v1/fixtures?league=Ekstraklasa

{
  "fixtures": [
    {
      "id": "2052881",                    // pass this as fixtureId
      "league": "Ekstraklasa",
      "homeTeam": "Legia Warszawa",
      "awayTeam": "Lech Poznań",
      "homeBadge": "https://.../legia.png",
      "awayBadge": null,
      "kickoffAt": "2026-09-12T17:30:00.000Z",
      "venue": "Stadion Wojska Polskiego"
    }
  ]
}
POST /bets, /bets/match, /bets/weather -> 201
{ "betId": "08c5dfef-7d71-4a0a-9fad-001597e4bb2e" }

POST /bets/{betId}/join | cancel | confirm -> 200
{ "ok": true }

Creating bets

curl -X POST https://sidebet.vip/api/public/v1/bets \
  -H "Authorization: Bearer sbv_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "BTC above 100k on Friday",
    "description": "Bitcoin closes above 100,000 USD on Friday 23:59 UTC.",
    "categoryId": "<category uuid>",
    "creatorSide": "YES",
    "stakeAmount": 100,
    "startsAt": "2026-09-10T10:00:00Z",
    "endsAt": "2026-09-12T23:59:00Z",
    "resolutionSource": "Binance BTC/USDT daily close"
  }'
curl -X POST https://sidebet.vip/api/public/v1/bets/match \
  -H "Authorization: Bearer sbv_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "fixtureId": "2052881", "pick": "HOME", "stakeAmount": 50, "safeClose": true, "partialFill": true }'
curl -X POST https://sidebet.vip/api/public/v1/bets/weather \
  -H "Authorization: Bearer sbv_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "marketId": "wx:warsaw:2026-09-10", "pick": "ABOVE", "stakeAmount": 50, "safeClose": true, "partialFill": true }'

Optional safeClose cancels an unmatched bet automatically and refunds the stake — at kickoff for match bets, at 12:00 local time in the city for weather bets — so nobody can join once the outcome is already leaning one way. Optional partialFill (creator-only, auto-settled bets) pairs an unmatched bet with the biggest opposing offer on the same event at close time: the smaller bet closes as MERGED and its creator joins the bigger bet at the smaller stake, with any surplus refunded. Match bets always settle from the official result. A draw voids the bet and both stakes are returned in full. Weather bets settle 10 minutes after local midnight from the METAR station's daily maximum temperature; the line always falls between two whole station degrees, so a tie is impossible.

Webhooks

Set one https callback URL in your account. We POST JSON for bet.matched, bet.settled, bet.refunded and bet.disputed. Every request carries x-sidebet-event and x-sidebet-signature, an HMAC-SHA256 hex digest of the raw body using your signing secret.

const expected = crypto
  .createHmac("sha256", process.env.SIDEBET_WEBHOOK_SECRET)
  .update(rawBody)
  .digest("hex");
if (expected !== req.headers["x-sidebet-signature"]) return res.status(401).end();

Errors

Errors return a JSON body { "error": "..." } with status 400 (invalid request), 401 (bad key), 403 (read-only key), 404 (not found) or 429 (rate limit). Withdrawals are not available through the API.