Client Integration Guide

Odds Distribution API

This guide is for third-party developers integrating the production HTTP API. All endpoints are JSON GET requests. No request body is required.

This deployment's base URL is:

Base URL https://oddsfeed.starluck.cloud/api/v1
Access requirement

No API key or Authorization header is used. Access is controlled by IP allowlisting — your server's public IP address must be allowlisted before match-data endpoints will respond.

Access

  • Call the API over HTTPS in production.
  • No API key and no Authorization header are required.
  • Access is controlled by IP allowlisting.
  • Your server's public IP address must be allowlisted before match-data access is enabled.
  • If the calling IP is not allowed, match endpoints return HTTP 403.

GET /api/v1/health can be used as a connectivity check and does not require an allowlisted IP. Match endpoints do.

Before integration, send the public IP address(es) of the servers that will call the API.

Supported sports

Sport slugs are lowercase and must match exactly.

SportURL slug
Cricketcricket
Footballfootball
Tennistennis
Horse Racinghorse-racing
Greyhound Racinggreyhound-racing

Unsupported slugs (including mixed case such as Cricket) return 400.

Endpoints

MethodURLPurpose
GET/api/v1/healthService health
GET/api/v1/{sport}/matchesAvailable matches for a sport (no full odds)
GET/api/v1/{sport}/matches/{eventId}One match with its current markets/odds

{sport} is one of the slugs above. {eventId} is the match's gmid — a positive integer from the match-list response.

There are no query parameters.

Health

GET /api/v1/health No IP allowlist required Use this to confirm the API is up and able to serve data.

Success — HTTP 200

{"status":"ok","redis":"ok"}

Degraded — HTTP 503

{"status":"degraded","redis":"unreachable"}

status is "ok" only when the backend data store is reachable.

Example request
curl -sS "https://oddsfeed.starluck.cloud/api/v1/health"

Match list

GET /api/v1/{sport}/matches Returns the available matches/events for the selected sport.

It returns match/event data only. It does not return the full markets/odds list. Use the single-match endpoint for odds.

The response is a JSON array. Each object is one event. Use gmid as the event ID for the single-match URL.

Example request
curl -sS "https://oddsfeed.starluck.cloud/api/v1/cricket/matches"

HTTP 200

[
  {
    "gmid": 1789048569,
    "ename": "India v Australia",
    "etid": 4,
    "status": "OPEN"
  }
]

gmid is the event ID. Use it in /matches/{eventId}.

Typical fields also include ename (event name), etid (sport identifier), and status (for example OPEN). Event objects may include additional metadata such as competition name or in-play flags. Ignore unused fields.

An empty array [] means no matches are currently published for that sport.

Errors

HTTPerrorWhen
400invalid_sport{sport} is not a supported slug
403ip_not_allowedCalling IP is not allowlisted
429rate_limitedRate limit exceeded
502malformed_upstream_dataUpstream catalog could not be read
503catalog_unavailableMatch data is not available yet
503redis_unavailableBackend data store unavailable
504redis_timeoutBackend data store timed out

Single match + odds

GET /api/v1/{sport}/matches/{eventId} Returns one match and its current available markets/odds.

{eventId} must be a positive integer (the event's gmid). The event must exist in that sport's match list.

The top-level response is always:

{
  "sport": "...",
  "event_id": "...",
  "event": {},
  "odds": []
}
FieldMeaning
sportSport slug from the request
event_idNumeric event ID (same value as event.gmid)
eventThe match object (same catalog fields as the list)
oddsArray of currently available markets

odds is the complete market list currently held for that event. It can be an empty array when the event exists but no markets are published yet.

Example request
curl -sS "https://oddsfeed.starluck.cloud/api/v1/cricket/matches/1789048569"

HTTP 200

{
  "sport": "cricket",
  "event_id": 1789048569,
  "event": {
    "gmid": 1789048569,
    "ename": "India v Australia",
    "etid": 4,
    "status": "OPEN"
  },
  "odds": [
    {
      "marketId": "1.234",
      "marketName": "Match Odds",
      "gtype": "match",
      "status": "OPEN",
      "runners": [
        {
          "selectionId": "47999",
          "runnerName": "India",
          "status": "ACTIVE",
          "ex": {
            "availableToBack": [{ "price": 1.75, "size": 50.0 }],
            "availableToLay": [{ "price": 1.76, "size": 40.0 }]
          }
        }
      ]
    },
    {
      "marketId": "8.111",
      "marketName": "Bookmaker",
      "gtype": "match1",
      "status": "OPEN",
      "runners": [
        {
          "selectionId": 48000,
          "runnerName": "India",
          "status": "ACTIVE",
          "ex": {
            "availableToBack": [{ "price": 1.97, "size": 100.0 }],
            "availableToLay": [{ "price": 2.02, "size": 80.0 }]
          }
        }
      ]
    }
  ]
}

Errors

HTTPerrorWhen
400invalid_sport{sport} is not a supported slug
400invalid_event_id{eventId} is not a positive integer
403ip_not_allowedCalling IP is not allowlisted
404event_not_foundEvent is not in that sport's match list
404odds_not_foundOdds are not available for this match
429rate_limitedRate limit exceeded
502malformed_upstream_dataUpstream odds could not be read
503catalog_unavailableMatch data is not available yet
503redis_unavailableBackend data store unavailable
504redis_timeoutBackend data store timed out

Odds structure

Identify each market by gtype and marketName. Markets currently returned include:

gtypeTypical marketNameShape
matchMatch Odds / Exchangerunners with back/lay
match1Bookmakerrunners with back/lay
fancy / fancy1Fancy / sessionsection ladder

Other gtype values may appear. Parse unknown markets defensively.

Match Odds and Bookmaker (runners)

  • marketNameMarket display name
  • marketIdMarket identifier
  • gtypeMarket type
  • statusMarket status
  • runners[]Selections in the market
  • runners[].selectionIdSelection identifier
  • runners[].runnerNameSelection name
  • runners[].statusSelection status
  • runners[].ex.availableToBackBack prices (price, size)
  • runners[].ex.availableToLayLay prices (price, size)

selectionId may be a string or a number. Bookmaker (match1) prices may also include price1.

Fancy (section)

Fancy markets use section instead of runners. Each section entry has a name (nat), a status (gstatus), and an odds ladder.

Fields not exposed

Certain provider-only fields are omitted from the public API.

Event objects do not include

  • betfairEventId
  • feed_source

Markets do not include

  • tv
  • tvChannel
  • odd_type

Do not depend on these fields. Additional public fields may be added later; ignore unused keys.

Refresh and usage

  • Each response contains the odds currently available at request time.
  • The API does not wait for a new upstream update before responding.
  • Calling the single-match endpoint keeps that match prioritized for refresh while you continue requesting it.
  • Repeated requests extend that active period.
  • Continue polling while the match is displayed or otherwise in use.
  • Stop polling when the match is no longer needed.
Important response behavior

The API does not guarantee real-time odds or a fixed freshness window.

Errors

Every error body has the same shape:

{
  "error": "event_not_found",
  "message": "Match not found"
}
HTTPerrorMeaningClient action
400invalid_sportUnsupported sport slugUse a supported slug
400invalid_event_idEvent ID is not a positive integerUse gmid from the match list
403ip_not_allowedCalling IP is not allowlistedConfirm the server public IP with the operator
404not_foundUnknown URLCheck the path
404event_not_foundMatch is not in that sport's listRefresh the match list
404odds_not_foundOdds are not available for this matchRetry later or skip the event
429rate_limitedRate limit exceededWait / back off; honour Retry-After if present
500internal_errorUnexpected server errorRetry later
502malformed_upstream_dataUpstream data could not be readRetry later
503catalog_unavailableMatch data is not available yetRetry shortly
503redis_unavailableBackend data store unavailableRetry later
503security_config_unavailableService is temporarily unavailableRetry later
504redis_timeoutBackend data store timed outRetry later

invalid_sport message:

Unsupported sport. Supported sports: cricket, football, tennis, horse-racing, greyhound-racing

Rate limiting

Requests may be rate limited.

  • HTTP 429 with "error": "rate_limited" means the current limit has been exceeded.
  • Wait and back off before retrying.
  • If a Retry-After header is present, wait that many seconds.

The numeric limit is set by the operator and is not a fixed public contract.

IP allowlist

Before integration, provide the public IP address(es) from which your systems will call the API.

If the IP is not allowed:

HTTP/1.1 403 Forbidden
{
  "error": "ip_not_allowed",
  "message": "Client address is not allowed"
}

Use a stable egress IP. Calls from an unlisted laptop, NAT, or proxy will be rejected.

Integration flow

  1. Call the sport match list.
  2. Read each event's gmid.
  3. Call the single-match endpoint with that ID.
  4. Parse event and odds.
  5. Poll the single-match endpoint while the match is in use.
  6. Stop polling when the match is finished or no longer needed.
# 1. List cricket matches
curl -sS "https://oddsfeed.starluck.cloud/api/v1/cricket/matches"

# 2–3. Fetch one match and its odds (replace with a gmid from step 1)
curl -sS "https://oddsfeed.starluck.cloud/api/v1/cricket/matches/1789048569"

JavaScript example

const response = await fetch(
  "https://oddsfeed.starluck.cloud/api/v1/cricket/matches/1789048569"
);

const data = await response.json();

On success, data.event_id is the event ID, data.event is the match, and data.odds is the market list.

Client implementation notes

  • Reuse HTTP connections where possible.
  • Handle 403 as an access problem (IP not allowlisted).
  • Handle 429 with backoff; honour Retry-After.
  • Handle 503 / 504 as temporary backend failures.
  • Handle 404 as an unavailable event, missing odds, or unknown route.
  • Do not assume hidden/internal fields exist.
  • Allow additional fields for future compatibility.

FAQ

Which sports are supported?

cricket, football, tennis, horse-racing, greyhound-racing.

How do I get an event ID?

Call GET /api/v1/{sport}/matches and use each event's gmid.

Does the match-list endpoint return odds?

No. It returns match/event data only. Use the single-match endpoint for markets and odds.

Does the single-match endpoint return all available markets?

Yes. odds is the complete list of markets currently available for that event. The array may be empty.

Do I need an API key?

No. Access is by IP allowlist.

What IP needs to be allowlisted?

The public IP of the server that will send the HTTP requests.

What does 403 mean?

The calling IP is not allowlisted (ip_not_allowed).

What does 429 mean?

The rate limit has been exceeded (rate_limited). Back off and retry.

What does 404 mean?

The event is not in that sport's list (event_not_found), odds are not available (odds_not_found), or the URL does not exist (not_found).

Should I keep polling?

Yes, while the match is displayed or in use. Stop when it is no longer needed.

Does the API guarantee real-time odds?

No. Responses contain the currently available odds at request time. The API does not wait for a new upstream update before responding.