SportLogic Virtuals API
The same Kiron virtual racing data that powers retail betting terminals, now as a REST API. Horses, greyhounds, motor racing, harness, speed skating. 8 game types, events every ~10 minutes, 24/7. Schedules, runners with odds, results with settlement. You build the front-end.
Getting Started
Typical workflow for consuming virtual racing data.
Workflow
- List game types - Call
GET /v1/game-typesto see available racing categories (e.g. Dashing Derby, Platinum Hounds). - List events - Call
GET /v1/eventsfiltered bygame_type_idandstatusto find upcoming or recent events. - Get event with runners and odds - Call
GET /v1/events/{id}to get the runner list with win/place odds, form, and draw colors. - Get results and settlement - After the event finishes, call
GET /v1/events/{id}/resultsfor finishing positions and settlement data (win, place, forecast, tricast).
First request
Replace sv_YOUR_KEY with the API key from your dashboard.
curl -G https://virtuals-api.sportlogic.io/api/v1/events \ -H "X-API-Key: sv_YOUR_KEY" \ --data-urlencode "status=scheduled" \ --data-urlencode "per_page=5"
X-API-Key header.
Authentication
Every request requires a valid API key in the X-API-Key header.
Header
| Header | Value | Description |
|---|---|---|
X-API-Key | sv_your_key_here | Required on all endpoints. Keys are prefixed with sv_. |
Register
Create a new account and receive an API key via POST /api/v1/register.
Auth error codes
| HTTP | Code | Meaning |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 403 | FORBIDDEN | Key suspended or insufficient permissions |
Rate Limits & Pricing
Limits are counted per API key. Rate limits are enforced per minute.
| Plan | Price | Events/day | Rate Limit |
|---|---|---|---|
Free | $0 | 500 | 10 req/min |
Starter | $69/mo | 15,000 | 120 req/min |
Pro | $129/mo | 50,000 | 300 req/min |
Business | $249/mo | 200,000 | 600 req/min |
Response headers
| Header | Description |
|---|---|
X-RateLimit-Limit | Your daily request limit |
X-RateLimit-Remaining | Requests remaining today |
On limit exceeded, the API returns HTTP 429 with error code RATE_LIMIT_EXCEEDED.
Business plan includes
- - Silk asset pack - 40 jockey silks + 8 game type icons (PNG/SVG)
- - VisionViewer integration - Kiron's 3D race renderer. Racing, Keno, LuckyLoot, Spin and Win.
- - 1-to-1 integration support
- - Custom rate limits on request
Business assets and VisionViewer docs are shared directly after subscription. Contact support@sportlogic.io to get started.
Pagination
All list endpoints use cursor-based pagination.
Parameters
| Param | Type | Description |
|---|---|---|
| cursor | string | Opaque cursor from previous response's pagination.next_cursor. Omit for first page. |
| per_page | integer | Items per page. Default 25, max 100. |
Response shape
{
"success": true,
"data": [ ... ],
"pagination": {
"next_cursor": "eyJzY2hlZ...",
"prev_cursor": null,
"has_more": true,
"per_page": 25
}
}
Game Types
8 Kiron virtual racing categories. Same game types running on retail betting terminals worldwide. Each has a fixed field size and event cycle.
List all active virtual racing game types.
Available game types
DashingDerbyPlatinumHoundsHarnessRacingMotorRacingHorseRacingRouletteV2SteepleChaseSpeedSkatingSingleSeaterMotorRacingResponse fields
- idGame type ID - use to filter events
- codeMachine-readable code (e.g. "DashingDerby")
- nameDisplay name
- categoryAlways "racing"
- runner_countNumber of runners per event
- cycle_secondsSeconds between events
- is_activeWhether this game type is currently running
curl https://virtuals-api.sportlogic.io/api/v1/game-types \
-H "X-API-Key: sv_YOUR_KEY"
const res = await fetch( 'https://virtuals-api.sportlogic.io/api/v1/game-types', { headers: { 'X-API-Key': 'sv_YOUR_KEY' } } ); const { data } = await res.json();
import requests r = requests.get( "https://virtuals-api.sportlogic.io/api/v1/game-types", headers={"X-API-Key": "sv_YOUR_KEY"} ) game_types = r.json()["data"]
Detailed info for a single game type, including available markets.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | integer | required | Game type ID (path parameter) |
curl https://virtuals-api.sportlogic.io/api/v1/game-types/1/info \ -H "X-API-Key: sv_YOUR_KEY"
Events
Virtual racing events with runners, odds, and results.
List events with optional filters. Returns cursor-paginated results.
| Parameter | Type | Required | Description |
|---|---|---|---|
| game_type_id | integer | optional | Filter by game type |
| status | string | optional | scheduled open closed started finished |
| per_page | integer | optional | 1–100, default 25 |
| cursor | string | optional | Pagination cursor from previous response |
Response fields
- idEvent ID
- game_type_idReferences a game type
- event_codeShort event code (e.g. "1031")
- venueVirtual venue name (e.g. "MILNERTON 1000")
- scheduled_atISO 8601 UTC scheduled start time
- statusscheduled · open · closed · started · finished
- game_typeEmbedded game type object
curl -G https://virtuals-api.sportlogic.io/api/v1/events \ -H "X-API-Key: sv_YOUR_KEY" \ --data-urlencode "game_type_id=1" \ --data-urlencode "status=finished" \ --data-urlencode "per_page=25"
const res = await fetch( 'https://virtuals-api.sportlogic.io/api/v1/events?game_type_id=1&status=finished', { headers: { 'X-API-Key': 'sv_YOUR_KEY' } } ); const { data, pagination } = await res.json();
import requests r = requests.get( "https://virtuals-api.sportlogic.io/api/v1/events", headers={"X-API-Key": "sv_YOUR_KEY"}, params={"game_type_id": "1", "status": "finished"} ) events = r.json()["data"]
Single event with full runner list. Each runner includes draw number, name, color, win/place odds, recent form, and finish position (if finished).
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | integer | required | Event ID (path parameter) |
Runner fields
- draw_numberPosition in the draw (1-14 depending on game type)
- nameRunner name
- colorHex color for the draw position (e.g. "#e53e3e")
- odds_winDecimal win odds as string
- odds_placeDecimal place odds as string
- formArray of recent finishing positions (most recent first)
- finish_positionFinal position (null if event not finished)
curl https://virtuals-api.sportlogic.io/api/v1/events/120486 \ -H "X-API-Key: sv_YOUR_KEY"
const res = await fetch( 'https://virtuals-api.sportlogic.io/api/v1/events/120486', { headers: { 'X-API-Key': 'sv_YOUR_KEY' } } ); const { data } = await res.json(); console.log(data.runners);
import requests r = requests.get( "https://virtuals-api.sportlogic.io/api/v1/events/120486", headers={"X-API-Key": "sv_YOUR_KEY"} ) runners = r.json()["data"]["runners"]
Finishing positions and settlement data for a completed event. Filter by draw_number or finish_position to look up a specific runner. Settlement data (win, place, forecast, tricast) is always returned in full regardless of filters - only the positions array is filtered.
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | integer | required | Event ID (path parameter) |
| draw_number | integer | optional | Filter positions to a specific runner by draw number |
| finish_position | integer | optional | Filter positions to a specific finishing position |
Settlement fields
- positions[]Ordered finishing positions with draw number, name, color, and odds
- settlement.winWinner details (draw_number, name, odds_win)
- settlement.placeArray of placed runners with odds_place
- settlement.forecastFirst and second draw numbers
- settlement.tricastFirst, second, and third draw numbers
- settlement.place_termsPlace terms description (e.g. "1-3 of 9 runners")
EVENT_NOT_FINISHED if the event hasn't completed yet.# All results for an event curl https://virtuals-api.sportlogic.io/api/v1/events/120486/results \ -H "X-API-Key: sv_YOUR_KEY" # Filtered: only draw number 3 curl "https://virtuals-api.sportlogic.io/api/v1/events/120486/results?draw_number=3" \ -H "X-API-Key: sv_YOUR_KEY"
const res = await fetch( 'https://virtuals-api.sportlogic.io/api/v1/events/120486/results', { headers: { 'X-API-Key': 'sv_YOUR_KEY' } } ); const { data } = await res.json(); console.log(data.settlement.win);
import requests r = requests.get( "https://virtuals-api.sportlogic.io/api/v1/events/120486/results", headers={"X-API-Key": "sv_YOUR_KEY"} ) results = r.json()["data"] print(results["settlement"]["win"])
Markets
Betting market types available for virtual racing events.
List all market types. Markets vary by game type.
Available markets
| Code | Name | Description |
|---|---|---|
WIN | Win | Runner to finish first |
PLACE | Place | Runner to finish in place positions |
SHOW | Show | Runner to finish in top 3 |
EXACTA | Exacta | First and second in exact order |
QUINELLA | Quinella | First and second in any order |
TRIFECTA | Trifecta | First, second, and third in exact order |
HEAD_TO_HEAD | Head to Head | Which of two runners finishes ahead |
ODD_EVEN | Odd/Even | Winner's draw number is odd or even |
HIGH_LOW | High/Low | Winner's draw number is high or low |
Response fields
- idMarket ID
- game_type_idWhich game type this market belongs to
- codeMachine-readable market code (e.g. "WIN")
- nameDisplay name
- tabUI grouping tab (MAIN or ALT)
- sort_orderDisplay order within the tab
curl https://virtuals-api.sportlogic.io/api/v1/markets \
-H "X-API-Key: sv_YOUR_KEY"
Runner Colors
Each draw position has a fixed color. The color field on runners uses these hex values.
| Draw | Color | Hex |
|---|---|---|
| 1 | Red | #e53e3e |
| 2 | Blue | #3182ce |
| 3 | Green | #38a169 |
| 4 | Yellow | #d69e2e |
| 5 | Orange | #dd6b20 |
| 6 | Purple | #805ad5 |
| 7 | White | #e2e8f0 |
| 8 | Pink | #ed64a6 |
| 9 | Brown | #8b6914 |
| 10 | Grey | #718096 |
| 11 | Teal | #319795 |
| 12 | Maroon | #9b2c2c |
Errors
All error responses follow a consistent envelope with machine-readable codes.
Error envelope
{
"success": false,
"error": {
"code": "MACHINE_READABLE_CODE",
"message": "Human-readable description"
}
}
Error codes
| HTTP | Code | Description |
|---|---|---|
| 401 | UNAUTHORIZED | Missing or invalid API key |
| 403 | FORBIDDEN | Key suspended or insufficient permissions |
| 404 | NOT_FOUND | Requested resource doesn't exist |
| 400 | VALIDATION_ERROR | Invalid query parameters |
| 429 | RATE_LIMIT_EXCEEDED | Daily or per-minute limit reached |
| 400 | EVENT_NOT_FINISHED | Results requested for an event that hasn't completed |