PredictorSDK
SDK

Go SDK Reference

Type reference for the PredictorSDK Go client.

The Go client is auto-generated from the OpenAPI spec and published as github.com/PredictorSDK/sdk-go.

Client surfaces

SurfaceEndpoint
client.GetPlans(ctx)GET /v1/plans (no auth required)
client.GetSportsMatchingMarkets(ctx, request)GET /v1/matching-markets/sports
client.LookupSportsMatchingMarkets(ctx, request)GET /v1/matching-markets/sports/lookup
client.GetMarkets(ctx, request)GET /v1/markets
client.GetCategories(ctx)GET /v1/categories
client.GetMarket(ctx, request)GET /v1/markets/{market_id}
client.GetEvent(ctx, request)GET /v1/events/{event_id}
client.GetBinanceCryptoPrices(ctx, request)GET /v1/crypto-prices/binance
client.GetPolymarketWallet(ctx, request)GET /v1/polymarket/wallet
client.ListPolymarketWalletPositions(ctx, request)GET /v1/polymarket/wallet/positions

Four surfaces are paginated with a cursor: GetSportsMatchingMarkets, GetMarkets, GetBinanceCryptoPrices and ListPolymarketWalletPositions. Each returns a *core.Page that walks the cursor for you; see Pagination. The other surfaces return the parsed response.

Usage

import (
    "context"
    "fmt"

    predictorsdk "github.com/PredictorSDK/sdk-go"
    predictorclient "github.com/PredictorSDK/sdk-go/client"
    "github.com/PredictorSDK/sdk-go/option"
)

func main() {
    client := predictorclient.New(option.WithToken("YOUR_API_KEY"))

    // A paginated call returns a page: page.Results holds this page's events.
    page, err := client.GetSportsMatchingMarkets(context.TODO(), &predictorsdk.GetSportsMatchingMarketsRequest{
        League: predictorsdk.GetSportsMatchingMarketsRequestLeagueNfl.Ptr(),
    })
    if err != nil {
        panic(err)
    }
    for _, event := range page.Results {
        fmt.Println(event.EventID, event.ScheduledDate.Format("2006-01-02"), event.Title)
    }

    // A lookup finds events by identifier and answers one unpaginated response.
    found, err := client.LookupSportsMatchingMarkets(context.TODO(), &predictorsdk.LookupSportsMatchingMarketsRequest{
        SourceID: []*string{predictorsdk.String("kalshi:KXNBAGAME-26OCT20OKCSAS")},
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(found.Lookups)

    categories, _ := client.GetCategories(context.TODO())
    plans, _ := client.GetPlans(context.TODO()) // Public: works without an API key.
    fmt.Println(categories, plans)
}

Pagination

A call to GetSportsMatchingMarkets, GetMarkets, GetBinanceCryptoPrices or ListPolymarketWalletPositions returns a *core.Page. Its iterator visits every item on every page; the client sends each next cursor and repeats the filters that issued it. Pass nil as the request to use every default.

import (
    "errors"

    "github.com/PredictorSDK/sdk-go/core"
)

// Every event on every page.
page, err := client.GetSportsMatchingMarkets(ctx, &predictorsdk.GetSportsMatchingMarketsRequest{
    League: predictorsdk.GetSportsMatchingMarketsRequestLeagueNfl.Ptr(),
    Limit:  predictorsdk.Int(50),
})
if err != nil {
    return err
}
iterator := page.Iterator()
for iterator.Next(ctx) {
    event := iterator.Current()
    fmt.Println(event.EventID, event.Title)
}
if err := iterator.Err(); err != nil {
    return err
}

// One provider's markets, a page at a time. The whole catalog is about
// 470,000 markets, thousands of pages, so filter by provider or category first.
markets, err := client.GetMarkets(ctx, &predictorsdk.GetMarketsRequest{
    Provider: predictorsdk.GetMarketsRequestProviderProphetx.Ptr(),
    Limit:    predictorsdk.Int(100),
})
for err == nil {
    fmt.Println(len(markets.Results), *markets.Response.Pagination.Total)
    markets, err = markets.GetNextPage(ctx)
}
if !errors.Is(err, core.ErrNoPages) { // ErrNoPages marks the end, not a failure
    return err
}
MemberDescription
page.ResultsThe items on the current page
page.ResponseThe whole response of the current page, such as Snapshot and Pagination
page.StatusCode, page.HeaderThe current page's HTTP metadata
page.GetNextPage(ctx)The next page, or core.ErrNoPages after the last one
page.Iterator()An iterator from this page on: Next(ctx), Current() and Err()

Pagination.Total is a *int: set on GetMarkets and GetSportsMatchingMarkets, and nil on GetBinanceCryptoPrices and ListPolymarketWalletPositions, whose upstreams cannot count. Follow GetNextPage rather than Total. See the Pagination guide for the cursor rules.

Configuration

Options are passed to predictorclient.New():

OptionDescription
option.WithToken(token)Your API key (Bearer token)
option.WithBaseURL(url)Override the base URL
option.WithHTTPClient(client)Custom *http.Client. The default client sets no timeout, so set one here or give each request a context deadline.
option.WithHTTPHeader(header)Additional HTTP headers
option.WithMaxAttempts(n)Attempts for a 408, 429 or 5xx response or a connection that failed before any response, the first included (default: 3)
option.WithoutRetries()Send each request once

Key types

All types live in the root predictorsdk package:

import predictorsdk "github.com/PredictorSDK/sdk-go"

SportsMatchingListResponse

The Response of the page GetSportsMatchingMarkets returns.

FieldTypeDescription
Data[]*CanonicalSportsEventMatched events, soonest scheduled start first, each once. Each carries its full-game moneyline, or every matched submarket with IncludeSubmarkets set to true
Pagination*PaginationBlockPagination metadata
Snapshot*SportsMatchingSnapshotMatching freshness. ObservedAt is when the oldest venue read behind the snapshot began, or nil when unknown — check it before treating a missing event as unmatched

SportsMatchingLookupResponse

The response from LookupSportsMatchingMarkets: one unpaginated response, with no Pagination block.

FieldTypeDescription
Data[]*CanonicalSportsEventThe events the identifiers found, soonest scheduled start first, each once, with every matched submarket unless IncludeSubmarkets is false
Lookupsmap[string]*SportsMatchingLookupResultEvery EventID and SourceID you sent, as sent, with the events it found
Snapshot*SportsMatchingSnapshotMatching freshness, as above

SportsMatchingLookupResult

FieldTypeDescription
EventIDs[]stringThe EventIDs of the events in Data that hold the identifier; empty when it found none

CanonicalSportsEvent

One matched game, as the list and the lookup both return it.

FieldTypeDescription
EventIDstringThe canonical event key, such as nba-okc-sas-2026-10-20
SportstringThe sport, such as basketball
LeaguestringThe league: nba, wnba, nfl, mlb or nhl
TitlestringThe event title, such as Oklahoma City Thunder vs. San Antonio Spurs
ScheduledStart*time.TimeWhen the game starts, when a venue published an exact start time; nil when venues publish only a date. Millisecond precision, in UTC
ScheduledDatetime.TimeThe game's calendar day in America/New_York, as a date at midnight UTC: format it with Format("2006-01-02"). Always set, even when ScheduledStart is nil
Participants[]*CanonicalSportsParticipantThe two sides, each with a Key, a Name and a Role (away or home)
Submarkets[]*CanonicalSportsSubmarketThe matched submarkets in reading order: the full game, then each half and quarter, and within each the moneyline, spreads, totals, team totals and props

PaginationBlock

FieldTypeDescription
LimitintEchoes the limit request param (default 25, max 100)
Total*intTotal matching items across all pages; nil on the routes that cannot count (wallet positions, crypto prices)
HasMorebooltrue when more pages exist
NextCursor*stringThe cursor for the next page. A page sends it for you; pass it back as Cursor only to resume a walk by hand

CanonicalSportsSourceMarket

One venue market matched to a canonical submarket, with the same fields on every venue. EventID is the venue's parent event, for GetEvent; MarketID is the market itself, for GetMarket. Pass Provider to either call as its Provider by converting it, predictorsdk.GetMarketRequestProvider(m.Provider).Ptr() (or GetEventRequestProvider for GetEvent), or use the composite string(m.Provider) + ":" + m.MarketID. Any of EventID, MarketID or MarketSlug, prefixed with Provider, finds the event again as SourceID; so does a Polymarket, Predict or AlphaArcade OutcomeID. See Which identifier goes where.

FieldTypeDescription
ProviderCanonicalSportsSourceMarketProviderkalshi, polymarket, predict, sxbet, alpha-arcade, prophetx, pred, or limitless
EventIDstringThe venue's parent event, for GetEvent. Not a market ID
MarketIDstringThe market, for GetMarket. Kalshi lists a game winner as one market per team, so it appears once per team
MarketNamestringThe market's name: the venue's own title, or on SX Bet its two outcome labels, as GetMarket titles it
MarketSlug*stringThe venue's slug, where it has one (Polymarket, AlphaArcade). SourceID accepts it; for GetMarket send MarketID
Outcomes[]*CanonicalSportsSourceOutcomeEach side's OutcomeID and Label, mapped to a canonical outcome; GetMarket returns the same IDs. Kalshi and Pred sides are yes/no, labelled Yes/No

GetSportsMatchingMarketsRequest

Pass nil to list every matched event with defaults. The filters narrow the list, and Total, HasMore and the cursors describe the filtered set.

FieldTypeDescription
Limit*intEvents per page (1–100, default 25)
Cursor*stringResumes a walk by hand; a page sends it for you. Valid only with the filters that issued it
League*GetSportsMatchingMarketsRequestLeagueOnly this league: GetSportsMatchingMarketsRequestLeagueNba, ...Wnba, ...Nfl, ...Mlb or ...Nhl, each with a Ptr()
ScheduledDate*time.TimeOnly the games on this day in America/New_York, sent as YYYY-MM-DD: the event's ScheduledDate. Build one with predictorsdk.Time(predictorsdk.MustParseDate("2026-10-15"))
Participant*stringOnly events with a participant whose Key is this (such as okc), case-insensitive. Keys are unique only within a league (la is the Rams in the NFL and the Sparks in the WNBA), so pair it with League
IncludeSettled*boolAlso read the settled archive: games whose date has passed, and cancelled games (default false)
IncludeSubmarkets*boolEvery matched submarket, or only the full-game moneyline (default false)
PlayerPropMatch*GetSportsMatchingMarketsRequestPlayerPropMatchPlayer-prop policy, e.g. predictorsdk.GetSportsMatchingMarketsRequestPlayerPropMatchSameProp.Ptr(); requires IncludeSubmarkets

LookupSportsMatchingMarketsRequest

Send at least one identifier; with both, at most 100 unique values in all. EventID and SourceID take one *string per identifier, so a lookup is SourceID: []*string{predictorsdk.String("kalshi:KXNBAGAME-26OCT20OKCSAS")}.

FieldTypeDescription
EventID[]*stringCanonical event key(s)
SourceID[]*stringVenue ID(s) as {provider}:{id}: any MarketID, MarketSlug or EventID a source market publishes, a Polymarket, Predict or AlphaArcade outcome token, or a Polymarket condition ID. Case-insensitive
IncludeSettled*boolAlso search the settled archive: games whose date has passed, and cancelled games (default false)
IncludeSubmarkets*boolEvery matched submarket (default true for a lookup), or only the full-game moneyline
PlayerPropMatch*LookupSportsMatchingMarketsRequestPlayerPropMatchPlayer-prop policy; requires IncludeSubmarkets, the default for a lookup

Lookups then reports what each identifier found:

for identifier, found := range response.Lookups {
    fmt.Println(identifier, found.EventIDs)
}

MarketsListResponse

The Response of the page GetMarkets returns. Contains Data, catalog Snapshot freshness, and Pagination.

FieldTypeDescription
Data[]*UnifiedMarketArray of markets for the current page
Snapshot*MarketsSnapshotCatalog freshness; ObservedAt is when the crawl behind the oldest provider snapshot in the bound page scope began
Pagination*PaginationBlockPagination metadata

UnifiedMarket

A market from any supported prediction market provider.

FieldTypeDescription
IDstringComposite ID in the format {provider}:{provider_id}
ProviderUnifiedMarketProviderkalshi, polymarket, predict, sxbet, hyperliquid, alpha-arcade, prophetx, limitless, or pred
TitlestringHuman-readable market title/question
CategoryMarketCategoryCanonical top-level category, such as sports, politics, or crypto

GetMarketsRequest

Pass nil to fetch all markets with defaults, or provide pagination fields.

FieldTypeDescription
Limit*intResults per page (1–100, default 25)
Cursor*stringResumes a walk by hand; a page sends it for you
Category*MarketCategoryOptional canonical category filter
Provider*GetMarketsRequestProviderOptional canonical provider filter; replay cursors with the same value

GetMarketRequest and GetEventRequest

GetMarketRequest.MarketID and GetEventRequest.EventID take an ID that is either platform-native or the composite {provider}:{id}. Both requests take an optional Provider (*GetMarketRequestProvider and *GetEventRequestProvider, such as predictorsdk.GetMarketRequestProviderKalshi.Ptr()) to say which venue an ID belongs to. A bare ID that more than one venue holds answers 409 as a *ConflictError listing the candidates; retry with Provider set to one of them, or with the composite form. The retired Platform is not accepted.

market, err := client.GetMarket(ctx, &predictorsdk.GetMarketRequest{
    MarketID: "KXNBAGAME-26OCT20OKCSAS-OKC",
    Provider: predictorsdk.GetMarketRequestProviderKalshi.Ptr(),
})
if err != nil {
    return err
}
event, err := client.GetEvent(ctx, &predictorsdk.GetEventRequest{
    EventID:  "KXNBAGAME-26OCT20OKCSAS",
    Provider: predictorsdk.GetEventRequestProviderKalshi.Ptr(),
})
if err != nil {
    return err
}
// event.Provider is "kalshi": pass it back as Provider for the nested markets.
fmt.Println(market.ID, event.Provider)

EventResponse

The response from GetEvent.

FieldTypeDescription
EventIDstringEcho of the event ID you sent
ProviderEventResponseProviderThe venue the ID resolved against, the value to pass as Provider to GetMarket for its nested markets
TitlestringThe event title
Markets[]*EventMarketThe markets nested under the event, each with a platform-native MarketID and a Title
Fanout*EventFanoutPresent when Kalshi sibling events were merged in; lists which siblings were attempted and which returned nothing

GetBinanceCryptoPricesRequest

FieldTypeDescription
CurrencystringBinance trading pair, such as btcusdt
StartTime*int64Start of the range, a Unix timestamp in milliseconds (inclusive)
EndTime*int64End of the range, a Unix timestamp in milliseconds (inclusive)
Limit*intPrices per page, 1–100 (default 100 with a time range); more than 100 is a 400
Cursor*stringResumes a walk by hand; a page sends it for you. The retired PaginationKey is not accepted

CryptoPricesResponse

The Response of the page GetBinanceCryptoPrices returns: newest prices first, one per second.

FieldTypeDescription
Data[]*CryptoPriceItemThe prices on this page
Pagination*PaginationBlockPagination metadata; Total is nil

Pointer helpers

The predictorsdk package provides pointer helpers for building requests:

import predictorsdk "github.com/PredictorSDK/sdk-go"

request := &predictorsdk.LookupSportsMatchingMarketsRequest{
    SourceID: []*string{predictorsdk.String("kalshi:KXNBAGAME-26OCT20OKCSAS")},
}

Available helpers: String, Bool, Int, Int64, Float64, Time, UUID, and MustParseDate and MustParseDateTime to build a time.Time from a date or a timestamp string.

Raw responses

Use WithRawResponse on a surface that is not paginated to access HTTP metadata:

import (
    "context"
    "fmt"

    predictorsdk "github.com/PredictorSDK/sdk-go"
    predictorclient "github.com/PredictorSDK/sdk-go/client"
    "github.com/PredictorSDK/sdk-go/option"
)

func main() {
    client := predictorclient.New(option.WithToken("YOUR_API_KEY"))

    raw, err := client.WithRawResponse.LookupSportsMatchingMarkets(context.TODO(), &predictorsdk.LookupSportsMatchingMarketsRequest{
        SourceID: []*string{predictorsdk.String("kalshi:KXNBAGAME-26OCT20OKCSAS")},
    })
    if err != nil {
        panic(err)
    }
    fmt.Println(raw.StatusCode)
    fmt.Println(len(raw.Body.Data))
}

A paginated surface keeps the metadata on its page: page.StatusCode and page.Header.

Error handling

The SDK returns typed errors for known HTTP error codes:

Error typeStatus codeDescription
*BadRequestError400Invalid query parameters
*UnauthorizedError401Missing or invalid API key
*PaymentRequiredError402Monthly allowance exhausted or subscription payment recovery required
*ForbiddenError403Insufficient permissions
*NotFoundError404Market or event not found
*ConflictError409Bare identifier resolves on multiple providers; retry with provider or a composite ID
*TooManyRequestsError429Rate limit exceeded
*BadGatewayError502Upstream provider or API-key authorization dependency failure
*ServiceUnavailableError503Service temporarily unavailable

The client makes up to three attempts, the request and two retries, for a 408, 429 or 5xx response by default (option.WithMaxAttempts, which counts the first attempt, or option.WithoutRetries). A 429 waits for Retry-After, or else until the second-based X-RateLimit-Reset. Any other retried status honours Retry-After and otherwise backs off exponentially from one second. No wait exceeds 60 seconds, and the request's context ends a wait as soon as it is done. A 409 is never retried: send the request again with a Provider from its Candidates.

A request that gets no response because the connection was refused, reset or closed by the server is retried the same way, with the same backoff and the same limit. A timeout and a cancelled context are never retried. Every call is a read, so repeating one is safe. The TypeScript and Python SDKs follow the same policy, and core.APIError.Header carries the response's request id.

import (
    "context"
    "errors"
    "fmt"

    predictorclient "github.com/PredictorSDK/sdk-go/client"
    predictorsdk "github.com/PredictorSDK/sdk-go"
    "github.com/PredictorSDK/sdk-go/option"
)

func main() {
    client := predictorclient.New(option.WithToken("YOUR_API_KEY"))

    // A venue ID needs its provider prefix: "kalshi:KXNBAGAME-26OCT20OKCSAS".
    _, err := client.LookupSportsMatchingMarkets(context.TODO(), &predictorsdk.LookupSportsMatchingMarketsRequest{
        SourceID: []*string{predictorsdk.String("KXNBAGAME-26OCT20OKCSAS")},
    })
    var badReq *predictorsdk.BadRequestError
    if errors.As(err, &badReq) {
        fmt.Println("Bad request:", badReq.Body.Code, badReq.Body.Message)
    }
}

On this page