SportLogic Virtuals API
Virtual racing data 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 /api/v1/game-typesto see available racing categories (e.g. Dashing Derby, Platinum Hounds). - List events - Call
GET /api/v1/eventsfiltered bygame_type_idandstatusto find upcoming or recent events. - Get event with runners and odds - Call
GET /api/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 /api/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. /health and /register are public.
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 | MISSING_API_KEY | No API key in the X-API-Key header |
| 401 | INVALID_API_KEY | The API key does not exist |
| 403 | INACTIVE_API_KEY | The API key has been deactivated |
Rate Limits & Pricing
Limits are counted per API key and enforced as a per-day quota that resets at midnight UTC.
| Plan | Price | Daily quota |
|---|---|---|
Free | $0 | 10 / day |
Starter | $59/mo | 15,000 / day |
Pro | $109/mo | 50,000 / day |
Business | $199/mo | 200,000 / day |
Upgrade at https://dashboard.sportlogic.io/virtuals/billing
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 - 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 virtual racing categories. 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
- categoryGame category: racing, player_sport, or numbers. Free/Starter keys receive only racing; Pro/Business also receive player_sport and numbers.
- 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"])
Account
Inspect usage for your API key and rotate it when needed.
Usage summary for the authenticated API key: current tier, daily quota, calls today/this month, and a per-endpoint breakdown.
Response fields
- api_keyKey name (the raw key is never returned here)
- tierCurrent tier: free, starter, pro, or business
- daily_limitRequests allowed per day for this tier
- calls_todayRequests made so far today (UTC)
- calls_this_monthRequests made in the current calendar month
- quota_remainingRequests left before the daily cap resets at midnight UTC
- avg_response_msAverage response time across recent calls
- daily_historyPer-day call counts for the last 7 days
- by_endpointPer-endpoint call counts and average response time
- last_used_atTimestamp of the most recent request
curl https://virtuals-api.sportlogic.io/api/v1/usage \
-H "X-API-Key: sv_YOUR_KEY"
Issues a new API key for your tenant (same name, tier, and daily limit) and immediately revokes the key used to make the request. The new raw key is shown only once.
Response fields
- api_keyThe new raw key (
sv_+ 40 chars), shown once - messageConfirmation that the old key was revoked
curl -X POST https://virtuals-api.sportlogic.io/api/v1/tenant/rotate-key \
-H "X-API-Key: sv_YOUR_KEY"
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",
"doc_url": "https://virtuals-api.sportlogic.io/docs#errors",
"request_id": "req_ab12cd34ef56"
}
}
Error codes
| HTTP | Code | Description |
|---|---|---|
| 401 | MISSING_API_KEY | No API key in the X-API-Key header |
| 401 | INVALID_API_KEY | API key does not exist |
| 403 | INACTIVE_API_KEY | API key has been deactivated |
| 403 | TIER_RESTRICTED | Category not available on your tier |
| 404 | RESOURCE_NOT_FOUND | Requested resource doesn't exist |
| 422 | VALIDATION_ERROR | Invalid query or body parameters |
| 429 | RATE_LIMIT_EXCEEDED | Daily quota exceeded |
| 400 | EVENT_NOT_FINISHED | Results requested for an event that hasn't completed |