SDK Guide — TypeScript, Python, Ruby, Go, Java
While you can always make requests to the API directly, the SDKs are an easy way to get started. Because SockOdds serves the SportsGameOdds v2 schema, the official SportsGameOdds SDKs work here unchanged — the only configuration is the base URL. This page shows install, configuration and use across the five languages.
These are SportsGameOdds' generated clients, not SockOdds's. Options and method names are theirs; SockOdds-only parameters (
includeFinished) can be passed through the raw-request escape hatch each client provides.Installation
# npm
npm install sports-odds-api
# yarn
yarn add sports-odds-api
# pnpm
pnpm add sports-odds-apipip install sports-odds-api
# poetry
poetry add sports-odds-apibundle add sports-odds-api
# or in the Gemfile
gem "sports-odds-api", "~> 1.0"go get github.com/SportsGameOdds/sports-odds-api-go// Maven
<dependency>
<groupId>com.sportsgameodds.api</groupId>
<artifactId>sports-odds-api</artifactId>
<version>1.0.0</version>
</dependency>
// Gradle
implementation 'com.sportsgameodds.api:sports-odds-api:1.0.0'Quick start
Configure the client with your key and the SockOdds base URL, then fetch a page of events:
import SportsGameOdds from "sports-odds-api";
const client = new SportsGameOdds({ apiKeyHeader: process.env.SOCKODDS_KEY, baseURL: "https://api.sockodds.com/v2" });
const page = await client.events.get({ leagueID: ["AFL"], oddsAvailable: true, limit: 5 });
console.log(`Found ${page.data.length} events`);
const event = page.data[0];
console.log(`Event: ${event.teams.away.names.medium} @ ${event.teams.home.names.medium}`);import os
from sports_odds_api import SportsGameOdds
client = SportsGameOdds(api_key_header=os.environ["SOCKODDS_KEY"], base_url="https://api.sockodds.com/v2")
page = client.events.get(league_id=["AFL"], odds_available=True, limit=5)
print(f"Found {len(page.data)} events")
event = page.data[0]
print(f"Event: {event.teams.away.names.medium} @ {event.teams.home.names.medium}")require "sports_odds_api"
client = SportsOddsAPI::Client.new(api_key_header: ENV["SOCKODDS_KEY"], base_url: "https://api.sockodds.com/v2")
page = client.events.get(league_id: ["AFL"], odds_available: true, limit: 5)
puts "Found #{page.data.length} events"
event = page.data[0]
puts "Event: #{event.teams.away.names.medium} @ #{event.teams.home.names.medium}"package main
import (
"context"
"fmt"
"os"
sportsoddsapi "github.com/SportsGameOdds/sports-odds-api-go"
"github.com/SportsGameOdds/sports-odds-api-go/option"
)
func main() {
client := sportsoddsapi.NewClient(option.WithAPIKeyHeader(os.Getenv("SOCKODDS_KEY")), option.WithBaseURL("https://api.sockodds.com/v2"))
page, err := client.Events.Get(context.Background(), sportsoddsapi.EventGetParams{LeagueID: sportsoddsapi.F([]string{"AFL"}), OddsAvailable: sportsoddsapi.F(true), Limit: sportsoddsapi.F(int64(5))})
if err != nil { panic(err) }
fmt.Printf("Found %d events\n", len(page.Data))
}import com.sportsgameodds.api.client.SportsGameOddsClient;
import com.sportsgameodds.api.client.okhttp.SportsGameOddsOkHttpClient;
SportsGameOddsClient client = SportsGameOddsOkHttpClient.builder()
.apiKeyHeader(System.getenv("SOCKODDS_KEY")).baseUrl("https://api.sockodds.com/v2").build();
var page = client.events().get(EventGetParams.builder().leagueID(List.of("AFL")).oddsAvailable(true).limit(5).build());
System.out.println("Found " + page.items().size() + " events");Pagination
Auto-pagination
The clients iterate nextCursor for you:
for await (const event of client.events.get({ leagueID: ["NRL"], finalized: false, limit: 100 })) console.log(event.eventID);for event in client.events.get(league_id=["NRL"], finalized=False, limit=100):
print(event.event_id)client.events.get(league_id: ["NRL"], finalized: false, limit: 100).auto_paging_each { |e| puts e.event_id }iter := client.Events.GetAutoPaging(ctx, params)
for iter.Next() { fmt.Println(iter.Current().EventID) }client.events().get(params).autoPager().forEach(e -> System.out.println(e.eventID()));Manual pagination
let page = await client.events.get({ leagueID: ["NRL"], limit: 100 });
while (page) { handle(page.data); page = page.hasNextPage() ? await page.getNextPage() : null; }page = client.events.get(league_id=["NRL"], limit=100)
while page:
handle(page.data)
page = page.get_next_page() if page.has_next_page() else NoneFiltering and query parameters
import SportsGameOdds from "sports-odds-api";
const client = new SportsGameOdds({ apiKeyHeader: "YOUR_API_KEY", baseURL: "https://api.sockodds.com/v2" });
const res = await client.events.get({ leagueID: ["AFL", "NRL"], oddsAvailable: true, oddID: ["points-home-game-ml-home"], includeOpposingOdds: true, bookmakerID: ["sportsbet", "tab"] });
console.log(res.data);from sports_odds_api import SportsGameOdds
client = SportsGameOdds(api_key_header="YOUR_API_KEY", base_url="https://api.sockodds.com/v2")
res = client.events.get({ league_id: ["afl", "nrl"], odds_available: true, odd_id: ["points-home-game-ml-home"], include_opposing_odds: true, bookmaker_id: ["sportsbet", "tab"] })
print(res.data)require "sports_odds_api"
client = SportsOddsAPI::Client.new(api_key_header: "YOUR_API_KEY", base_url: "https://api.sockodds.com/v2")
res = client.events.get({ league_id: ["afl", "nrl"], odds_available: true, odd_id: ["points-home-game-ml-home"], include_opposing_odds: true, bookmaker_id: ["sportsbet", "tab"] })
puts res.dataclient := sportsoddsapi.NewClient(option.WithAPIKeyHeader("YOUR_API_KEY"), option.WithBaseURL("https://api.sockodds.com/v2"))
res, err := client.Events.Get({ leagueID: ["AFL", "NRL"], oddsAvailable: true, oddID: ["points-home-game-ml-home"], includeOpposingOdds: true, bookmakerID: ["sportsbet", "tab"] })
if err != nil { log.Fatal(err) }
fmt.Println(res.Data)SportsGameOddsClient client = SportsGameOddsOkHttpClient.builder()
.apiKeyHeader("YOUR_API_KEY").baseUrl("https://api.sockodds.com/v2").build();
var res = client.events.get({ leagueID: ["AFL", "NRL"], oddsAvailable: true, oddID: ["points-home-game-ml-home"], includeOpposingOdds: true, bookmakerID: ["sportsbet", "tab"] });
System.out.println(res.items());Available filtersAvailable filters mirror the events endpoint:
leagueID, sportID, eventID(s), oddID, bookmakerID, teamID, playerID, oddsAvailable, live, started, ended, finalized, startsAfter, startsBefore, includeAltLines, includeOpposingOdds, limit, cursor.Error handling
import SportsGameOdds, { APIError, RateLimitError, AuthenticationError } from "sports-odds-api";
try {
await client.events.get({ leagueID: ["AFL"] });
} catch (e) {
if (e instanceof RateLimitError) console.log("429 — wait for Retry-After");
else if (e instanceof AuthenticationError) console.log("401/403 — check the key");
else if (e instanceof APIError) console.log(e.status, e.message);
else throw e;
}from sports_odds_api import APIError, RateLimitError, AuthenticationError
try:
client.events.get(league_id=["AFL"])
except RateLimitError:
print("429 — wait for Retry-After")
except AuthenticationError:
print("401/403 — check the key")
except APIError as e:
print(e.status_code, e.message)Common error types
AuthenticationError (401 missing/unknown key, 403 deactivated), RateLimitError (429), NotImplemented (501 on /stream/events), InternalServerError (5xx).Timeout and retry configuration
const client = new SportsGameOdds({ apiKeyHeader: KEY, baseURL: "https://api.sockodds.com/v2", timeout: 30_000, maxRetries: 2 });client = SportsGameOdds(api_key_header=KEY, base_url="https://api.sockodds.com/v2", timeout=30.0, max_retries=2)client := sportsoddsapi.NewClient(option.WithAPIKeyHeader(KEY), option.WithBaseURL("https://api.sockodds.com/v2"), option.WithMaxRetries(2), option.WithRequestTimeout(30*time.Second))SportsGameOddsOkHttpClient.builder().apiKeyHeader(KEY).baseUrl("https://api.sockodds.com/v2").maxRetries(2).timeout(Duration.ofSeconds(30)).build();Best practicesRetries are safe on every endpoint (all GET). Keep them at 2 or fewer — a 429 retried immediately just burns quota.
Working with odds data
for (const event of page.data) {
for (const [oddID, odd] of Object.entries(event.odds)) {
for (const [book, q] of Object.entries(odd.byBookmaker)) {
if (!q.available) continue;
console.log(oddID, book, q.decimal, "fair:", odd.fairOdds);
}
}
}for event in page.data:
for odd_id, odd in event.odds.items():
for book, q in odd.by_bookmaker.items():
if not q.available: continue
print(odd_id, book, q.decimal, "fair:", odd.fair_odds)Real-time streaming
Not availableSockOdds does not stream. The SDKs'
stream.events helpers will receive a 501; use the polling pattern instead.Type safety and IDE support
The TypeScript, Python (typed), Go and Java clients carry the v2 types — Event, Odds, ByBookmakerOdds — which are the types SockOdds returns. SockOdds-only fields (decimal, info.stale, links.betslip) are present on the wire and reachable through each client's raw-response accessor.
Advanced usage
Accessing raw response data
const { data: body, response } = await client.events.get({ leagueID: ["AFL"] }).withResponse();
console.log(response.headers.get("x-ratelimit-remaining"));res = client.events.with_raw_response.get(league_id=["AFL"])
print(res.headers.get("x-ratelimit-remaining"))