Skip to main content
Logo
Explore APIsSupport Portal
  • Home
  • Match Preview
  • Tournament Preview
  • Virtual Stadium
  • StatsHub
  1. Engagement Tools
  2. Tournament Bet Insights

Tournament Bet Insights

Widgets, Engagement Tools, BET
Bet InsightsBet Recommendation
Last updated about 2 months ago
Is this site helpful?
On this page
  • Supported Content and Environment
  • Required Parameters
  • Adapter
  • Adapter Endpoint Requirements
  • API Reference
  • Tournament-Specific Parameters
  • Shared Parameters
  • Basic Integration Example
  • Advanced Integration
  • Tournament Bet Slip Integration — Full Example
  • Custom Theming
  • Tournament-specific theming classes
  • Tips
  • Identifier Selection
  • Controlling Insight Volume
  • Tournament Hub Integration
  • Match Context Display
  • Responsive Design
  • Related Resources

Tournament Bet Insights widget displays AI-powered betting insights and recommendations across multiple matches within a tournament, league, or season. The widget analyzes betting patterns, historical data, and statistical trends to highlight valuable betting opportunities from upcoming matches in the specified competition. It aggregates insights from multiple events to help users identify the most promising betting opportunities across an entire tournament or league, making it ideal for overview pages, tournament hubs, and league standings sections. The widget offers the same flexible integration modes and card layout variants as the match-level Bet Insights widget, including inline display for dedicated sections or button-triggered modal for compact placements.

Unified odds feed (UOF) mapping required

This widget requires Sportradar Unified Odds Feed (UOF) identifiers to correctly match insights to your offering and odds. If your platform does not use UOF IDs you will need to map UOF identifiers to your own market/outcome IDs. Mapping UOF to proprietary identifiers is complex and is difficult to achieve full coverage across all markets and specifier variants — expect limited support.

Warning — this widget isn't suited with declarative integration

This widget requires the onItemClick callback to communicate user interactions with your application. Because HTML declarative integration cannot provide callback functions, the declarative method is not suited for this widget — use the JavaScript/programmatic integration shown in the examples below.

Tournament Bet Insights Web Inline

See the Tournament Bet Insights widget demo.

#Supported Content and Environment

#Required Parameters

  • widget-name: betInsights.tournament

Required identifiers (at least one):

At least one of the following identifiers must be provided:

  • matchId: Sportradar match identifier. Shows insights for matches related to this match's tournament
  • tournamentId: Sportradar tournament identifier. Shows insights for all matches in this specific tournament
  • uniqueTournamentId: Sportradar unique tournament identifier. Shows insights for matches across all seasons of this tournament
  • seasonId: Sportradar season identifier. Shows insights for all matches in this specific season

See Getting Identifiers for details on obtaining these identifiers.

Environment Requirements

Technical Requirements:

  • JavaScript enabled
  • XMLHttpRequest support for data fetching
  • CSS3 support for styling and animations

Supported Sports

  • Soccer

Supported Languages

Expanded Table

Supported Markets

Soccer

Expanded Table

#Adapter

For implementation guidance, example adapter implementations, and the adapter endpoint contract required by this widget, see the Adapter overview. It explains how to choose between Generic-Sportradar, custom-mapping, or self-hosted adapters and includes sample payloads you can adapt.

warning

Betting insights rely on Sportradar's Unified Odds Feed (UOF) market and outcome identifiers. You must map those identifiers to your application's market/outcome IDs. While simple market mapping can be achieved for common markets, achieving complete coverage across all markets and variants is difficult.

#Adapter Endpoint Requirements

Required:
  • eventMarkets or (market + availableMarketsForEvent)
  • event
Optional:
  • betSlipSelection

Main Configurable Features

Integration Mode

The widget can be embedded inline (always visible) or as a button that opens a pop-up overlay.

Inline Integration

Always-visible insights displayed directly on the page — ideal for dedicated betting sections.

javascript

#API Reference

#Tournament-Specific Parameters

Expanded Table

#Shared Parameters

All of the following parameters from Bet Insights are also supported:

Expanded Table

note

minOdds, maxOdds, and testMarkets are available on only and are supported by Tournament Bet Insights.

Integration Process

This section outlines the process required to integrate the Bet Insights widget into your platform.

Communication Channel Setup

Your sales contact person will initiate a shared communication channel via Slack invite to facilitate real-time collaboration between teams. This channel serves as the primary medium for answering questions, exchanging required information, and providing ongoing integration support.

Technical Kickoff Meeting

Your sales contact person will organize a kickoff meeting with your technical team and Sportradar's integration team. This meeting serves as the formal start of the technical integration process and ensures both teams are aligned on requirements, timelines, and next steps. During the kickoff the teams will decide the adapter implementation type — this is important because Sportradar needs to confirm whether we have access to your odds and you can use generic adapter or if your platform uses Unified Odds Feed (UOF) identifiers. If your platform does not use UOF IDs, you may need to implement a custom adapter on your side; that decision can affect timeline, required effort, and the level of market coverage achievable.

#Basic Integration Example

Properties do not always transfer from the above table directly into integration code. Properties must be transformed differently for each integration method:

JavaScript/Programmatic Integration

  • Property names remain unchanged in camelCase
  • Properties become members of the 4th parameter object in SIR() call
  • Example: cardVariant: "compact"
info

In javascript integration, the properties go into an object which is passed as the 4th argument of the call ti SIR() function. Please see Global SIR API

HTML/Declarative Integration

  • Convert camelCase to lowercase with dashes, e.g. cardVariant becomes card-variant
  • Add data-sr- prefix
  • Example: cardVariant →

To run this widget you must provision an adapter that supplies match and market data. The best adapter type depends on how your data is structured and delivered. During onboarding our team will review your data, recommend the optimal adapter, and assist with configuration.

Learn more about adapter options here.

Important

Replace <CLIENT_ID> and <DATA_SOURCE> with the values provided during onboarding (for example client id: client1, data source: spider). In code use adapterDataSource: 'spider' and https://widgets.sir.sportradar.com/client1/widgetloader.

#Advanced Integration

info

The examples in this section demonstrate the bet slip integration pattern using the Bet Insights widget. The pattern is identical for Tournament Bet Insights — adapt the identifier prop (uniqueTournamentId, seasonId, tournamentId, or matchId) and widget key (betInsights.tournament) in your integration.

Handling user selections

The following example shows how to respond when a user clicks an insight card. The onItemClick callback receives the click target and the outcome data, which you can use to build a selection object and push it into your bet slip. Walk through the highlighted sections to understand each part of the flow.

#Tournament Bet Slip Integration — Full Example

The following snippet brings together the widgetloader, the adapter's betSlipSelection endpoint, and the onItemClick handler specifically for Tournament Bet Insights.

Important

Replace <CLIENT_ID> and <DATA_SOURCE> with the values provided during onboarding (for example client id: client1, data source: spider). In code use adapterDataSource: 'spider' and https://widgets.sir.sportradar.com/client1/widgetloader.

html
<script>
    (function(a,b























































Widget Behavior

The following describes how the Bet Insights widget reacts to data, market and outcome state changes, and user interactions. It covers visibility rules, how selections and bet-slip events are handled, and how adapter updates or error states affect the widget UI and available actions.

Flow Diagram

Bet Insights Integration Data Flow

The diagram above illustrates the complete data lifecycle for the Bet Insights widget, which runs in the end user's browser and coordinates data between Sportradar and your systems:

  1. Insights Generation (1): AI/ML models in the Sportradar API produce market insights and deliver them to the widget.
  2. Market Data Resolution (2–4): The widget requests available markets and odds from your Client API via the Adapter; the Adapter transforms and returns matching available market data.
  3. User Interaction (5–7): The end user clicks an outcome (5); the widget invokes the configured onItemClick handler (6) so your app can add the selection to the bet slip (7).
  4. State Synchronization (8–9): Your Adapter exposes a betSlipSelection subscription/callback that the widget registers; when your bet slip changes the Adapter invokes the callback (8–9) and the widget updates its UI to reflect current selections.

Market status

Market suspended example

When market status is set to suspended, the widget disables that market's outcomes and displays the label "temporary unavailable" in place of odds, indicating the market is temporarily not available for betting.

javascript

#Custom Theming

Widget comes with pre-existing styling but can be customized by applying custom CSS properties to its different HTML elements. The widget's custom class selectors and supported CSS properties are listed below. Note that all custom classes must be nested within the .sr-bb.sr-<WIDGET_NAME> selector class. This ensures that the custom styles only apply to that widget and not to other elements on the page.

Important

Replace <WIDGET_NAME> with insights!

scss
.sr-bb.sr-insights {
    .srct-ins-button {
        border-radius: 2px;
    }
    .srct-ins-showmore {
        color: #4786ff;
    }
}

Expanded Table

#Tournament-specific theming classes

The following classes are specific to the Tournament Bet Insights widget and provide customization for the event header area within each insight card:

Expanded Table

ClassCustomization options
srct-ins-card-headercolor, background-color, border-color, font-family
srct-ins-card-header__live-labelcolor, font-weight, font-family
srct-ins-card-header__competitorscolor, font-size, font-weight
srct-ins-card-header__datetimecolor, font-size, font-weight
srct-ins-card-header__resultcolor, font-weight

#Tips

#Identifier Selection

Choose the right identifier based on your page context:

javascript
// League overview page — use uniqueTournamentId for all seasons
{ uniqueTournamentId: 135 }

// Specific season page — use seasonId
{ seasonId: 132030 }

// Tournament page — use tournamentId for a specific instance
{ tournamentId: 30 }

// Match detail page — use matchId to show insights from other matches in the same tournament
{ matchId: 61591316 }

#Controlling Insight Volume

Use numberOfCards and numberOfEvents to balance coverage and focus:

javascript
// Focused: many insights from few matches
{
    uniqueTournamentId: 135,
    numberOfEvents: 3,   // Only next 3 matches
    numberOfCards: 12    // Up to 12 insights total
}

// Broad: fewer insights spread across more matches
{
    uniqueTournamentId: 135,
    numberOfEvents: 10,  // Up to 10 matches
    numberOfCards: 10    // Only top 10 insights
}





#Tournament Hub Integration

Perfect for tournament overview and standings pages:

javascript
// Tournament hub — button integration in header
SIR('addWidget', '#tournament-header-insights', 'betInsights.tournament', {
    uniqueTournamentId: 135,
    integration: 'button',
    widgetTitle: 'Best Bets',
    numberOfCards: 10
});

// Tournament page sidebar
SIR(















#Match Context Display

When using matchId, show insights from other tournament matches alongside match-specific insights:

javascript
// Match detail page — related matches section
SIR('addWidget', '#related-insights', 'betInsights.tournament', {
    matchId: 61591316,
    integration: 'inline',
    widgetTitle: 'Other Tournament Insights',
    numberOfCards: 8
});

// Match-specific insights on the same page
SIR('addWidget', '#match-insights', 'betInsights',



#Responsive Design

javascript
// Desktop — tournament overview
if (window.innerWidth > 1024) {
    SIR('addWidget', '#tournament-insights', 'betInsights.tournament', {
        uniqueTournamentId: 135,
        integration: 'inline',
        cardsLayout: 'horizontal',
        cardVariant: 'default',
        numberOfCards:












#Related Resources

Bet Insights

Match-level AI betting insights widget with full API reference and advanced integration examples.

Learn More

Getting Identifiers

Learn how to obtain tournament, season, and match IDs for widget configuration.

Learn More

Global SIR API

Complete reference for the global SIR API function, including initialization, configuration options, and widget management methods.

Learn More
CodeLanguageNative
sqiAlbanianShqip
aaArabicالعربية
hyeArmenianՀայերեն
azeAzerbaijaniAzərbaycan dili
bsBosnianBosanski
bgBulgarianБългарски
zhChinese (Simplified)简体中文
zhtChinese (Traditional)中文繁體
hrCroatianHrvatski
csCzechČesky
daDanishDansk
nlDutchNederlands
enEnglish
en_usEnglish (US)
etEstonianEesti
fiFinnishSuomeksi
frFrenchFrançais
kaGeorgianქართული
deGermanDeutsch
elGreekEλληνικά
hebHebrewעברית
hiHindiहिन्दी
huHungarianMagyar
islIcelandicÍslenska
idIndonesianBahasa Indonesia
itItalianItaliano
jaJapanese日本語
kmKhmerខ្មែរ
koKorean한국어
lvLatvianLatviešu
ltLithuanianLietuvių
mkMacedonianМакедонски
noNorwegianNorsk
plPolishPolski
ptPortuguesePortuguês
brPortuguese (Brazil)Português do Brasil
roRomanianRomână
ruRussianРусский
srSerbian (Cyrillic)Cрпски
srlSerbian (Latin)Srpski
skSlovakSlovenčina
slSlovenianSlovenščina
esSpanishEspañol
swSwahiliKiswahili
seSwedishSvenska
thThaiไทย
trTurkishTürkçe
tukTurkmen
ukrUkrainianУкраїнська
viVietnameseTiếng Việt
Market IDMarket Name
11x2
8{!goalnr} goal
9Last goal
10Double chance
11Draw no bet
12{$competitor1} no bet
13{$competitor2} no bet
14Handicap {hcp}
15Winning margin
16Handicap
18Total
19{$competitor1} total
20{$competitor2} total
21Exact goals
23{$competitor1} exact goals
24{$competitor1} exact goals
24{$competitor2} exact goals
25Goal range
26Odd/even
27{$competitor1} odd/even
28{$competitor2} odd/even
29Both teams to score
30Which team to score
31{$competitor1} clean sheet
32{$competitor2} clean sheet
33{$competitor1} win to nil
34{$competitor2} win to nil
351x2 & both teams to score
36Total & both teams to score
371x2 & total
38{!goalnr} goalscorer
39Last goalscorer
40Anytime goalscorer
41Correct score [{score}]
45Correct score
46Halftime/fulltime correct score
47Halftime/fulltime
48{$competitor1} to win both halves
49{$competitor2} to win both halves
50{$competitor1} to win either half
51{$competitor2} to win either half
52Highest scoring half
53{$competitor1} highest scoring half
54{$competitor2} highest scoring half
551st/2nd half both teams to score
56{$competitor1} to score in both halves
57{$competitor2} to score in both halves
58Both halves over {total}
59Both halves under {total}
601st half - 1x2
621st half - {!goalnr} goal
631st half - double chance
641st half - draw no bet
651st half - handicap {hcp}
661st half - handicap
681st half - total
691st half - {$competitor1} total
701st half - {$competitor2} total
711st half - exact goals
741st half - odd/even
751st half - both teams to score
761st half - {$competitor1} clean sheet
771st half - {$competitor2} clean sheet
781st half - 1x2 & both teams to score
791st half - 1x2 & total
811st half - correct score
832nd half - 1x2
842nd half - {!goalnr} goal
852nd half - double chance
862nd half - draw no bet
872nd half - handicap {hcp}
882nd half - handicap
902nd half - total
912nd half - {$competitor1} total
922nd half - {$competitor2} total
932nd half - exact goals
942nd half - odd/even
952nd half - both teams to score
962nd half - {$competitor1} clean sheet
972nd half - {$competitor2} clean sheet
982nd half - correct score
100When will the {!goalnr} goal be scored (15 min interval)
101When will the {!goalnr} goal be scored (10 min interval)
10510 minutes - 1x2 from {from} to {to}
122Will there be a penalty shootout
136Booking 1x2
137{!bookingnr} booking
138Total booking points
139Total bookings
142Exact bookings
143{$competitor1} exact bookings
144{$competitor2} exact bookings
146Sending off
147{$competitor1} sending off
148{$competitor2} sending off
1491st half - booking 1x2
1501st half - {!bookingnr} booking
1511st half - total booking points
1521st half - total bookings
1531st half - {$competitor1} total bookings
1541st half - {$competitor2} total bookings
1551st half - exact bookings
1561st half - {$competitor1} exact bookings
1571st half - {$competitor2} exact bookings
1591st half - sending off
1601st half - {$competitor1} sending off
1611st half - {$competitor2} sending off
162Corner 1x2
163{!cornernr} corner
164Last corner
165Corner handicap
166Total corners
167{$competitor1} total corners
168{$competitor2} total corners
169Corner range
170{$competitor1} corner range
171{$competitor2} corner range
172Odd/even corners
1731st half - corner 1x2
1741st half - {!cornernr} corner
1751st half - last corner
1761st half - corner handicap
1771st half - total corners
1801st half - {$competitor1} exact corners
1811st half - {$competitor2} exact corners
1821st half - Corner range
1831st half - odd/even corners
184{!goalnr} goal & 1x2
199Correct score
220Will there be overtime
540Double chance (match) & 1st half both teams score
541Double chance (match) & 2nd half both teams score
5421st half - double chance & both teams to score
5432nd half - 1x2 & both teams to score
5442nd half - 1x2 & total
5452nd half - double chance & both teams to score
546Double chance & both teams to score
547Double chance & total
548Multigoals
549{$competitor1} multigoals
550{$competitor2} multigoals
551Multiscores
5521st half - multigoals
5532nd half - multigoals
770{player} assists (incl. overtime)
775{player} goals (incl. overtime)
776{player} shots (incl. overtime)
777{player} shots on goal (incl. overtime)
778{player} passes (incl. overtime)
780{player} tackles (incl. overtime)
818Halftime/fulltime & total
819Halftime/fulltime & 1st half total
820Halftime/fulltime & exact goals
854{$competitor1} or over {total}
855{$competitor1} or under {total}
856Draw or over {total}
857Draw or under {total}
858{$competitor2} or over {total}
859{$competitor2} or under {total}
860{$competitor1} or both teams to score
861Draw or both teams to score
862{$competitor2} or both teams to score
863{$competitor1} or any clean sheet
864Draw or any clean sheet
865{$competitor2} or any clean sheet
879{$competitor2} to win
880{$competitor1} to win
881Any team to win
882{player} to score (incl. overtime)
888Anytime goalscorer & 1x2
889Anytime goalscorer & correct score
890{!goalnr} goalscorer & correct score
891{!goalnr} goalscorer & 1x2
11791st Half Result or Match Result
1183{player} total shots (incl. overtime)
1185{player} total shots on goal (incl. overtime)
1187{player} total passes (incl. overtime)
1189{player} total tackles (incl. overtime)
1191{player} to be carded (incl. overtime)
tip

This widget requires an adapter to supply match, market and odds data. See the Adapter overview.

{ integration: "inline" }

Widget Title

The title in the widget header (or button label) can include or hide its icon.

Title with Icon
javascript
{ widgetTitle: "Bet Insights" }

Card Variants

Controls the visual style of each insight card.

Default Card

Standard card with market name and outcome information.

javascript
{ cardVariant: "default" }

Outcome Position

Controls where the outcome name appears relative to the odds on each card.

Expanded Table

PositionConfigDescription
Bottom (default)outcomeButtonPosition: "bottom"Outcome name below the odds
TopoutcomeButtonPosition: "top"Outcome name above the odds

Cards Layout

Vertical Layout

Cards stacked vertically — suitable for narrow containers and sidebars.

javascript
{ cardsLayout: "vertical" }

Pop-up Direction

When using button integration (integration: "button"), controls which direction the pop-up opens.

Pop-up Left
javascript
{ integration: "button", modalPosition: "left" }

Widget Header Options

Header Disabled

Hides the widget header entirely in inline mode.

javascript
{ disableWidgetHeader: true }

See the Bet Insights demo for live examples.

PropertyTypeRequiredDefaultDescription
matchIdnumberConditional-Sportradar match identifier. Shows insights for matches in the same tournament as this match. At least one of matchId, tournamentId, uniqueTournamentId, or seasonId is required. See Getting Identifiers
tournamentIdnumberConditional-Sportradar tournament identifier. Shows insights from all matches in this specific tournament. At least one of the four ID props is required
uniqueTournamentIdnumberConditional-Sportradar unique tournament identifier. Shows insights from matches across all seasons of this tournament. At least one of the four ID props is required
seasonIdnumberConditional-Sportradar season identifier. Shows insights from all matches in this specific season. At least one of the four ID props is required
numberOfCardsnumberNo15Maximum number of insight cards to display. Must be between 1 and 20
numberOfEventsnumberNo-Maximum number of distinct matches to include insights from. Must be between 1 and 20. When unset, insights from all available matches are included
PropertyTypeDefaultDescription
integrationstring"inline"Widget integration mode: "inline" or "button"
cardsLayoutstring"vertical"Layout direction for insight cards: "vertical" or "horizontal"
cardVariantstring"default"Card display style: "default", "defaultIcon", "compact", "button", "buttonFooter", "single"
outcomeOrderstring"bottom"Position of outcome name relative to odds: "top", "bottom". Only applicable with cardVariant='compact', cardVariant='button', and cardVariant='buttonFooter'
outcomeButtonPositionstring"bottom"Position of call-to-action button in card: "top", "bottom". Not applicable with cardVariant='button' and cardVariant='buttonFooter'
widgetTitlestring|false"Tournament Bet Insights"Title displayed in widget header or button label. Set to false to hide
widgetIconstring|falseDefault iconURL of custom icon image. Set to false to hide
disableWidgetHeaderbooleanfalseWhen true, hides widget header in inline mode
enableCollapsebooleantrueWhen true, allows collapsing widget content in inline mode. Only applicable when disableWidgetHeader=false
startCollapsedbooleanfalseWhen true and enableCollapse is true, widget starts collapsed
modalPositionstring"left"Direction modal opens in button integration: "left", "right", "bottom"
modalMaxHeightnumber|string-Maximum height of modal content. Allowed units: %, px, vh. Only applicable with integration='button' and cardsLayout='vertical'
isMobilebooleanfalseWhen true, opens pop-up at viewport bottom edge, full width (only with integration='button')
ignoreAdapterValuesbooleanfalseWhen true, uses Sportradar standard names instead of adapter-provided translations
capitalizeMarketNameAndOutputbooleanfalseWhen true, capitalizes first letter of market and outcome names
onItemClickfunction-Callback triggered when insight card or outcome is clicked. Receives event and outcome data for bet slip integration
Bet Insights
not
data-sr-card-variant
  • Example: filters.sport.hidden → Complex objects must be passed as JSON strings
  • info

    In HTML integration, the properties go into the parent HTML object as object properties, prefixed with data-sr- as explained above.

    Only base property support

    This method supports only simple (base) properties and does not support properties that require functions.

    info

    In all examples replace sportradar in the widgetloader URL path with your clientId.

    Example if your clientId is client1:

    • This URL: https://widgets.sir.sportradar.com/sportradar/widgetloader
    • becomes: https://widgets.sir.sportradar.com/client1/widgetloader
    html
    <script>
        (function(a,b,c,d,e,f,g,h,i){a[e]||(i=a[e]=function(){(a[e].q=a[e].q||[]).push(arguments)},i.l=1*new Date,i.o=f,
        g=b.createElement(c),h=b.getElementsByTagName(c)[0],g.async=1,g.src=d,g.setAttribute("n",e),h.parentNode.insertBefore(g,h)
        )})(window,document,"script","https://widgets.sir.sportradar.com/<CLIENT_ID>/widgetloader","SIR", {
            language: 'en',
            adapterDataSource: '<DATA_SOURCE>'
        });
    
        const widgetProps = {
            // Required identifiers — provide at least one
            uniqueTournamentId: 135,
            // tournamentId: 30,
            // seasonId: 132030,
            // matchId: 61591316,
    
            // Configuration
            integration: "button",
            modalPosition: "right",
            numberOfCards: 15,
    
            // ... Optional: Additional customization props (see API Reference)
        };
    
        SIR('addWidget', '#tournament-bet-insights', 'betInsights.tournament', widgetProps);
    </script>
    
    <div id="tournament-bet-insights"></div>
    javascript
        let betSlipChangeCallback = undefined;
        let betSlipState = { // Simplified bet slip integration for demonstration purposes
            selection: [],
        };
    
        // Called on user interactions
        function onItemClick(target, data) {
            if (target === 'externalOutcome') { //  When user clicks an outcome on an insight card
                // Construct a selection object in the shape your bet slip integration expects.
                // The widget provides external IDs via data.externalEvent, data.externalMarket,
                // and data.externalOutcome — map them to your own format below.
                const bet = {
                    type: 'uf',
                    event: `${data.externalEvent.id}`,
                    market: `${data.externalMarket.id}`,
                    outcome: `${data.externalOutcome.id}`
                };
                if (data.externalMarket.specifier && data.externalMarket.specifier.value) {
                    bet.specifiers = `${data.externalMarket.specifier.value}`;
                }
    
                // Add the new selection
                betSlipState = {
                    selection: [...betSlipState.selection, bet]
                };
    
                // Notify the adapter so the widget reflects the updated bet slip state
                betSlipChangeCallback && betSlipChangeCallback(betSlipState);
            }
        }
    
        const widgetProps = {
            matchId: 61591316,
            onItemClick: onItemClick,
            // ... Additional customization props (see API Reference)
        };
    
        SIR('addWidget', '#bet-insights', 'betInsights', widgetProps);

    Bet Slip Integration

    The following example shows how to connect the widget to your bet slip so it always reflects the punter's current selections. The adapter's betSlipSelection endpoint keeps the widget in sync, and onItemClick lets you act when the user clicks an outcome card. Walk through the highlighted sections to understand each part of the flow.

    We recommend revalidating each selection and refreshing odds when adding them to the bet slip — or at minimum revalidating selections and odds immediately before accepting a bet — to ensure markets are still available and the odds presented to the user are up to date.

    javascript
    {
        onItemClick: function(event, outcomeData) {
            // Add to bet slip
            betSlip.addSelection({
                matchId: outcomeData.matchId,
                marketId: outcomeData.market.id,
                outcomeId: outcomeData.outcome.id,
                odds: outcomeData.odds,
                source: 'bet_insights'
            });
    
        }
    }
    javascript
        const adapter = {
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    Hooking Widget Events into the Bet Slip

    The following example brings together the widgetloader, the adapter's betSlipSelection endpoint, and the onItemClick handler into a single ready-to-use snippet. Replace the placeholder values.

    Important

    Replace <CLIENT_ID> and <DATA_SOURCE> with the values provided during onboarding (for example client id: client1, data source: spider). In code use adapterDataSource: 'spider' and https://widgets.sir.sportradar.com/client1/widgetloader.

    html
    <script>
        (function(a,b,c,d,e,f,g,h,i){a[e]||(i=a[e]=function(){(a[e].q=a[e].q||[]).push(arguments)},i.l=1*new Date,i.o=f,
        g=b.createElement(c),h=b.getElementsByTagName(c)[0],g.async=1,g.src=d,g.setAttribute("n",e),h.parentNode.insertBefore(g,h)
        )})(window,document,"script","https://widgets.sir.sportradar.com/<CLIENT_ID>/widgetloader","SIR", {
            language: 'en',
            adapterDataSource: '<DATA_SOURCE>'
        });
    
        // Tracks the current bet slip selections in the format the widget expects.
        // In production, replace this with your own bet slip store / state manager.
        let betSlipState = { selection: [] };
        let notifyWidget = null;
    
        const adapter = {
            endpoints: {
                betSlipSelection: (args, callback) => {
                    // Store the callback so we can notify the widget whenever selections change.
                    notifyWidget = callback;
                    // Push the current state immediately so the widget is in sync on load.
                    callback(undefined, betSlipState);
    
                    return () => {
                        notifyWidget = null;
                    };
                },
            },
        };
    
        // Register the adapter once on page load — USE ONLY ONE APPROPRIATE TO YOUR ADAPTER TYPE
        SIR('registerAdapter', adapter); // Generic-Sportradar & Self-hosted
        // SIR('registerAdapter', '<HOSTED_ADAPTER_NAME>', adapter); // Custom-mapping
    
        function onItemClick(args) {
            if (args.type === 'addSelectionsToBetSlip') {
                // Map the widget's selections to the format the betSlipSelection endpoint expects.
                const newSelections = args.data.selections.map((sel) => ({
                    type: 'uf',
                    event: sel.eventId,         // e.g., "sr:match:12345"
                    market: sel.marketId,       // e.g., "38"
                    specifiers: sel.specifiers, // e.g., "goalNr=1" (optional)
                    outcome: sel.outcomeId,     // e.g., "sr:player:1050245"
                    odds: { type: 'eu', value: sel.odds },
                }));
    
                betSlipState = {
                    selection: [...betSlipState.selection, ...newSelections],
                };
    
                // Notify the widget so it immediately highlights the newly selected outcomes.
                notifyWidget && notifyWidget(undefined, betSlipState);
            }
        }
    
        const widgetProps = {
            matchId: 61591316,
            onItemClick: onItemClick,
            // ... Additional customization props (see API Reference)
        };
    
        SIR('addWidget', '#bet-insights', 'betInsights', widgetProps);
    </script>
    
    <div id="bet-insights"></div>
    ,
    c
    ,
    d
    ,
    e
    ,
    f
    ,
    g
    ,
    h
    ,
    i
    ){
    a[e]
    ||
    (i
    =
    a[e]
    =
    function
    (){
    (a[e]
    .
    q
    =
    a[e]
    .
    q
    ||
    [])
    .
    push
    (arguments)
    },
    i
    .
    l
    =
    1
    *
    new
    Date
    ,
    i
    .
    o
    =
    f
    ,
    g=b.createElement(c),h=b.getElementsByTagName(c)[0],g.async=1,g.src=d,g.setAttribute("n",e),h.parentNode.insertBefore(g,h)
    )})(window,document,"script","https://widgets.sir.sportradar.com/<CLIENT_ID>/widgetloader","SIR", {
    language: 'en',
    adapterDataSource: '<DATA_SOURCE>'
    });
    let betSlipState = { selection: [] };
    let notifyWidget = null;
    const adapter = {
    endpoints: {
    betSlipSelection: (args, callback) => {
    notifyWidget = callback;
    callback(undefined, betSlipState);
    return () => {
    notifyWidget = null;
    };
    },
    },
    };
    // Register the adapter once on page load — USE ONLY ONE APPROPRIATE TO YOUR ADAPTER TYPE
    SIR('registerAdapter', adapter); // Generic-Sportradar & Self-hosted
    // SIR('registerAdapter', '<HOSTED_ADAPTER_NAME>', adapter); // Custom-mapping
    function onItemClick(args) {
    if (args.type === 'addSelectionsToBetSlip') {
    const newSelections = args.data.selections.map((sel) => ({
    type: 'uf',
    event: sel.eventId, // e.g., "sr:match:12345"
    market: sel.marketId, // e.g., "38"
    specifiers: sel.specifiers, // e.g., "goalNr=1" (optional)
    outcome: sel.outcomeId, // e.g., "sr:player:1050245"
    odds: { type: 'eu', value: sel.odds },
    }));
    betSlipState = {
    selection: [...betSlipState.selection, ...newSelections],
    };
    notifyWidget && notifyWidget(undefined, betSlipState);
    }
    }
    const widgetProps = {
    uniqueTournamentId: 135, // or tournamentId / seasonId / matchId
    onItemClick: onItemClick,
    numberOfCards: 15,
    // ... Additional customization props (see API Reference)
    };
    SIR('addWidget', '#tournament-bet-insights', 'betInsights.tournament', widgetProps);
    </script>
    <div id="tournament-bet-insights"></div>
    const market = {
        id: "1",
        name: "Match Winner (1X2)",
        outcomes: [
            { id: "1", name: "Home", status: "active", odds: { type: "eu", value: "2.10" } },
            { id: "X", name: "Draw", status: "active", odds: { type: "eu", value: "3.40" } },
            { id: "2", name: "Away", status: "active", odds: { type: "eu", value: "3.50" } }
        ],
        status: "suspended"
    };

    If a market or outcome has any status other than active (for example: suspended or cancelled), the widget will hide that market to avoid showing stale or unavailable betting options.

    ClassCustomization options
    srct-ins-buttonbackground-color, border-radius
    srct-ins-button__iconcolor
    srct-ins-button_textcolor, font-size, font-family
    srct-ins-headerbackground-color, border-color, border-width
    srct-ins-header__iconcolor
    srct-ins-header__textcolor, font-size, font-weight
    srct-ins-header__arrowcolor
    srct-ins-containerbackground-color, font-family
    srct-ins-cardbackground-color, border-radius, border-color, box-shadow
    srct-ins-textcolor, text-decoration
    srct-ins-showmorecolor, font-size
    srct-ins-popupbackground-color, color, font-size, font-family
    srct-ins-popup__iconcolor
    srct-ins-ctabackground-color, border-color
    srct-ins-marketcolor, font-size, font-weight
    srct-ins-market__iconcolor
    srct-ins-outcomebackground-color, border-radius, padding
    srct-ins-outcome--selectedbackground-color
    srct-ins-outcome--disabledbackground-color
    srct-ins-outcome__namecolor, font-size, font-weight
    srct-ins-outcome__valuecolor, font-size, font-weight
    srct-ins-bubble-countbackground-color, border-color, color
    srct-ins-bubble-count-textcolor
    srct-ins-bubble-newbackground-color, border-color, color
    srct-ins-robot-iconcolor
    // Maximum coverage
    {
    uniqueTournamentId: 135,
    numberOfCards: 20 // Maximum allowed, no event limit
    }
    'addWidget'
    ,
    '#tournament-sidebar'
    ,
    'betInsights.tournament'
    ,
    {
    uniqueTournamentId: 135,
    integration: 'inline',
    cardsLayout: 'vertical',
    cardVariant: 'compact',
    numberOfCards: 12,
    enableCollapse: true
    });
    // Season standings page
    SIR('addWidget', '#standings-insights', 'betInsights.tournament', {
    seasonId: 132030,
    integration: 'inline',
    cardsLayout: 'horizontal',
    numberOfEvents: 5,
    numberOfCards: 10
    });
    {
    matchId: 61591316,
    integration: 'inline',
    widgetTitle: 'This Match Insights'
    });
    15
    });
    }
    // Mobile — compact button
    else {
    SIR('addWidget', '#tournament-insights', 'betInsights.tournament', {
    uniqueTournamentId: 135,
    integration: 'button',
    isMobile: true,
    modalPosition: 'bottom',
    cardVariant: 'compact',
    numberOfCards: 10
    });
    }

    Widget Theming

    Customize widget appearance, colors, fonts, and styling to match your brand.

    Learn More

    Integration Best Practices

    Performance optimization, security considerations, and deployment strategies.

    Learn More
    endpoints
    :
    {
    betSlipSelection: (args, callback) => {
    yourBetSlipStore.onBetSlipChange((currentSelections) => {
    callback(undefined, {
    selection: currentSelections.map((sel) => ({
    type: 'uf',
    event: sel.eventId, // e.g., "sr:match:12345"
    market: sel.marketId, // e.g., "38"
    specifiers: sel.specifiers, // e.g., "goalNr=1" (optional)
    outcome: sel.outcomeId, // e.g., "sr:player:1050245"
    odds: { type: 'eu', value: sel.odds },
    })),
    });
    });
    return () => {
    yourBetSlipStore.offBetSlipChange();
    };
    },
    },
    };
    // USE ONLY ONE APPROPRIATE TO YOUR ADAPTER TYPE
    SIR('registerAdapter', adapter); // Generic-Sportradar & Self-hosted
    SIR('registerAdapter', '<HOSTED_ADAPTER_NAME>', adapter); // Custom-mapping
    function onItemClick(args) {
    if (args.type === 'addSelectionsToBetSlip') {
    // Check if markets are still open and available
    const isValid = checkSelectionValid(args.data.selections);
    if (isValid) {
    yourBetSlipStore.addSelections(
    convertToStoreSelection(args.data.selections)
    );
    // Check if odds are the same as they were shown in widget
    const haveOddsChanged = checkOdds(args.data.selections);
    // Visual feedback
    if (haveOddsChanged) {
    showToast(`Odds have changed!, ${outcomeData.outcome.name} was added with adjusted odds.`);
    } else {
    showToast(`${outcomeData.outcome.name} added to bet slip`);
    }
    } else {
    // Visual feedback
    showToast(`Selection is no longer available.`);
    }
    }
    }
    const widgetProps = {
    matchId: 61591316,
    onItemClick: onItemClick,
    // ... Additional customization props (see API Reference)
    };
    SIR('addWidget', '#bet-insights', 'betInsights', widgetProps);