---
title: "Similar Bets (Bet Slip)"
canonical_url: "https://apidocs.sportradar.com/resources/widgets/docs/bet-recommendation/similar-bets"
markdown_url: "https://apidocs.sportradar.com/resources/widgets/docs/bet-recommendation/similar-bets.md"
last_updated: "2026-05-28T09:25:24Z"
---

# Similar Bets (Bet Slip)

**Similar Bets** widget (BetSlip Recommendation) is a front-end widget that provides bet recommendations based on existing selection in the bet slip.

- Punters past activity
- General betting trends
- Overall betting volume
- Seasonal variations
- New discovery and engagement patterns

> **Info**
>
> **Similarity requirement**
> This widget requires at least one event ID in the `similarEventIds` array to generate recommendations. It analyzes these events to find matches with similar characteristics (sport, status, betting patterns).

**Recommendation type**

- **Similar events**: List of events that are similar to the events in the bet slip.

**Integration options**

- The widget can be placed next to or inside the bet slip displaying events that are similar to those that have already been added to the bet slip.

## Key Capabilities

### Smart Matching

Smart AI-driven recommendations based on the punter's most recent and relevant bet slip activity.

### Live & Pre-match

Seamless support for both real-time in-play event suggestions and upcoming pre-match selection boosts.

### Native Flow

Deeply integrated user experience that feels like a natural extension of the operator's checkout flow.

### Flexible Placement

Designed to sit either next to or directly inside the bet slip, adapting to various sidebar and modal widths.

### Responsive Agility

Fully adaptive design that maintains high-density information clarity across desktop and mobile viewports.

### Branding Control

Complete creative control over colors, typography, and corner radius to match your platform's design system.

## Settings and Customization

- Time span: live, upcoming, or both (default)
- Sport filter: single sport or multiple sports
- Competition/country filter
- Events: number of recommendations

## Styling Options

- Elements (container, cards, buttons): colors and rounded corners
- Text: font styles and colors

## Requirements

- Access to the client API

- Required endpoints: Odds, Match IDs of selections currently in the bet slip

- Requires an adapter to be registered via `SIR('registerAdapter', '{ADAPTER_NAME}')`. See the adapter overview: <https://apidocs.sportradar.online/resources/widgets/docs/adapter/Overview>

- **widget-name**: `betRecommendation.similarBets`

**Environment Requirements**

## Technical Requirements:

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

**Supported Sports**

## Main Configurable Features

See the [Similar Bets widget demo](https://widgets.sir.sportradar.com/bet-recommendation#widget:\(name:betRecommendation.similarBets\)).
Illustrations of main configurations with relevant property values below.

**Default**

![Default](https://apidocs.sportradar.com/resources/widgets/static/img/similar-bets/default.png)

**Compact Cards**

![Compact Cards](https://apidocs.sportradar.com/resources/widgets/static/img/similar-bets/compactCards.png)
Two similar events for balanced space usage.

### Filters Object

The `filters` object provides optional filtering to refine similar event recommendations. **Note**: `recommendationType` is automatically set to "similar" and cannot be changed.

| Property            | Type                    | Default     | Description                                                                                                                                                                                                                                                                                                                                                                                                |
| ------------------- | ----------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `sport`             | `object`                | `undefined` | Sport filter configuration.                                                                                                                                                                                                                                                                                                                                                                                |
| `sport.available`   | `array<string\|number>` | `[]`        | Array of Sportradar sport IDs to limit similar event recommendations. Empty array or omitted shows similar events from all sports. Example: `[1]` restricts to soccer similar events only. Useful when bet slip contains single-sport selections and you want to maintain sport consistency. See [Sports Reference](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/references/sports.md). |
| `sport.sportNames`  | `boolean`               | `false`     | Display mode for sport indicators. When `true`, shows sport names as text labels. When `false`, shows sport-specific icons. Typically keep `false` for compact display in bet slip.                                                                                                                                                                                                                        |
| `time`              | `object`                | `undefined` | Time/status filter configuration.                                                                                                                                                                                                                                                                                                                                                                          |
| `time.available`    | `array<string>`         | `[]`        | Array of time filter options. Options: `"live"` (only live similar events), `"not_started"` (only upcoming similar events). Example: `["not_started"]` shows only upcoming similar events. Useful to match bet slip event timing (if bet slip has only pre-match bets, show only pre-match similar events).                                                                                                |
| `time.active`       | `array<string>`         | `[]`        | Initially active time filters. Can select multiple from `available` array. Example: `["not_started"]` starts showing only upcoming events.                                                                                                                                                                                                                                                                 |
| `country`           | `object`                | `undefined` | Country/region filter configuration.                                                                                                                                                                                                                                                                                                                                                                       |
| `country.available` | `array<string\|number>` | `undefined` | Array of country identifiers to limit similar events geographically. Example: `["GB", "DE", "ES"]` shows only similar events from UK, Germany, Spain. Useful for geo-compliance or regional betting preferences. See [Getting Identifiers](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/getting-identifiers.md).                                                                        |
| `league`            | `object`                | `undefined` | League/tournament filter configuration.                                                                                                                                                                                                                                                                                                                                                                    |
| `league.available`  | `array<string\|number>` | `undefined` | Array of tournament/league identifiers to limit similar events by competition. Example: `["sr:tournament:17", "sr:tournament:34"]` shows only similar events from Premier League and Bundesliga. Useful when bet slip contains events from specific leagues. See [Getting Identifiers](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/getting-identifiers.md).                            |

#### Filters Example

```javascript
{
  sport: {
    available: [1, 2],  // Soccer and Basketball only
    sportNames: false
  },
  time: {
    available: ['not_started'],
    active: ['not_started']  // Only upcoming similar events
  },
  country: {
    available: ['GB', 'DE', 'ES', 'IT']  // European countries
  },
  league: {
    available: [
      'sr:tournament:17',   // Premier League
      'sr:tournament:34',    // Bundesliga
    ]
  }
}
```

## Theming

Theming customization allows to tailor the appearance of Bet Recommendation widgets to meet specific needs and preferences. In the context of the Bet Recommendation widget, customization refers to the ability to modify the default styling of the widget by applying custom CSS properties to the various HTML elements that make up the widget.

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.

All custom classes must be nested within the `.sr-bb.sr-br-betslip` selector class. This ensures that the custom styles only apply to that widget and not to other elements on the page.

### Custom Class Selectors

| CSS class                     | Supported CSS properties                     |
| ----------------------------- | -------------------------------------------- |
| `srct-br-container`           | `background-color`, `font-family`            |
| `srct-br-footer`              | `color`                                      |
| `srct-br-loading`             | `background-color`, `border-radius`          |
| `srct-br-content`             | `background-color`                           |
| `srct-br-content__title`      | `color`                                      |
| `srct-br-cardlist`            | `gap`                                        |
| `srct-br-cardlist__divider`   | `border-color`, `border-width`               |
| `srct-br-card`                | `background-color`, `color`, `border-radius` |
| `srct-br-card__divider`       | `border-color`                               |
| `srct-br-outcome`             | `background-color`, `color`, `border-radius` |
| `srct-br-outcome--selected`   | `background-color`, `color`, `border-radius` |
| `srct-br-outcome--disabled`   | `background-color`, `color`, `border-radius` |
| `srct-br-outcome__name`       | `font-size`, `color`                         |
| `srct-br-outcome__value`      | `font-size`, `color`                         |
| `srct-br-eventinfo`           | `font-size`, `color`                         |
| `srct-br-eventinfo__info`     | `font-size`                                  |
| `srct-br-eventinfo__icon`     | `color`                                      |
| `srct-br-eventinfo__time`     | `font-size`, `color`                         |
| `srct-br-eventinfo__status`   | `font-size`, `color`                         |
| `srct-br-eventinfo__name`     | `font-size`, `color`                         |
| `srct-br-scoreboard`          | `font-size`, `color`                         |
| `srct-br-scoreboard__teams`   | `font-size`, `color`                         |
| `srct-br-scoreboard__scores`  | `font-size`, `color`                         |
| `srct-br-scoreboard__score-1` | `font-size`, `color`                         |
| `srct-br-scoreboard__score-2` | `font-size`, `color`                         |

## Integration

> **Warning**
>
> **Mandatory Configuration**
> The `filters` property is required for this widget to function correctly. It defines the recommendation logic and basic data constraints.

> **Warning**
>
> **Similar Recommendations Requirement**
> When using the `similar` recommendation type, you must provide the `similarEventIds` identifier to specify the reference matches. Refer to [Getting Identifiers](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/getting-identifiers.md) for more details.

![Bet Recommendation technical guide](https://apidocs.sportradar.com/resources/widgets/static/img/betRecommendationTechnicalGuide.jpg)

Demo: [Open demo](https://widgets.sir.sportradar.com/bet-recommendation#widget:\(name:betRecommendation.similarBets\))

**Basic Sidebar**

Basic bet slip sidebar integration using current event IDs from the user's bet slip.

**JavaScript**

```javascript
const betSlipEventIds = betSlip.getEventIds();

SIR("addWidget", "#bet-slip-recommendations", "betRecommendation.similarBets", {
  similarEventIds: betSlipEventIds,
  count: 3,
});
```

**HTML (data attributes)**

```html
<div
  class="sr-widget"
  data-sr-widget="betRecommendation.similarBets"
  data-similar-event-ids="[12345, 67890, 11111]"
  data-count="3"
></div>
```

**Dynamic Update**

Dynamic update logic to refresh recommendations whenever the bet slip contents change.

**JavaScript**

```javascript
betSlip.on("change", function () {
  const eventIds = betSlip.getEventIds();

  if (eventIds.length > 0) {
    SIR(
      "addWidget",
      "#bet-slip-recommendations",
      "betRecommendation.similarBets",
      {
        similarEventIds: eventIds,
        count: 3,
      },
    );
  } else {
    document.getElementById("bet-slip-recommendations").innerHTML = "";
  }
});
```

**Minimal Display**

Minimal display with one or two similar events for compact UI containers.

**JavaScript**

```javascript
SIR("addWidget", "#bet-slip-recommendations", "betRecommendation.similarBets", {
  similarEventIds: [12345],
  count: 1,
});
```

**HTML (data attributes)**

```html
<div
  class="sr-widget"
  data-sr-widget="betRecommendation.similarBets"
  data-similar-event-ids="[12345]"
  data-count="1"
></div>
```

**Click Handler**

Interaction handling for adding selections to the bet slip and tracking analytics.

**JavaScript**

```javascript
SIR("addWidget", "#bet-slip-recommendations", "betRecommendation.similarBets", {
  similarEventIds: betSlip.getEventIds(),
  count: 3,
  onItemClick: function (target, data) {
    if (target === "outcome") {
      const { event, market, outcome } = data;

      betSlip.addSelection({
        eventId: event.id,
        eventName: `${event.teams.home.name} vs ${event.teams.away.name}`,
        marketId: market.id,
        marketName: market.name,
        outcomeId: outcome.id,
        outcomeName: outcome.name,
        odds: outcome.odds,
        sportId: event.sport.id,
      });

      gtag("event", "similar_bet_added", {
        source: "bet_slip_recommendations",
        event_id: event.id,
        odds: outcome.odds,
      });
    } else if (target === "event") {
      showEventDetailsModal(data.event.id);
    }
  },
});
```

**Soccer Filtered**

Recommendations filtered specifically for soccer events.

**JavaScript**

```javascript
SIR("addWidget", "#bet-slip-recommendations", "betRecommendation.similarBets", {
  similarEventIds: betSlip.getEventIds(),
  count: 3,
  filters: {
    sport: {
      available: [1],
    },
  },
});
```

**HTML (data attributes)**

```html
<div
  class="sr-widget"
  data-sr-widget="betRecommendation.similarBets"
  data-similar-event-ids="[12345, 67890]"
  data-count="3"
  data-filters='{"sport": {"available": [1]}}'
></div>
```

**Upcoming Only**

Filtered for upcoming events that have not yet started.

**JavaScript**

```javascript
SIR("addWidget", "#bet-slip-recommendations", "betRecommendation.similarBets", {
  similarEventIds: betSlip.getEventIds(),
  count: 3,
  filters: {
    time: {
      available: ["not_started"],
      active: ["not_started"],
    },
  },
});
```

**HTML (data attributes)**

```html
<div
  class="sr-widget"
  data-sr-widget="betRecommendation.similarBets"
  data-similar-event-ids="[12345, 67890]"
  data-count="3"
  data-filters='{"time": {"available": ["not_started"], "active": ["not_started"]}}'
></div>
```

**Layout Variants**

Horizontal cards layout for wide bet slip containers or marketplace views.

**JavaScript**

```javascript
SIR("addWidget", "#bet-slip-recommendations", "betRecommendation.similarBets", {
  similarEventIds: betSlip.getEventIds(),
  count: 3,
  cardsLayout: "horizontal",
});
```

**HTML (data attributes)**

```html
<div
  class="sr-widget"
  data-sr-widget="betRecommendation.similarBets"
  data-similar-event-ids="[12345, 67890]"
  data-count="3"
  data-cards-layout="horizontal"
></div>
```

**Manager Lifecycle**

Advanced integration using a manager class to handle the full bet slip lifecycle.

**JavaScript**

```javascript
class BetSlipManager {
  constructor() {
    this.betSlip = new BetSlip();
    this.setupRecommendations();
  }

  setupRecommendations() {
    this.betSlip.on("selectionAdded", () => this.updateRecommendations());
    this.betSlip.on("selectionRemoved", () => this.updateRecommendations());
    this.betSlip.on("cleared", () => this.hideRecommendations());
  }

  updateRecommendations() {
    const eventIds = this.betSlip.getEventIds();
    if (eventIds.length === 0) return this.hideRecommendations();

    SIR(
      "addWidget",
      "#bet-slip-recommendations",
      "betRecommendation.similarBets",
      {
        similarEventIds: eventIds,
        count: Math.min(3, 4 - eventIds.length),
        onItemClick: (target, data) => {
          if (target === "outcome") {
            this.betSlip.addSelection(data);
          }
        },
      },
    );
  }

  hideRecommendations() {
    document.getElementById("bet-slip-recommendations").innerHTML = "";
  }
}
```

**Styled Container**

With custom styling options to control widget padding and spacing.

**HTML (data attributes)**

```html
<div
  class="sr-widget"
  data-sr-widget="betRecommendation.similarBets"
  data-similar-event-ids="[12345, 67890]"
  data-count="3"
  data-styling='{"disablePaddingTop": false, "disablePaddingBottom": false}'
></div>
```

### onItemClick

The `onItemClick` callback is fired whenever the user interacts with the widget. The first argument is a `target` string that identifies the interaction type; the second argument is a `data` object containing contextual information.

| `target` value          | Trigger                                                        | Key `data` properties                                         |
| :---------------------- | :------------------------------------------------------------- | :------------------------------------------------------------ |
| `"externalOutcome"`     | User clicks a single outcome button                            | `externalEvent`, `externalMarket`, `externalOutcome`          |
| `"externalOutcomes"`    | User clicks multiple outcomes at once (e.g. combo card)        | Array of `{ externalEvent, externalMarket, externalOutcome }` |
| `"externalEvent"`       | User clicks an event header/card                               | `externalEvent`                                               |
| `"externalCompetition"` | User clicks a competition/league name                          | `externalCompetition`                                         |
| `"goToBetSlip"`         | User clicks the "Go to Bet Slip" button (swipeBet only)        | —                                                             |
| `"betSlipMode"`         | Bet slip mode changes between single and multi (swipeBet only) | `value: "single" \| "multi"`                                  |

```javascript
SIR("addWidget", "#sr-widget", "betRecommendation.markets", {
  onItemClick: function (target, data) {
    if (target === "externalOutcome") {
      // Add single outcome to bet slip
      const { externalEvent, externalMarket, externalOutcome } = data;
      betSlip.add({
        eventId: externalEvent.id,
        marketId: externalMarket.id,
        outcomeId: externalOutcome.id,
      });
    } else if (target === "externalEvent") {
      // Navigate to event/match detail page
      window.location.href = `/matches/${data.externalEvent.id}`;
    } else if (target === "externalCompetition") {
      // Navigate to competition/league page
      window.location.href = `/league/${data.externalCompetition.tournament.id}`;
    }
  },
  filters: { recommendationType: { available: "popular" } },
});
```

For the full flow after a selection is emitted — trusting snapshot odds, re-pricing against your platform, validating availability, and accepting or rejecting the bet — see [Handling Selections and Odds](https://apidocs.sportradar.com/resources/widgets/docs/tutorials/handling-selections-and-odds.md).

Note: Widgets support callbacks on outcome clicks — the `onItemClick` handler receives `target === "outcome"` and a `data` object containing `externalEvent`, `externalMarket` and `externalOutcome`. Use this for custom outcome callbacks (e.g., add-to-betslip, analytics, modals).

The widget also exposes `onTrack` for event tracking analytics. See the [tracking guide](https://apidocs.sportradar.online/resources/widgets/docs/tutorials/tracking-guide) for details.

### Bet Slip Sync

To keep the widget's selected-outcome state in sync with your own bet slip (i.e. show outcomes as selected when they were added outside the widget), use `registerOnBetSlipChange` inside `registerAdapter`.

```javascript
// 1. Track your bet slip state
let changeCallback;
let betSlipState = { betslip: [], combinedOddsValue: undefined };

// 2. Notify the widget whenever the bet slip changes
function onBetSlipChanged(callback) {
  changeCallback = callback;
  changeCallback && changeCallback(betSlipState); // push current state immediately
}

// 3. Handle outcome clicks from the widget
function onItemClick(target, data) {
  if (target === "externalOutcome") {
    const newBet = {
      externalEventId: data.externalEvent.id,
      externalMarketId: data.externalMarket.id,
      externalOutcomeId: data.externalOutcome.id,
      externalSpecifierId: data.externalMarket.specifier
        ? data.externalMarket.specifier.value
        : undefined,
    };
    betSlipState = {
      betslip: [...betSlipState.betslip, newBet],
      combinedOddsValue: "14.52", // replace with your own calculation
    };
    changeCallback && changeCallback(betSlipState);
  }
}

// 4. Register adapter with bet slip sync + add the widget
SIR("registerAdapter", "{ADAPTER_NAME}", { onBetSlipChanged });
SIR("addWidget", "#sr-widget", "betRecommendation.markets", { onItemClick });
```

Until a custom adapter is developed, use the `mockData` adapter for local testing:

```javascript
SIR("registerAdapter", "mockData", { onBetSlipChanged });
```

### Adapter Setup

An **adapter** is a software component developed by the Sportradar engineering team that bridges the Bet Recommendation widgets and your platform's API. It retrieves data from your API and feeds it to the widget, ensuring seamless communication between the two systems.

SIR Widgets supports two types of adapter implementations:

1. [Self-Service Adapter](https://apidocs.sportradar.com/resources/widgets/docs/adapter/SelfServiceImplementation.md)
2. [Hosted Adapter](https://apidocs.sportradar.com/resources/widgets/docs/adapter/HostedImplementation.md)

Before adapter development begins, confirm and align your API contract with the Sportradar engineering team. Integration requires two SIR calls:

| SIR method                    | Purpose                                                               |
| :---------------------------- | :-------------------------------------------------------------------- |
| `SIR('registerAdapter', ...)` | Configure the adapter that retrieves and displays data from your API. |
| `SIR('addWidget', ...)`       | Mount the widget on the page.                                         |

```javascript
(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/sportradar/widgetloader",
  "SIR",
  { language: "en" },
);

SIR("registerAdapter", "{ADAPTER_NAME}");
SIR("addWidget", "#sr-widget", "betRecommendation.markets");
```

#### Adapter Endpoint Requirements

##### Mandatory Endpoints

- `eventMarkets` or (`market` + `availableMarketsForEvent`)
- `event`

##### Optional Endpoints

- `filterMarkets` - narrows markets selection for widget specific use
- `betSlipSelection` - visual representation of markets already in betslip
- `cashBackSelections` - adds visual representation of cash back markets

#### Adapter Implementation Template

This is an example of an adapter implementing all endpoints. It is intended to be a copy/paste template, where only data fetching and transformation need to be implemented. When implementing an adapter, implement only the endpoints which are required by the widget being integrated, and discard the rest. For each endpoint, only the `getData${ENDPOINT_NAME}()` and `transormData${ENDPOINT_NAME}()` functions need to be implemented.

***Expand Adapter Template Code***

```html
<script type="text/javascript">
// Widget loader script from Step 3 
ndatory 
// -------- Data + Transform functions --------

async function getDataMarket(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataMarket(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    market: {
      id: data.marketId,
      name: data.marketName,
      outcomes: data.outcomes.map((outcome) =>  { id: outcome.id, name: outcome.name, odds: outcome.odds } )
    },
    event: { id: data.eventId, type: data.eventType }
  };
  */

  return {
    market: {
      id: "sr:market:1",
      name: "Match Winner",
      outcomes: [
        { id: "1", name: "Home", odds: 2.5 },
        { id: "X", name: "Draw", odds: 3.0 },
        { id: "2", name: "Away", odds: 3.2 }
      ]
    },
    event: "sr:match:12345"
  };
}

async function getDataAvailableMarketsForEvent(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataAvailableMarketsForEvent(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    selection: data.map((selection) => { type: selection.type, event: selection.event, market: selection.market})
  }; 
  */

  return {
    selection: [
      {
        type: "uf",
        event: "61513908",
        market: "1",
      },
    ],
  };
}

async function getDataEventMarkets(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataEventMarkets(data, args) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  let markets = []

  function mapMarkets(market){
    return {
      id: market.id,
      status: market.status,
      name: market.name,
      outcomes: market.outcomes.map((outcome) => {id: outcome.id, name: outcome.name , odds: { type: outcome.odds.type, value: outcome.odds.value}, status: outcome.status})
    };
  }

  return data.forEach(mapMarkets);
  */

  return {
    event: args.selection.event,
    markets: [
      {
        id: "1",
        status: "active",
        name: "1x2",
        outcomes: [
          {
            id: "1",
            name: "Tenhaisen",
            odds: { type: "decimal", value: "1.88" },
            status: "active",
          },
          {
            id: "2",
            name: "draw",
            odds: { type: "decimal", value: "3.85" },
            status: "active",
          },
          {
            id: "3",
            name: "Hoftenstain",
            odds: { type: "decimal", value: "3.7" },
            status: "active",
          },
        ],
      },
    ],
  };
}

async function getDataEvent(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataEvent(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    event: {
      id: data.event,
      date: {
        displayValue: data.displayTime,
        startTime: data.dateTime,
      },
      sport: {
        id: data.sport.id,
        name: data.sport.name,
      },
      category: {
        id: data.category.id,
        name: data.category.country,
      },
      tournament: {
        id: data.tournament.id,
        name: data.tournament.name,
      },
      teams: data.teams.map((team) => {id: team.id, name: team.name}),
      isLive: data.isLive,
    },
  }; 
  
  */
  return {
    event: {
      id: args.selection.event,
      date: {
        displayValue: "14/01/26, 19:30",
        startTime: "2026-01-14T19:30:00.000Z",
      },
      sport: {
        id: "1",
        name: "Soccer",
      },
      category: {
        id: "30",
        name: "Germany",
      },
      tournament: {
        id: "42",
        name: "Liga Supreme",
      },
      teams: [
        { id: "1270229", name: "Tenhaisen" },
        { id: "31531", name: "Hoftenstain" },
      ],
      isLive: false,
    },
  };
}

async function getDataFilterMarkets(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataFilterMarkets(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    selection: data.selection.map((market) => {type: market.type, event: market.event, market: market.id})
  }
  */
  return {
    selection: [
      {
        type: "uf",
        event: "61513908",
        market: "1",
      },
    ],
  };
}

async function getDataBetSlipSelection(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataBetSlipSelection(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    selection: data.selection.map((market) => {event: market.event, market: market.market, outcome: market.outcome, type: market.type}),
  };
  */

  return {
    selection: [
      {
        event: "61513908",
        market: "1",
        outcome: "1",
        type: "uf",
      },
    ],
  };
}

async function getDataCashBackSelections(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataCashBackSelections(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    events: data.events.map((event) => {event: event.id, type: event.type}),
  }
  */

  return {
    events: [
      {
        event: "56418457",
        type: "uf",
      },
    ],
  };
}

async function getDataTickets(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataTickets(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    tickets: data.tickets.map((ticket) => {
      ticketId: ticket.id,
      bets: ticket.bets.map((bet) => {
        betId: bet.id,
        selections: bet.selections.map((selection) => {
          type: selection.type,
          eventId: selection.eventId,
          marketId: selection.marketId,
          outcomeId: selection.outcomeId,
          odds: { type: selection.odds.type, value: selection.odds.value },
        }),
        odds: { type: bet.odds.type, value: bet.odds.value },
        stake: bet.stake.map((stake) => {
          type: stake.type,
          currency: stake.currency,
          amount: stake.amount,
          mode: stake.mode
        }),
      }),
      version: ticket.version,
    }),
  };
  */

  return {
    tickets: [
      {
        ticketId: "ticket_123456",
        bets: [
          {
            betId: "bet_001",
            selections: [
              {
          type: "uf",
          productId: "1",
          eventId: "sr:match:12345",
          marketId: "1",
          outcomeId: "1",
          odds: { type: "decimal", value: "2.10" },
          context: {
            eventName: "Manchester United vs Liverpool",
            marketName: "1x2",
            outcomeName: "Home",
            tournament: "Premier League",
            sportName: "Soccer",
            categoryName: "England",
            isLive: false,
            eventStartTime: "1738519200000",
            sportId: "1"
          }
        }
            ],
            odds: { type: "decimal", value: "2.10" },
            stake: [{
              type: "cash",
              currency: "USD",
              amount: "10.00",
              mode: "total"
            }],
            payout: [{ type: "cash", currency: "USD", amount: "21.00" }]
          }
        ],
        version: "2.0"
      }
    ]
  };
}

async function getDataMatchEventSuggestedSelection(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataMatchEventSuggestedSelection(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    selections: data.selections.map((selection) => {
      event: selection.event,
      market: selection.market,
      outcome: selection.outcome,
      type: selection.type,
      specifiers: selection.specifiers
    })
  };
  */

  return {
    selections: [
      { event: "sr:match:12345", market: "1", outcome: "1", type: "uf" },
      { event: "sr:match:12345", market: "18", specifiers: "total=2.5", type: "uf" }
    ]
  };
}

async function getDataRecommendedSelections(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataRecommendedSelections(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    selection: data.selections.map((selection) => {
      event: selection.event,
      market: selection.market,
      outcome: selection.outcome,
      type: selection.type,
      specifiers: selection.specifiers
    })
  };
  */

  return {
    selection: [
      { event: "sr:match:12345", market: "1", outcome: "1", type: "uf" },
      { event: "sr:match:67890", market: "18", specifiers: "total=2.5", outcome: "13", type: "uf" }
    ]
  };
}

async function getDataCalculateCustomBetXML(args) {
  // Here fetch data from your data source and return it
  return {};
}

function transformDataCalculateCustomBetXML(data) {
  // Here transform your data into data structure exemplified by the object below.
  /*
  // Illustration how data transformation might work from client data to Adapter types
  return {
    payload: '<xml>data</xml>',
  }
  */
  return {
    payload: `<filtered_calculation_response generated_at="2025-04-16T13:29:08+00:00">
                <calculation odds="27.50303106727931" probability="0.027418715351118873" harmonization="false"/>
                <available_selections>
                  <event id="sr:match:12345678">
                    <markets>
                      <market id="65" specifiers="hcp=0:2" conflict="false">
                        <outcome id="1711" conflict="true"/>
                        <outcome id="1712" conflict="true"/>
                        <outcome id="1713" conflict="false"/>
                      </market>
                      ...
                    </markets>
                  </event>
                </available_selections>
              </filtered_calculation_response>`
  };
}

// -------- Adapter --------

const adapter = {
  config: {},
  endpoints: {
    market: (args, callback) => {
      getDataMarket(args)
        .then(data => transformDataMarket(data))
        .then(result => callback(undefined, result));
      return () => {};
    },

    availableMarketsForEvent: (args, callback) => {
      getDataAvailableMarketsForEvent(args)
        .then(data => transformDataAvailableMarketsForEvent(data))
        .then(result => callback(undefined, result));
      return () => {};
    },

    eventMarkets: (args, callback) => {
      getDataEventMarkets(args)
        .then(data => transformDataEventMarkets(data, args))
        .then(result => callback(undefined, result));
      return () => {};
    },

    event: (args, callback) => {
      getDataEvent(args)
        .then(data => transformDataEvent(data, args))
        .then(result => callback(undefined, result));
      return () => {};
    },

    filterMarkets: (args, callback) => {
      getDataFilterMarkets(args)
        .then(data => transformDataFilterMarkets(data))
        .then(result => callback(undefined, result));
      return () => {};
    },

    betSlipSelection: (args, callback) => {
      getDataBetSlipSelection(args)
        .then(data => transformDataBetSlipSelection(data))
        .then(result => callback(undefined, result));
      return () => {};
    },

    cashBackSelections: (args, callback) => {
      getDataCashBackSelections(args)
        .then(data => transformDataCashBackSelections(data))
        .then(result => callback(undefined, result));
      return () => {};
    },

    tickets: (args, callback) => {
      getDataTickets(args)
        .then(data => transformDataTickets(data))
        .then(result => callback(undefined, result));
      return () => {};
    },

    matchEventSuggestedSelection: (args, callback) => {
      getDataMatchEventSuggestedSelection(args)
        .then(data => transformDataMatchEventSuggestedSelection(data))
        .then(result => callback(undefined, result));
      return () => {};
    },

    recommendedSelections: (args, callback) => {
      getDataRecommendedSelections(args)
        .then(data => transformDataRecommendedSelections(data))
        .then(result => callback(undefined, result));
      return () => {};
    },

    calculateCustomBetXML: (args, callback) => {
      getDataCalculateCustomBetXML(args)
        .then(data => transformDataCalculateCustomBetXML(data))
        .then(result => callback(undefined, result));
      return () => {};
    },
  },
};
</script>
```

### Data Types

The following data types are provided by the adapter and are also available in the `onItemClick` callback payload.

**Event**

| Property                          | Type                | Required | Description                                                      |
| :-------------------------------- | :------------------ | :------- | :--------------------------------------------------------------- |
| `id`                              | `string \| number`  | Yes      | Sportradar event ID.                                             |
| `externalId`                      | `string \| number`  | —        | Client-side event ID.                                            |
| `date`                            | `string`            | Yes      | Formatted date string displayed in the widget.                   |
| `sport.id`                        | `string \| number`  | Yes      | Sport ID. Use `sportsMapping` if not using Sportradar sport IDs. |
| `sport.name`                      | `string`            | Yes      | Sport name.                                                      |
| `category.id`                     | `string \| number`  | —        | Category ID.                                                     |
| `category.name`                   | `string`            | Yes      | Category name (e.g. "England").                                  |
| `tournament.id`                   | `string \| number`  | —        | Tournament/league ID.                                            |
| `tournament.name`                 | `string`            | Yes      | Tournament name.                                                 |
| `teams`                           | `Array<{id, name}>` | Yes      | Home and away competitors.                                       |
| `isLive`                          | `boolean`           | Yes      | Whether the event is currently live.                             |
| `liveCurrentTime`                 | `string`            | Yes      | Live time display (e.g. `"2nd set"`, `"45'"`).                   |
| `result1` / `result2` / `result3` | `result`            | —        | Score columns: `{ result: [homeScore, awayScore] }`.             |

**Market**

| Property          | Type               | Required | Description                                            |
| :---------------- | :----------------- | :------- | :----------------------------------------------------- |
| `id`              | `string \| number` | Yes      | Market ID.                                             |
| `name`            | `string`           | —        | Market name (e.g. `"Match Winner"`).                   |
| `status.isActive` | `boolean`          | —        | When `false`, see [Widget Behavior](#widget-behavior). |

**Outcome**

| Property          | Type               | Required | Description                                                                        |
| :---------------- | :----------------- | :------- | :--------------------------------------------------------------------------------- |
| `id`              | `string \| number` | Yes      | Outcome ID.                                                                        |
| `name`            | `string`           | Yes      | Outcome name (e.g. `"Home"`, `"Draw"`).                                            |
| `odds`            | `string \| number` | Yes      | Odds value. Use a `number` type to enable odds-change indicators (up/down arrows). |
| `specifier.value` | `string \| number` | —        | Additional specifier (e.g. handicap value `"-2.50"`).                              |
| `status.isActive` | `boolean`          | —        | When `false`, see [Widget Behavior](#widget-behavior).                             |

## Tips

### Dynamic `similarEventIds` Management

Re-render the widget whenever the bet slip changes to keep recommendations in sync:

```javascript
betSlip.on("selectionAdded", updateSimilarBets);
betSlip.on("selectionRemoved", updateSimilarBets);
betSlip.on("selectionChanged", updateSimilarBets);
betSlip.on("cleared", hideSimilarBets);

function updateSimilarBets() {
  const eventIds = betSlip.getEventIds();
  if (eventIds.length === 0) {
    hideSimilarBets();
    return;
  }
  SIR("addWidget", "#similar-bets", "betRecommendation.similarBets", {
    similarEventIds: eventIds,
    count: 3,
  });
}
```

Debounce updates if the bet slip fires multiple events in quick succession:

```javascript
let updateTimer;

function debouncedUpdateSimilarBets() {
  clearTimeout(updateTimer);
  updateTimer = setTimeout(() => {
    updateSimilarBets();
  }, 500);
}

betSlip.on("selectionAdded", debouncedUpdateSimilarBets);
betSlip.on("selectionRemoved", debouncedUpdateSimilarBets);
```

Limit recommendations as the bet slip grows, and hide the widget when it is full:

```javascript
function updateSimilarBets() {
  const selections = betSlip.getSelections();
  const eventIds = selections.map((s) => s.eventId);

  if (eventIds.length === 0) {
    hideSimilarBets();
    return;
  }

  if (eventIds.length >= 5) {
    hideSimilarBets();
    return;
  }

  const recommendationCount = Math.min(3, 5 - eventIds.length);
  SIR("addWidget", "#similar-bets", "betRecommendation.similarBets", {
    similarEventIds: eventIds,
    count: recommendationCount,
  });
}
```
