Skip to content

Sportsbook iframe integration: sessions, events, height and security

A developer's guide to sportsbook iframe integration: launch tokens, SDK options, postMessage events, auto height and security for a play-for-fun sportsbook.

Updated 10 min readBy The Sportsbook team

A sportsbook iframe integration has two halves: your server opens a player session with an API key and gets a single-use launch URL, and your page loads that URL in an iframe, either through a small JavaScript SDK or as a raw <iframe> that you wire up with postMessage. This guide covers both halves for The Sportsbook, with the real code, the ten events the embed sends, auto height, session renewal and a security checklist. It is a play-for-fun sportsbook: players bet free tokens, never real money.

Key takeaways

iframe vs SDK vs API: what each does here

Vendor pages usually answer "iframe vs API" in one paragraph: the iframe is faster, the API more flexible. Here the three pieces simply have separate jobs.

The embed places bets with the player's session token, never with your API key. Your server doesn't call the bet endpoints at all. If you're still deciding whether you need a hosted sportsbook or raw prices, the comparison of sportsbook APIs, odds APIs and widgets covers that first.

The launch token flow

Every page view that shows the sportsbook starts on your server, which knows who is logged in and asks for a session:

Shell
curl -X POST https://api.thesportsbook.app/b2b/v1/sessions \  -H "Authorization: Bearer $SPORTSBOOK_API_KEY" \  -H "Content-Type: application/json" \  -d '{"user_ref": "viewer-42", "display_name": "Viewer 42", "locale": "en"}'
JSON
{  "launch_token": "eyJhbGciOi...",  "launch_url": "https://play.thesportsbook.app/embed?token=eyJhbGciOi...",  "expires_in": 60}

user_ref is your own stable, pseudonymous id for the player (1 to 128 characters). Don't use an email address, a real name or anything that changes, such as a display name. display_name should be a nickname the player chose.

Why single use and 60 seconds? The launch URL ends up in the page and the iframe's src. A token that works once and dies a minute later is worthless to anyone who copies it. The embed exchanges it straight away for a player session that lasts 12 hours, and removes it from the URL.

So:

Mounting with the SDK

The SDK is the recommended path. Load it once and call TheSportsbook.init:

HTML
<div id="sportsbook"></div><script src="https://play.thesportsbook.app/sdk.js"></script><script>  const sportsbook = TheSportsbook.init({    container: "#sportsbook",    launchUrl: "LAUNCH_URL_FROM_YOUR_SERVER",    height: "auto",    onBetPlaced(bet)    { refreshBalance(); },    onTokensRequested() { openFreeRefill(); },    async onSessionExpired(e) {      if (e.reason === "user_blocked") return;      const { launch_url } = await fetch("/api/sportsbook/session", { method: "POST" }).then((r) => r.json());      sportsbook.updateToken(launch_url);    },  });</script>

refreshBalance, openFreeRefill and the /api/sportsbook/session route are your own code. If you don't need callbacks at all, two lines are enough: <div data-thesportsbook data-launch-url="LAUNCH_URL_FROM_YOUR_SERVER"></div> plus the script tag. The SDK mounts every element with data-thesportsbook on page load.

Options

Methods

init returns an instance with four methods: updateToken() swaps the session without a reload, setHeight() switches between auto and fixed height at runtime, on() and off() add and remove listeners (pass "*" for every event), and destroy() removes the iframe and every listener. The full list, including the static helpers, is in the SDK reference (opens in a new tab). TypeScript types are served at https://play.thesportsbook.app/sdk.d.ts.

The ten events

Events are informational. The source of truth for balances is the API in managed mode, or the calls to your own wallet in seamless mode. Never credit or debit tokens because a message arrived in the browser.

The raw iframe: postMessage events, origin and source checks

If you can't load a third-party script, or you want full control, use the iframe directly:

HTML
<iframe id="sportsbook" src="LAUNCH_URL_FROM_YOUR_SERVER"        style="width:100%;height:720px;border:0" allow="clipboard-write" title="Sportsbook"></iframe><script>  const iframe = document.getElementById("sportsbook");  window.addEventListener("message", (e) => {    if (e.origin !== "https://play.thesportsbook.app" || e.source !== iframe.contentWindow) return;    const msg = e.data;    if (!msg || msg.source !== "sportsbook") return;    switch (msg.type) {      case "resize":            iframe.style.height = msg.payload.height + "px"; break;      case "bet-placed":        refreshBalance(); break;      case "tokens-requested":  showHowToGetTokens(); break;      case "session-expired":   renewSession(msg.payload.reason); break;      case "maintenance":       setBetPromptsPaused(msg.payload.active); break;      case "tenant-suspended":  showUnavailableNotice(msg.payload.active); break;    }  });</script>

The helper functions are your code. Three checks matter, in this order:

When you post to the iframe, always pass the embed origin as the target, never "*". A wildcard target sends your message to whatever document happens to be in the frame. Details are in the raw iframe section of the docs (opens in a new tab).

Auto height and mobile layouts

Nested scrollbars ruin embedded apps on phones. With height: "auto" the SDK resizes the iframe to its content, so the page scrolls as one document. It also tells the sportsbook which part of the iframe is on screen, so the betslip and notifications stay in view even when the iframe is taller than the viewport. If your layout needs a fixed box, pass a number of pixels, or switch at runtime with setHeight().

The layout follows the width of the iframe, not the window, so give it your full content column. On a phone-sized frame the sidebar becomes a drawer and the betslip a bottom sheet; on a wide one you get the sidebar or top bar and the list or grid cards you picked in the console. Test at the widths your theme really renders.

Renewing a session without a reload

A player session lasts 12 hours. Long matches, a laptop left open overnight or a browser tab restored the next day will outlive it. When that happens the embed sends session-expired with reason: "expired". The same event arrives when the player presses "Start a new session" on one of the embed's session screens.

The fix is the same in every case: ask your server for a new session and hand it to the running iframe.

JavaScript
async onSessionExpired(e) {  if (e.reason === "user_blocked") return;  const { launch_url } = await fetch("/api/sportsbook/session", { method: "POST" }).then((r) => r.json());  sportsbook.updateToken(launch_url);}

updateToken() accepts a launch token or the full launch URL, and the result arrives as session-updated or error. The iframe isn't reloaded, so the player stays where they were. With a raw iframe, post { source: "sportsbook-sdk", type: "session-token", payload: { token } } to the iframe with the embed origin as target, or reload the iframe with a new URL.

Your /api/sportsbook/session route must identify the player from your own session cookie. It must not read a user_ref from the request body.

Handling maintenance, suspension and blocked players

Three states can interrupt play. The embed already shows each one to the player; the events let your page react too.

Blocking takes effect quickly: an open embed is told within about a second and emits session-expired with reason: "user_blocked". Unblocking doesn't revive old sessions, so open a new one. Open bets still settle normally. If a player asks you to stop them playing, blocking is how you do it.

Rate limits

Two budgets apply, and they are sized so that normal use never reaches them:

The rate limit section of the docs (opens in a new tab) has the details.

Security checklist

Adapted from the security checklist in the docs (opens in a new tab):

A word on allowed origins: they govern browser API access. They don't stop another site from framing your sportsbook, because framing isn't restricted per operator yet. What protects a sportsbook is that only your server, with your API key, can open player sessions. Without a fresh launch URL from you, a frame on someone else's site has no player to show.

If you run your own token balances, the seamless wallet adds its own list (signature checks, idempotency, retries), covered in the seamless wallet integration guide.

What sportsbook iframe vendors usually leave out

Most sportsbook iframe pages we found describe the benefits and show no code. Whichever provider you evaluate, these are the questions worth asking before you sign anything:

Some answers will differ for real-money providers, because a real-money wallet has requirements a play-for-fun one doesn't. If you're building on a site that needs real-money betting, talk to those providers.

Where to go next

For the step-by-step version from sign-up to a branded sportsbook, read how to embed a sportsbook on your website. On React, the React and Next.js embed guide turns this into a route handler and a component. The complete guide to embeddable sportsbooks covers sports, leagues, branding, plans and the play-for-fun rules.

The quickest way to see the events, the auto height and the mobile layout is the live sportsbook demo (opens in a new tab): the real embed with free demo tokens and no sign-up.

Frequently asked questions

What is a sportsbook iframe?

It is a complete sportsbook served from the provider's domain and shown inside a frame on your page. Your site keeps its own login and layout, and the frame handles markets, the betslip and bet history. With The Sportsbook, your server opens a player session first and the frame loads with a single-use launch URL.

Is iframe or API integration better for a sportsbook?

They do different jobs. The API is server-to-server: opening player sessions and managing token balances. The iframe is the player interface. With The Sportsbook you use both: one API call per session, then the iframe or the SDK to show it. The API doesn't hand you odds to build your own betting interface.

How long does a sportsbook iframe integration take?

The technical part is small: one server endpoint that opens a session and a few lines of HTML or JavaScript. Most of the time goes into your own login, how players get free tokens, and testing on phones. The Free plan lets you try the whole flow before you commit to anything.

Can I make the sportsbook iframe resize to its content?

Yes. With the SDK, set height to 'auto' (the default) and the iframe follows its content. With a raw iframe, listen for the resize message, check its origin and source, and set the iframe height from payload.height in pixels.

Can other websites embed my sportsbook iframe?

Framing isn't restricted per operator yet, so don't rely on that. What protects your sportsbook is that only your server, holding your API key, can open player sessions. The launch URL is single use and expires after 60 seconds, so a copied link is useless.

Written by

The Sportsbook team

The people who build The Sportsbook. We have spent seven years building sportsbook software, and we write these guides from that work.

Last updated .

  1. Guides

    Embed a sportsbook in a React or Next.js app

    Embed a sportsbook in React or Next.js: a route handler that opens sessions, a component around the JS SDK, cleanup and events. Play-for-fun, no real money.

    7 min read

See it on a real page. Open the playground, no sign-up.

The playground is the real embed on a sample site, with free demo tokens. Place a bet, watch it settle, then start your own on Free.

Free plan · nothing to pay, no approval. For businesses and creators, 18+.