Virtual Racing API

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.

Draw 1 Draw 5 Draw 9
Base URL https://virtuals-api.sportlogic.io/api/v1
Auth X-API-Key: sv_YOUR_KEY
Format REST + JSON
Get API Key →

Getting Started

Typical workflow for consuming virtual racing data.

Workflow

  1. List game types - Call GET /api/v1/game-types to see available racing categories (e.g. Dashing Derby, Platinum Hounds).
  2. List events - Call GET /api/v1/events filtered by game_type_id and status to find upcoming or recent events.
  3. 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.
  4. Get results and settlement - After the event finishes, call GET /api/v1/events/{id}/results for 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"
Tip: All data endpoints require the X-API-Key header. /health and /register are public.

Authentication

Every request requires a valid API key in the X-API-Key header.

Header

HeaderValueDescription
X-API-Keysv_your_key_hereRequired 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

HTTPCodeMeaning
401MISSING_API_KEYNo API key in the X-API-Key header
401INVALID_API_KEYThe API key does not exist
403INACTIVE_API_KEYThe 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.

PlanPriceDaily quota
Free$010 / day
Starter$59/mo15,000 / day
Pro$109/mo50,000 / day
Business$199/mo200,000 / day

Upgrade at https://dashboard.sportlogic.io/virtuals/billing

Response headers

HeaderDescription
X-RateLimit-LimitYour daily request limit
X-RateLimit-RemainingRequests 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.

Game Types

8 virtual racing categories. Each has a fixed field size and event cycle.

GET /api/v1/game-types

List all active virtual racing game types.

Available game types

Dashing DerbyDashingDerby · 14 runners · 10 min
Platinum HoundsPlatinumHounds · 8 runners · 10 min
Harness RacingHarnessRacing · 10 runners · 9 min
Motor RacingMotorRacing · 14 runners · 9 min
Horse Racing RouletteHorseRacingRouletteV2 · 8 runners · 9 min
Steeple ChaseSteepleChase · 14 runners · 9 min
Speed SkatingSpeedSkating · 6 runners · 9 min
Single Seater Motor RacingSingleSeaterMotorRacing · 14 runners · 9 min
Virtual racing engine. Fixed field sizes, a consistent odds model, and deterministic race outcomes. We expose the data - you render it however you want.

Response 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"]

Response
{ "success": true, "data": [ { "id": 1, "code": "DashingDerby", "name": "Dashing Derby", "category": "racing", "is_active": true }, { "id": 2, "code": "PlatinumHounds", "name": "Platinum Hounds", "category": "racing", "is_active": true }, { "id": 3, "code": "HarnessRacing", "name": "Harness Racing", "category": "racing", "is_active": true } ] }
GET /api/v1/game-types/{id}/info

Detailed info for a single game type, including available markets.

ParameterTypeRequiredDescription
idintegerrequiredGame type ID (path parameter)
curl https://virtuals-api.sportlogic.io/api/v1/game-types/1/info \
  -H "X-API-Key: sv_YOUR_KEY"

Response
{ "success": true, "data": { "id": 1, "code": "DashingDerby", "name": "Dashing Derby", "category": "racing", "is_active": true } }

Events

Virtual racing events with runners, odds, and results.

GET /api/v1/events

List events with optional filters. Returns cursor-paginated results.

ParameterTypeRequiredDescription
game_type_idintegeroptionalFilter by game type
statusstringoptionalscheduled open closed started finished
per_pageintegeroptional1–100, default 25
cursorstringoptionalPagination 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"]

Response
{ "success": true, "data": [ { "id": 120486, "game_type_id": 1, "event_code": "1031", "venue": "MILNERTON 1000", "scheduled_at": "2026-04-07T04:46:00.000000Z", "status": "finished", "game_type": { "id": 1, "code": "DashingDerby", "name": "Dashing Derby", "category": "racing" } } ], "pagination": { "next_cursor": "eyJzY2hlZ...", "prev_cursor": null, "has_more": true, "per_page": 25 } }
GET /api/v1/events/{id}

Single event with full runner list. Each runner includes draw number, name, color, win/place odds, recent form, and finish position (if finished).

ParameterTypeRequiredDescription
idintegerrequiredEvent 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"]

Response
{ "success": true, "data": { "event": { "id": 120486, "game_type_id": 1, "event_code": "1031", "venue": "MILNERTON 1000", "scheduled_at": "2026-04-07T04:46:00.000000Z", "status": "finished" }, "runners": [ { "draw_number": 1, "name": "Chilly Billy", "color": "#e53e3e", "odds_win": "12.25", "odds_place": "3.02", "form": [7, 2, 3, 2, 3], "finish_position": 3 }, { "draw_number": 2, "name": "Dawn Dragon", "color": "#3182ce", "odds_win": "17.16", "odds_place": "3.45", "form": [1, 0, 0, 0, 2], "finish_position": 2 }, { "draw_number": 3, "name": "Cincinnati Kid", "color": "#38a169", "odds_win": "4.34", "odds_place": "1.98", "form": [1, 1, 4, 1, 1], "finish_position": 1 } ] } }
GET /api/v1/events/{id}/results

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.

ParameterTypeRequiredDescription
idintegerrequiredEvent ID (path parameter)
draw_numberintegeroptionalFilter positions to a specific runner by draw number
finish_positionintegeroptionalFilter 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")
Returns EVENT_NOT_FINISHED if the event hasn't completed yet.

curl https://virtuals-api.sportlogic.io/api/v1/events/120486/results \
  -H "X-API-Key: sv_YOUR_KEY"


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"])

Response
{ "success": true, "data": { "event_id": 120486, "status": "finished", "positions": [ { "finish_position": 1, "draw_number": 3, "name": "Cincinnati Kid", "color": "#38a169", "odds_win": "4.34", "odds_place": "1.98" }, { "finish_position": 2, "draw_number": 2, "name": "Dawn Dragon", "color": "#3182ce", "odds_win": "17.16", "odds_place": "3.45" }, { "finish_position": 3, "draw_number": 1, "name": "Chilly Billy", "color": "#e53e3e", "odds_win": "12.25", "odds_place": "3.02" } ], "settlement": { "win": { "draw_number": 3, "name": "Cincinnati Kid", "odds_win": "4.34" }, "place": [ { "finish_position": 1, "draw_number": 3, "name": "Cincinnati Kid", "odds_place": "1.98" }, { "finish_position": 2, "draw_number": 2, "name": "Dawn Dragon", "odds_place": "3.45" }, { "finish_position": 3, "draw_number": 1, "name": "Chilly Billy", "odds_place": "3.02" } ], "forecast": { "first": 3, "second": 2 }, "tricast": { "first": 3, "second": 2, "third": 1 }, "place_terms": "1-3 of 9 runners" } } }

Account

Inspect usage for your API key and rotate it when needed.

GET /api/v1/usage

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"

Response
{ "success": true, "data": { "api_key": "Acme Corp Key", "tier": "pro", "daily_limit": 50000, "calls_today": 1284, "calls_this_month": 39120, "quota_remaining": 48716, "avg_response_ms": 42, "daily_history": [ { "date": "2026-07-14", "calls": 2210 } ], "by_endpoint": [ { "endpoint": "GET /api/v1/events", "calls_today": 980, "calls_this_month": 28110, "avg_response_ms": 38 } ], "last_used_at": "2026-07-15T09:41:00.000000Z" } }
POST /api/v1/tenant/rotate-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.

Store it immediately. The presenting key stops working as soon as the new one is issued, and the new key cannot be retrieved again.

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"

Response
{ "success": true, "data": { "api_key": "sv_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0", "message": "Store this key safely -- it will not be shown again. Your old key has been revoked." } }

Markets

Betting market types available for virtual racing events.

GET /api/v1/markets

List all market types. Markets vary by game type.

Available markets

CodeNameDescription
WINWinRunner to finish first
PLACEPlaceRunner to finish in place positions
SHOWShowRunner to finish in top 3
EXACTAExactaFirst and second in exact order
QUINELLAQuinellaFirst and second in any order
TRIFECTATrifectaFirst, second, and third in exact order
HEAD_TO_HEADHead to HeadWhich of two runners finishes ahead
ODD_EVENOdd/EvenWinner's draw number is odd or even
HIGH_LOWHigh/LowWinner'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"

Response
{ "success": true, "data": [ { "id": 1, "game_type_id": 1, "code": "WIN", "name": "Win", "tab": "MAIN", "sort_order": 1 }, { "id": 2, "game_type_id": 1, "code": "PLACE", "name": "Place", "tab": "MAIN", "sort_order": 2 }, { "id": 4, "game_type_id": 1, "code": "EXACTA", "name": "Exacta", "tab": "ALT", "sort_order": 1 } ], "pagination": { "next_cursor": "eyJpZCI6...", "prev_cursor": null, "has_more": true, "per_page": 25 } }

Runner Colors

Each draw position has a fixed color. The color field on runners uses these hex values.

DrawColorHex
1Red#e53e3e
2Blue#3182ce
3Green#38a169
4Yellow#d69e2e
5Orange#dd6b20
6Purple#805ad5
7White#e2e8f0
8Pink#ed64a6
9Brown#8b6914
10Grey#718096
11Teal#319795
12Maroon#9b2c2c
Not all game types use all 12 colors. For example, Speed Skating has 6 runners and only uses draw positions 1–6.

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

HTTPCodeDescription
401MISSING_API_KEYNo API key in the X-API-Key header
401INVALID_API_KEYAPI key does not exist
403INACTIVE_API_KEYAPI key has been deactivated
403TIER_RESTRICTEDCategory not available on your tier
404RESOURCE_NOT_FOUNDRequested resource doesn't exist
422VALIDATION_ERRORInvalid query or body parameters
429RATE_LIMIT_EXCEEDEDDaily quota exceeded
400EVENT_NOT_FINISHEDResults requested for an event that hasn't completed