SDK

Go SDK

Typed responses and context-aware calls, errors you match with errors.Is, iterators for paged endpoints and a WebSocket client.

Not published yet: the install lines go live with the first release.

Install

One package, no setup beyond your API key.

Go
$ go get github.com/sportsapiapp/sportsapi-go

Authenticate

Use the API key from your dashboard. Pass it to the client, or set SPORTSAPI_API_KEY and the client reads it.

Go
client := sportsapi.NewClient(sportsapi.WithAPIKey("sk_live_…")) // or SPORTSAPI_API_KEY

First calls

Every method returns the API's { data, meta } envelope, typed. Path parameters come first, in path order, then the query parameters with the API's own names.

Go
live, err := client.Livescores.List(ctx, &sportsapi.LivescoresListParams{Sport: sportsapi.Ptr("football")})
if err != nil {
	log.Fatal(err)
}
for _, f := range live.Data {
	fmt.Println(f.Home.Name, *f.HomeScore.Current, "-", *f.AwayScore.Current, f.Away.Name)
}

table, err := client.Leagues.Standings(ctx, 17, &sportsapi.LeaguesStandingsParams{Season: 61627})

Errors

One error type per HTTP status, each carrying the API's code, message and Retry-After. Branch on the code, never on the message.

Go
_, err := client.Fixtures.WinProbability(ctx, 211521, nil)
switch {
case errors.Is(err, sportsapi.ErrForbidden): // 403: endpoint_not_in_plan, …
case errors.Is(err, sportsapi.ErrNotFound): // 404: not_found, no_data, not_supported
}
var apiErr *sportsapi.APIError
if errors.As(err, &apiErr) {
	fmt.Println(apiErr.Status, apiErr.Code, apiErr.RetryAfter)
}

Paging

Every endpoint with a page parameter has a pages iterator that walks the pages until there are no more.

Go
for page, err := range client.Teams.FixturesPages(ctx, 42, &sportsapi.TeamsFixturesParams{Type: sportsapi.Ptr("recent")}) {
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(len(page.Data))
}

WebSocket stream (Business)

The stream mints its own single-use ticket, subscribes, reconnects and replays what it missed, and merges every delta into the fixtures it holds.

Go
s, err := client.Stream(ctx, []string{"livescores", "fixture:237609"}, nil)
if err != nil {
	log.Fatal(err)
}
defer s.Close()
for f := range s.Frames() {
	if d, ok := f.Value.(*sportsapi.WsDelta); ok {
		fmt.Println("delta", d.ID)
	}
	live, _ := s.Fixtures("livescores") // the merged state, any time
	_ = live
}

Options

A 429 rate_limited or a 503 is retried after Retry-After (twice by default); a quota error never is.

Go
client := sportsapi.NewClient(
	sportsapi.WithAPIKey(key),     // or SPORTSAPI_API_KEY
	sportsapi.WithMaxRetries(2),
)
// SPORTSAPI_BASE_URL points it elsewhere
Every endpoint, with this SDK's call on each page→