Catalog API response principles
The exact content of the response depends on the content type. The sections that follow describe what Stories, Moments, and Videos share. The per-type pages describe the differences.
Pagination response
All list endpoints return the same envelope:
{
"totalItems": 37,
"result": [ { } ]
}totalItems: total items matching your query, not the number on the current page. Exception: whenPageSize=0,totalItemsis0result: the content items for the current page
Properties common to all content types
| Property | Notes |
|---|---|
id | Content identifier |
type | Story, Moment, or Video |
title | Title |
description | Description |
version | Version number (integer) |
aspectRatios | Available aspect ratios |
status | Draft or Active |
createTime | ISO 8601 creation timestamp |
updateTime | ISO 8601 last-update timestamp |
thumbnails | Array of { url, aspectRatio } |
geoTargeting | Where the content may be shown. See Geo-targeting |
metadata | Related sports entities. See Metadata |
executionTimeType | Workflow context. See executionTimeType |
labels | Array of { title, identifier } |
extraInfo | Open dictionary of display metadata. See extraInfo |
defaultLanguage | Language of the top-level text fields |
translatedTitles | Localized titles by language code |
competitionId | Competition identifier |
publishMethod | How the content was published. May be null |
creationSettings | { creationTimePeriod, ruleName, storyType } |
game | Game details. Populated on Stories only, when the story is linked to a game. Always null on Moments and Videos |
rating | Content rating (number). May be null |
Properties by content type
| Property | Stories | Moments | Videos |
|---|---|---|---|
htmlUrl | Yes | Yes | No |
isLive | Yes | No | No |
pages | Yes | No | No |
titleImageUrl | Yes | No | No |
subtitle | No | Yes | Yes |
duration | No | Yes | Yes |
videoRenditions | pages[] only | Yes | Yes |
translatedDescriptions | No | Yes | Yes |
translatedSubtitles | No | Yes | Yes |
Geo-targeting
"geoTargeting": { "includedGeos": ["gb"], "excludedGeos": null }includedGeos: show only in these countries.nullmeans no restrictionexcludedGeos: never show in these countries
Country codes are lowercase ISO 3166 two-letter codes.
Honoring geoTargeting is a requirement, not a suggestion. WSC Sports also enforces it on delivery, so geo-restricted content will not play outside its target countries. If you ignore geoTargeting, you show viewers items that silently fail to play.
The following approaches are supported:
- Pass
Geo={viewer country}on your request and let the feed return only content that can be shown there. PassingGeois the simplest approach and the recommended default. - Read
geoTargetingper item and apply it in your own rendering. Use this if you cache one pull and serve several countries from it.
If you render video yourself from videoRenditions rather than embedding htmlUrl, enforcing geoTargeting is entirely your responsibility.
Metadata
Content can be associated with a competition, competitions[], game, team, player, round, and season. Each is an object:
| Field | Type |
|---|---|
provider | Always null on metadata.* entities. A provider name appears only on game.participants[].ids.provider |
providerId | string. The provider's ID for this entity |
wscId | integer. The WSC ID |
Use wscId with Entities.*.WscId filters, and providerId with Entities.*.ServiceProviderId plus Entities.IdType=ServiceProviderId.
extraInfo
extraInfo carries display metadata: player name, scores, game clock, the action type.
"extraInfo": {
"homeTeam": "Home team",
"awayTeam": "Away team",
"propertyId": "c81e728d9d4c2f636f067f89cc14862c",
"propertyName": "League",
"isCurrentlyLive": "False",
"activeLabels": "[\"wsc-ingame\"]",
"gameId": "1522600076",
"roundId": "6",
"seasonId": "2026"
}extraInfo is an open dictionary, not a fixed schema. Keys vary by sport and content type. Treat a missing key as normal rather than as an error, and check for a key before reading it.
Keys generally present on Stories: homeTeam, awayTeam, propertyId, propertyName, gameId, roundId, seasonId, activeLabels, isCurrentlyLive. teamId and playerId are not always present.
activeLabelsis a string, not an array. It arrives as text containing a bracketed list, as in the sample above.isCurrentlyLiveis a string (TrueorFalse), not a boolean.- When
extraInfo.ratingis present, it is a string, while the top-levelratingis a number.
videoRenditions
Moments and Videos can carry top-level videoRenditions. Stories do not. When direct video access is enabled, Story pages[] can carry pages[].videoRenditions in the same shape.
| Field | Type |
|---|---|
url | string |
fileType | Mp4, HLS, or DASH |
aspectRatio | aspect ratio enum (can be Unknown) |
bitRate | number or null |
resolution | { level, height, width }. level is LowRes, MidRes, or HighRes. height and width are number or null |
Use this when you need the video inside your own player rather than embedding the hosted page. videoRenditions is returned when direct video access is enabled for your feed. Ask your account manager to confirm.
htmlUrl
Stories and Moments can carry htmlUrl, a WSC Sports-hosted page for that single item. Videos do not.
htmlUrl is returned when HTML pages are enabled for your feed. Content published before they were enabled does not have one. If you expect htmlUrl and it is absent, ask your account manager to confirm HTML is enabled for your feed.
htmlUrl is unique to your feed. The same content returns a different URL to each publisher, because the URL carries your feed's own tracking parameter. Compare and cache by id, never by URL. If your key is reissued, re-fetch rather than reusing stored URLs.
executionTimeType
Workflow context for the content: None, PreGame, InGame, PostGame.
In practice only Stories carry a value other than None, and only where the publishing workflow set one. Moments and Videos are always None. Don't build logic on this field for those types.
Localization
translatedTitles, translatedDescriptions, and translatedSubtitles are objects keyed by language code. defaultLanguage tells you which language the top-level fields are in. An empty object means no translations exist for that field.
"translatedTitles": {
"en": "Match Highlights",
"es": "Resumen del partido"
}Aspect ratios
- Square
- Horizontal
- Vertical
- FourToFive
- FiveToFour
- ThreeToFour
- FourToThree
- TwoToOne
- OneToTwo
- SevenToEight
- EightToSeven
- TwoToThree
- ThreeToOne
- ThreeToTwo
- OneToThree
- ThreeToEight
- FiveToOne
- ThirtyEightToNine
- Unknown
Updated about 1 month ago
