STATSCORE SportWidgets Embedding Guide

This documentation will guide you through the process of embedding our widgets on your page smoothly.

Basic concepts

Each widget you can embed on your page has its own configuration that defines what type of widget it is and how it is customized.The configuration is provided to you by STATSCORE and it has format of 24 character string ex. 2d21e6a2db4e9207a5eb2c65. Additionally each type of widget has its own input data structure that you need to provide when you are embedding it. For example, when you want to embed LivematchProV3 Widget which covers certain sport event then input data you provide would contain: event id and language

Embeder

To be able to embed widgets on your page first thing you need to do is include our library:

<script>
!function(){var d="STATSCOREWidgetsEmbederScript";if(!window.document.getElementById(d)){window.STATSCOREWidgets={},window.STATSCOREWidgets.onLoadCallbacks=[],window.STATSCOREWidgets.onLoad=function(d){window.STATSCOREWidgets.onLoadCallbacks.push(d)};var n=window.document.createElement("script");n.src="https://wgt-s3-cdn.statscore.com/bundle/Embeder.js",n.async=!0,n.id=d,n.addEventListener("error",function(d){for(var n=0;n<window.STATSCOREWidgets.onLoadCallbacks.length;n++)window.STATSCOREWidgets.onLoadCallbacks[n](d)}),window.document.body.appendChild(n)}}();
</script>

Example of use

HTML

<div id="myWidget"></div>

JavaScript - embedding widget

// Hook up when library is loaded and ready to use.
// You can use this method as many times as necessary - if library
// is already loaded provided callback will fire immediately
STATSCOREWidgets.onLoad(err => {
    if (err) {
        switch (err.type) {
            case 'NetworkError':
                // Handle network error here
                break;

            case 'BrowserNotSupported':
                // Handle unsupported browser here
                break;
        }

        return;
    }

    // Widget will be appended to this HTMLElement.
    //
    // If you are using framework then follow its documentation
    // on how to get DOM Element from your component.
    // Vue.js https://vuejs.org/v2/api/#ref
    // React https://en.reactjs.org/docs/refs-and-the-dom.html
    // Angular https://angular.io/api/core/ElementRef
    const element = document.getElementById('myWidget');

    // Configuration that you should receive from STATSCORE
    const configurationId = '2d21e6a2db4e9207a5eb2c65';

    // Input data for widget type you want to embded
    const inputData = { eventId: 3038800, language: 'en' };

    // Optional object with options.
    // You can check available options further in the docs.
    const options = {};

    const widget = new window.STATSCOREWidgets.Widget(element, configurationId, inputData, options);
});

JavaScript - events API

const someEventCallback = () => { /* .. do stuff */ };

You can listen for event using "on" and "once" methods

widget.on('someEvent', someEventCallback);
widget.once('someEvent', someEventCallback);

You can destroy listener by using and off method

widget.off('someEvent', someEventCallback);

Available lifecycle events:

widget.on('beforeInsert', () => { /* Triggers when data necessary to display widget is loaded and widget is ready to be inserted into document */ });
widget.on('load', () => { /* Triggers when widget is loaded but not yet interactive */ });
widget.on('mount', () => { /* Triggers when widget is loaded and interactive */ });
widget.on('error', e => { /* Handle errors here */ });

You can handle more specific errors by checking it's error code. See full list of possible codes <a href="#error-codes">here</a>

widget.on('error', e => {
    if (e.code === 1234) {
        console.log(e.message);
        ...
    }
});

Event coverage:

widget.on('requiredCoverageNotSatisfied', ({ isFtOnly: boolean, isBasic: boolean, statsLvl: StatsLvl}) => {
    // If given widget depends on "eventId" input data and "satisfiesCoverage" option
    // was provided this event will be triggered when given constrains weren't satisfied.
});

You can find more about "satisfiesCoverage" option in <a href='#options'>"Options" section</a>.

JavaScript - removing widget

// Remove our widget from DOM Tree and cleanup everything that is
// related to this widget. You can remove widget from the DOM Tree
// directly - destroy method will be queued and executed after short
// delay, although we recommend using destroy method directly.
//
// If you are using framework then we recommend hooking into one of
// component lifecycle hooks and calling destroy directly
// Vue.js https://vuejs.org/v2/api/#beforeDestroy
// React https://en.reactjs.org/docs/react-component.html#componentwillunmount
// Angular https://angular.io/guide/lifecycle-hooks#ondestroy
await widget.destroy();

Widgets groups

If you find yourself in a situation when you embed the same type of widget for each sport, you can use widget groups to organize things a little.

A group of configurations allows you to have one entity which links each configuration you choose with the sport you choose. The only limitation is that each group can have only one type of widgets inside (for example, you can't have LivematchProWidget and StandingsWidget in one group).

After you create your group, you can use its ID to embed it. Only difference between embedding regular configuration is that you have to use STATSCOREWidgets.WidgetGroup class instead of STATSCOREWidgets.Widget. API of WidgetGroup class is exactly the same as Widget class.

Widgets input data list

EventIncidentsWidget

WidgetParameter(s)
EventIncidents (by event)language
eventId
providerId (optional)

EventsWidget

WidgetParameter(s)
Events (by season)language
seasonId
eventType
groupId (optional)
Events (by season & participant)language
seasonId
participantId
eventType
groupId (optional)
Events (by participant)language
participantId
eventType

EventStatsWidget

WidgetParameter(s)
EventStats (by event)language
eventId
providerId (optional)

FormH2HWidget

WidgetParameter(s)
FormH2H (by event)language
eventId
providerId (optional)

GeneralH2HWidget

WidgetParameter(s)
GeneralH2H (by participants)language
participantId1
participantId2
GeneralH2H (by event)language
eventId
providerId (optional)

H2HWidget

WidgetParameter(s)
H2H (by event)language
eventId
providerId (optional)

InfographicWidget

WidgetParameter(s)
Infographic (by event)language
eventId
providerId (optional)

LastMatchesH2HWidget

WidgetParameter(s)
LastMatchesH2H (by participants)language
participantId1
participantId2
LastMatchesH2H (by event)language
eventId
providerId (optional)

LineupsWidget

WidgetParameter(s)
Lineups (by event)language
eventId
providerId (optional)

LiveBarWidget

WidgetParameter(s)
LiveBar (by event)language
eventId
providerId (optional)

LiveWidget

WidgetParameter(s)
Live (by event)language
eventId
providerId (optional)

ScoreboardWidget

WidgetParameter(s)
Scoreboard (by event)language
eventId
providerId (optional)

StandingsWidget

WidgetParameter(s)
Standings (by event)language
eventId
providerId (optional)
Standings (by season)language
seasonId
Standings (by competition)language
competitionId
providerId (optional)

StandingWidget

WidgetParameter(s)
Standing (by standing)language
standingId
Standing (by event)language
eventId
providerId (optional)
standingType
Standing (by season)language
seasonId
standingType
Standing (by stage)language
stageId
standingType
Standing (by competition)language
competitionId
standingType
providerId (optional)

StatsH2HWidget

WidgetParameter(s)
StatsH2H (by event)language
eventId
providerId (optional)

StatsSliderWidget

WidgetParameter(s)
StatsSlider (by event)language
eventId
providerId (optional)
StatsSlider (by season)language
seasonId
StatsSlider (by competition)language
competitionId
providerId (optional)

LivematchProWidget

WidgetParameter(s)
LivematchPro (by event)language
eventId
providerId (optional)

PrematchProWidget

WidgetParameter(s)
PrematchPro (by event)language
eventId
providerId (optional)

VoteWidget

WidgetParameter(s)
Votelanguage
eventId
providerId (optional)

Parameters list

ParameterTypeDescription
languagestringLanguage (default "en")
eventIdnumberId of sport event. If you are NOT using STATSCORE ids then you should prefix the id with "m:" (for example m:123)
participantId1, participantId2numberIds of participants you want to compare
standingIdnumberId of standing
seasonIdnumberId of season
groupIdnumberId of group
stageIdnumberId of stage
competitionIdnumberId of competition. If you are NOT using STATSCORE ids then you should prefix the id with "m:" (for example m:123)
standingTypestringAvailable options depends on sport (see table below)
eventTypestringPossible values: fixture, results
invertedParticipantsbooleanSet 'true' if you want to invert participants (e.g. for American sports)
providerIdnumberId of mapping provider. This option works only if your account has enabled option for multiple mapping providers.

Standing types per sport

SportStanding types
American FootballLeague, Home, Away, Form
BaseballLeague, Home, Away, Form
BasketballLeague, Home, Away, Form
Basketball 3x3League
FutsalLeague, Home, Away, Form, TopScorers, Cards, Halftime
HandballLeague, Home, Away, Form, Halftime
Ice hockeyLeague, Home, Away, Form
RugbyLeague, Home, Away
Rugby LeagueLeague
SoccerLeague, Home, Away, Form, TopScorers, Assists, Cards, Halftime
SpeedwayLeague, GrandPrix
TennisAtpRanking, AtpDoubles, WtaRanking, WtaDoubles
VolleyballLeague, Home, Away, Form

Options

NameTypeDefaultDescription
loader{ enabled?: boolean, size?: number, color1?: string, color2?: string }{ enabled: false, size: 60, color1: '#ffffff', color2: '#808080' }Decide if you want to display loader and customize it. Keep in mind that if someone has good connection displaying loader can actually make widgets show up slower since we wait until initial animation of loader is finished before displaying the widget
mobileAppTokenstring | undefinedundefinedIf you are embedding widgets in mobile application you must pass token that was provided by STATSCORE
satisfiesCoverage{ ftOnlyAcceptable?: boolean, basicAcceptable?: boolean, minStatsLevel?: StatsLvl }undefinedPassing this option embeds widget conditionally if given event coverage constraints are fulfilled. If given coverage constraints are not sasisfied "requiredCoverageNotSatisfied" event is triggered. You can find example in "JavaScript - events API" section. ftOnlyAcceptable - should widget be loaded if only full time result is available for given event. basicAcceptable - should widget be loaded if given event has only basic coverage. minStatsLevel - minimum stats level that given event should have in order to be loaded. Should be one of values: bronze, silver, gold, vip. This option works only for widgets with input data that contains "eventId"

Error codes

CodeDescription
1000Configuration with given ID cannot be found
1001Widget group with given ID cannot be found
1002Widget group ID has wrong format
1003Widget group does not contain configuration for given sport
1004Token authorization failed
1005Host authorization failed
1006Host authorization failed. Subdomains are going to deep
1007Event not found
1008Event doesn't exist
1009Participants not found
1010Competition doesn't exist
1011Season not found
1012Stage not found
1013Standing not found
1014Sport not found
1015Venue not found
1016Invalid incidents ID's
1017Brackets for stage or stage doesn't exist
1018There is no prematch widget instance. If Widgets and LiveSwapper were initialized asynchronously then this may be the issue
1019eventId is required input data parameter in both prematch and live widgets when using LiveSwapper
1020eventId should be same in both prematch and live widgets
1021Invalid LiveSwapper options property
1022Invalid satisfiesCoverage property
1023Invalid satisfiesCoverage.ftOnlyAcceptable property
2002Invalid satisfiesCoverage.basicAcceptable property
1024Invalid satisfiesCoverage.minStatsLevel property
1025Provided configuration doesn't support sport derived from input data
1026Provider does not exist or is not available
1027Mapping for provided event id does not exist
1028Mapping for provided competition id does not exist

Integrations

Integrations allow to communicate and pass data to widgets and components. Each integration has its own API and its available when involved component gets initiated.

You can use given integration by using getIntegration method:

widget.getIntegration('IntegrationName', integration => {
   // Here you can use integration that has been resolved
});

Interactivity integration

Interactivity integration can be used to intercept various user interactions (for example clicks on elements)

Example of binding standing participants click (integration is available in standing components):

widget.getIntegration('Interactivity', integration => {
    const onClickCallback = participant => {
        console.log(participant);
    };

    // Use integration standingParticipant.onClick method to bind your callback
    integration.standingParticipant.onClick(onClickCallback);

    // To remove click bind use standingParticipant.offClick method
    // In this example bind is removed after timeout
    setTimeout(() => {
        integration.standingParticipant.offClick(onClickCallback);
    }, 5000);
});

Example of binding event participants click (integration is available in components with lists of events):

widget.getIntegration('Interactivity', integration => {
    const onClickCallback = participant => {
        console.log(participant);
    };

    // Use integration eventParticipant.onClick method to bind your callback
    integration.eventParticipant.onClick(onClickCallback);

    // To remove click bind use eventParticipant.offClick method
    // In this example bind is removed after timeout
    setTimeout(() => {
        integration.eventParticipant.offClick(onClickCallback);
    }, 5000);
});

Tipster integration

BetTip integration allows you to attach odds to betting hints and filter them according to your criteria.

Basic integration

To initialize the integration, simply call setBetTipCreator method and pass callback in which you can attach options (like odds or on click behaviour) to given tip provided by us.

let refresh: (() => void) | undefined;
widget.getIntegration('Tipster', integration => {
    refresh = integration.setBetTipCreator((tip: Tip) => {
        // Null if you don't have odds for given market or
        // you don't want to display tip for this bet
        const odds: number | null = myFunctionToGetOdds(tip);

        // Active tips are styled differently - you can for example
        // set given tip as active if user has given bet in betslip
        const isActive: boolean = myFunctionToCheckIfGivenTipShouldBeActive(tip);

        // Define what happens when someone clicks on the tip
        const onClick: (() => void) | undefined  = (tip: Tip) => {
            myFunctionThatAddsGivenBetToBetSlip(tip);

            // If you want to see any changes in the widget, you need to call the
            // refresh function. In this example we assume
            // that adding a bet to the bet slip will make this tip active
            // in which case you will need to call refresh to update the
            // tip on the widget
            refresh();
        };

        return {
            odds,
            isActive,
            onClick,
        };
    });
});

// You can call refresh everytime you want to change any options attached to tips
refresh();

Properties available in Tip type:

type Tip = {
    tipId: string
    type: 'InPlay' | 'Prematch'
    marketId: number
    betName: string
    betLine: string | null
    text: string
}

Here are some assumptions that should help you understand the code and principles behind this integration:

Custom filters

By default:

Both of those behaviours can be changed. For starters you can change constraint that is responsible for displaying only tips with odds attached to them:

Example 1. Displaying tips regardless if it has odds

integration.setAdditionalConstraintsForDefaultTipSelector((betTip: BetTip): boolean => {
    return true;
});

Example 2. Displaying tip if odds are attached and higher than 1.5

integration.setAdditionalConstraintsForDefaultTipSelector((betTip: BetTip): boolean => {
    return betTip.odds !== null && betTip.odds > 1.5;
});

In previous examples we changed part of logic in default tips selector (function responsible for filtering tips). To change one tip per market constraint you have to replace tips selector with custom one:

Example 1. Displaying every available tip

integration.setTipSelector((betTips: BetTip[]): BetTip[] => {
    return betTips.filter(betTip => true);
});

Keep in mind that one market can have multiple tips with multiple lines so displaying everything may lead to filling widget with very similar tips.

Properties available in BetTip type:

type BetTip = {
    tipId: string
    type: 'InPlay' | 'Prematch'
    marketId: number
    betName: string
    betLine: string | null
    text: string
}

Bets integration

Various components (for example PrematchBoard in PrematchProWidget) support displaying bets that you can pass through Bets integration:

widget.getIntegration('Bets', integration => {
    integration.setMarkets([
        {
            bets: [
                {
                    betName: '1',
                    odds: 1.56,
                    isActive: false,
                    trend: 'neutral',
                    onClick: () => {
                        // ...
                    }
                }
            ]
        }
    ]);
});

Instead of passing array with markets you can also pass single market object. In case where you pass array then in components where we display single market first one will be displayed.

Properties available in Market type:

type Bet = {
    // Text that is displayed for this bet.
    betName: string

    // Bet odds
    odds: number

    // If given bet is active then its styled differently.
    // You can for example set given bet as active if it's in betslip.
    isActive: boolean

    // Arrow suggesting if odds for given bet went up or down.
    trend: 'up' | 'down' | 'neutral'

    // You can define what happens when someone clicks on
    // given bet. For example you can add it to betslip.
    onClick?: () => void
}

type Market = {
    bets: Bet[]
}

Keep in mind that in some scenarios, for example:

You have to call setMarkets again and pass new data.

LiveSwapper

If you ever find yourself in need of displaying different prematch and live widgets, we offer possibility to switch those widgets automatically for you. You can decide which widget to display before match starts and which widget to display shortly before or when match is live.

When embedding widget this solution loads either live or prematch widget. If prematch widget was loaded it will automatically switch to given live widget shortly before event starts.

This solution works only for widgets that include eventId in their input data.

Example of use

// Note: you can also use WidgetGroup instead of Widget
const prematchWidget = new window.STATSCOREWidgets.Widget(element, prematchConfigurationId, prematchInputData, prematchOptions);
const liveWidget = new window.STATSCOREWidgets.Widget(element, liveConfigurationId, liveInputData, liveOptions);

const liveSwapper = new window.STATSCOREWidgets.LiveSwapper(prematchWidget, liveWidget, {
    // Optional
    // If passed, prematch -> live swap won't occur if coverage you gave is not satisfied.
    // Structure of object is same as "satisfiesCoverage" option in Widget.
    satisfiesCoverage: {
        ftOnlyAcceptable: false,
        basicAcceptable: false,
        minStatsLevel: 'bronze'
    }
});

liveSwapper.on('error', e => {
    if (e.code === 1234) {
        console.log(e.message)
        ...
    }
});

LiveSwapper error event catches errors from both passed widgets and errors from LiveSwapper itself. For more details about error event refer to error event in widget.

Removing currently embedded widget

You can either remove currently embedded widget with destroy method as you would normally do or use LiveSwapper destroy method which determines which widget is currently embedded and removes it:

await liveSwapper.destroy();

Events

// LiveSwapper has its own events you can listen to.
// It has same events API as Widget objects (on, once, off methods).
liveSwapper.on('swapCoverageNotSatisfied', ({ isFtOnly, isBasic, statsLvl }) => {
    // ...
});

liveSwapper.on('embedding', ({
     // Widget instance that is being embedded
     widget,

     // Is current embed swap from prematch to live.
     // Returns true only if transition happens from already embeded prematch widget.
     isSwap
}) => {
    // ...
});

License information

The widgets provided by STATSCORE are copyrighted and may only be used by entering into the relevant license agreement, which can be found at https://www.statscore.com/service-license-agreement. STATSCORE sp. z o.o. is the only entity authorized to enter into licensing agreements, if you have been provided with a widget by any other entity please contact us immediately. Any unauthorized use of the widget constitutes copyright infringement and is therefore illegal.

STATSCORE is committed to protecting its interests as well as the interests of its customers using the widgets lawfully and will respond to any case of infringement.

STATSCORE SportWidgets

Copyright (C) STATSCORE sp. z o.o. 2019-2024

The livescore widgets posted on the website are the property of STATSCORE sp. z o.o., to which STATSCORE sp. z o.o. owns the full economic and moral copyrights. Use of the widgets in any way is subject to the terms of the Service License Agreement, the full text of the document is available at https://www.statscore.com/service-license-agreement.

It is prohibited to use or redistribute the widgets in any form if you have not previously entered into the Service License Agreement with STATSCORE sp. z o.o.. If you wish to do so, you should contact STATSCORE sp. z o.o. in order to obtain more information. Acceptance of the Service License Agreement terms and conditions alone, without STATSCORE sp. z o.o. knowledge, will not be considered a valid conclusion of the contract.

If the widgets have been provided or transferred to you in any form and you have not previously entered into an agreement with STATSCORE sp. z o.o. please contact us.

You can contact STATSCORE sp. z o.o. at the following e-mail address license@statscore.com.