Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

polymarket-go

Experimental

A Go client library for the Polymarket APIs. Provides access to market data, order books, pricing, events, trades, positions, and authenticated endpoints (orders, balances, cancellations) across all three Polymarket API surfaces.

Zero external dependencies — built entirely on the Go standard library.

Installation

go get github.com/caxqueiroz/polymarket-go

Requires Go 1.25 or later.

CLOB V2 migration

Order signing and submission use the official CLOB V2 protocol. The API base URL remains https://clob.polymarket.com.

  • The Polygon exchange addresses and order-signing domain use V2. L1 credential authentication still uses its original domain.
  • Signed orders include a millisecond Timestamp, Metadata, and Builder. Empty metadata/builder values become zero bytes32; custom values must be 32-byte hex strings. Expiration remains in the HTTP payload but is not part of the V2 signature.
  • Taker, Nonce, and FeeRateBPS are deprecated. Nonzero values are rejected instead of silently ignored; do not populate them from the fee-rate endpoint.
  • PostOrderWithOptions supports PostOnly and DeferExec. Post-only orders require GTC or GTD. Use OrderFAK for fill-and-kill; the legacy OrderIOC constant is translated to FAK on submission.
  • SignatureTypePoly1271 supports deposit-wallet signatures. Set Maker to the deposit-wallet address and authenticate HTTP requests with the owner EOA's credentials/address. Wallet deployment, collateral wrapping, and approvals are separate operations and are not performed by this library.
  • Builder attribution is a public bytes32 code in CreateOrderParams.Builder, included before signing. WithBuilderCredentials is a deprecated no-op and no longer emits legacy builder HMAC headers. Query the public paginated feed with GetBuilderTradesByCode; WithBuilderCode supplies the code for the deprecated GetBuilderTrades wrapper, not for order signing.
  • Trade responses retain taker-order and maker-fill attribution, and balance responses expose the per-contract Allowances map. Cancellation methods require server acknowledgement; partial batch cancellation returns both successful IDs and a *CancelError describing unconfirmed IDs.
signed, err := signer.CreateOrder(polymarket.CreateOrderParams{
    TokenID: tokenID,
    Price:   0.50,
    Size:    10,
    Side:    polymarket.Buy,
    Builder: os.Getenv("POLY_BUILDER_CODE"), // optional public bytes32 code
})
if err != nil {
    return err
}
resp, err := client.Clob.PostOrderWithOptions(ctx, signed, polymarket.OrderGTC,
    polymarket.PostOrderOptions{PostOnly: true})

go run ./examples/postorder -token TOKEN_ID previews an order without network calls. It requires a local POLY_PRIVATE_KEY; only an explicit -send submits an order. Check the token's tick size, minimum order size, market type, collateral, approvals, and account eligibility before submitting.

Offline regression coverage includes independent viem-generated V2 signing vectors (with source commit recorded in testdata/v2-signing.json), all four signature types, negative-risk domains, literal HTTP payloads, pagination, and cancellation failures. Run go test -race ./... and go vet ./.... These checks do not establish live trading readiness.

Quick Start

Public endpoints (no credentials needed)

package main

import (
    "context"
    "fmt"
    "log"

    polymarket "github.com/caxqueiroz/polymarket-go"
)

func main() {
    client := polymarket.NewClient()
    ctx := context.Background()

    // Search for markets
    results, err := client.Gamma.Search(ctx, "bitcoin")
    if err != nil {
        log.Fatal(err)
    }
    for _, e := range results.Events {
        fmt.Println(e.Title)
    }

    // Get order book depth
    book, err := client.Clob.GetBook(ctx, "TOKEN_ID")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Best bid: %s, Best ask: %s\n", book.Bids[0].Price, book.Asks[0].Price)
}

Authenticated endpoints (orders, balances, trades)

client := polymarket.NewClient(
    polymarket.WithCredentials(&polymarket.Credentials{
        Key:        os.Getenv("POLY_API_KEY"),
        Secret:     os.Getenv("POLY_API_SECRET"),
        Passphrase: os.Getenv("POLY_PASSPHRASE"),
        Address:    os.Getenv("POLY_ADDRESS"),
    }),
)

orders, err := client.Clob.GetOrders(ctx, nil)
balance, err := client.Clob.GetBalanceAllowance(ctx, nil)
trades, err := client.Clob.GetTrades(ctx, nil)
_, err = client.Clob.CancelAll(ctx)

Architecture

The top-level Client provides access to three sub-clients, each targeting a different Polymarket API:

client := polymarket.NewClient()

client.Clob   // Order book, pricing, market parameters
client.Gamma  // Market/event metadata, search, tags
client.Data   // Trades, positions, activity, open interest
Sub-client Base URL Focus
ClobClient https://clob.polymarket.com Order book, prices, spreads, tick sizes
GammaClient https://gamma-api.polymarket.com Market/event metadata, search, tags
DataClient https://data-api.polymarket.com Trades, positions, activity, holders

Public endpoints work without credentials. Authenticated CLOB endpoints (orders, trades, balances) require API key credentials.

Configuration

Use functional options to customize the client:

client := polymarket.NewClient(
    polymarket.WithHTTPClient(myHTTPClient),
    polymarket.WithClobBaseURL("https://custom-clob-url.com"),
    polymarket.WithGammaBaseURL("https://custom-gamma-url.com"),
    polymarket.WithDataBaseURL("https://custom-data-url.com"),
)
Option Description
WithHTTPClient(c) Use a custom *http.Client (timeouts, transport, retries)
WithClobBaseURL(u) Override CLOB API base URL
WithGammaBaseURL(u) Override Gamma API base URL
WithDataBaseURL(u) Override Data API base URL
WithCredentials(c) Set API key credentials for authenticated CLOB requests

Authentication

Several CLOB endpoints require API key authentication. To get your API key, secret, and passphrase, follow the Polymarket CLOB Authentication guide to derive credentials from your wallet.

Pass your credentials via WithCredentials:

client := polymarket.NewClient(
    polymarket.WithCredentials(&polymarket.Credentials{
        Key:        "your-api-key",        // API key (UUID)
        Secret:     "your-base64-secret",  // Base64url-encoded HMAC secret
        Passphrase: "your-passphrase",     // Passphrase
        Address:    "0xYourWallet",        // Wallet address (hex)
    }),
)

// Authenticated endpoints now work automatically
orders, err := client.Clob.GetOrders(ctx, nil)
balance, err := client.Clob.GetBalanceAllowance(ctx, nil)
err = client.Clob.CancelOrder(ctx, "order-id")

When credentials are set, the library automatically signs every CLOB request with the required POLY_* headers (HMAC-SHA256). Public endpoints continue to work normally — the auth headers are simply included alongside your requests.

For production use, load credentials from environment variables:

client := polymarket.NewClient(
    polymarket.WithCredentials(&polymarket.Credentials{
        Key:        os.Getenv("POLY_API_KEY"),
        Secret:     os.Getenv("POLY_API_SECRET"),
        Passphrase: os.Getenv("POLY_PASSPHRASE"),
        Address:    os.Getenv("POLY_ADDRESS"),
    }),
)

Order Signing (EIP-712)

Placing orders on the CLOB requires EIP-712 typed data signatures. The library includes a complete, zero-dependency implementation of Keccak-256 and secp256k1 ECDSA signing.

Create and sign an order

// 1. Create an order signer with your private key
signer, err := polymarket.NewOrderSigner(
    os.Getenv("POLY_PRIVATE_KEY"), // hex-encoded secp256k1 private key
    polymarket.Polygon,            // chain config (Polygon mainnet)
)

// 2. Build and sign an order in one step
signed, err := signer.CreateOrder(polymarket.CreateOrderParams{
    TokenID: "71321045679252212594626385532706912750332728571942532289631379312455583992563",
    Price:   0.50,    // price per share (0..1)
    Size:    10.0,    // number of shares
    Side:    polymarket.Buy,
})

// 3. Post the signed order via the CLOB client (requires API credentials)
resp, err := client.Clob.PostOrder(ctx, signed, polymarket.OrderGTC)
fmt.Printf("Order ID: %s\n", resp.OrderID)

Advanced: build then sign separately

// Build the order (unsigned)
order, err := signer.BuildOrder(polymarket.CreateOrderParams{
    TokenID:       tokenID,
    Price:         0.75,
    Size:          100.0,
    Side:          polymarket.Sell,
    Expiration:    time.Now().Add(time.Hour).Unix(), // Unix seconds; HTTP-only
    NegRisk:       true,          // for neg-risk markets
    SignatureType: polymarket.SignatureTypePoly, // proxy wallet
    Maker:         proxyWalletAddr,             // override maker address
})

// Sign it
signed, err := signer.SignOrder(order)

Order types

Constant Description
OrderGTC Good Till Cancelled
OrderGTD Good Till Date
OrderFOK Fill Or Kill
OrderFAK Fill And Kill
OrderIOC Legacy spelling; submitted as FAK

Signature types

Constant Description
SignatureTypeEOA Direct wallet (externally owned account)
SignatureTypePoly Polymarket proxy wallet
SignatureTypeGnosisSafe Gnosis Safe multisig
SignatureTypePoly1271 Deposit wallet (POLY_1271), with its address as Maker

API Reference

CLOB Client — Order Book & Pricing

// Health & info
client.Clob.Health(ctx)                                     // error
client.Clob.ServerTime(ctx)                                 // (string, error)

// Markets
client.Clob.GetMarket(ctx, conditionID)                     // (*ClobMarket, error)
client.Clob.ListMarkets(ctx, nextCursor)                    // (*CursorPage[ClobMarket], error)
client.Clob.ListSimplifiedMarkets(ctx, nextCursor)          // (*CursorPage[SimplifiedMarket], error)
client.Clob.AllMarkets(ctx)                                 // *CursorIterator[ClobMarket]

// Single-token pricing
client.Clob.GetPrice(ctx, tokenID, side)                    // (string, error)
client.Clob.GetMidpoint(ctx, tokenID)                       // (string, error)
client.Clob.GetSpread(ctx, tokenID)                         // (string, error)
client.Clob.GetLastTradePrice(ctx, tokenID)                 // (*LastTradePrice, error)

// Bulk pricing (POST)
client.Clob.GetPrices(ctx, []BookParams{...})               // (map[string]map[string]string, error)
client.Clob.GetMidpoints(ctx, []BookParams{...})            // (map[string]string, error)
client.Clob.GetSpreads(ctx, []BookParams{...})              // (map[string]string, error)
client.Clob.GetLastTradesPrices(ctx, []BookParams{...})     // ([]LastTradesPriceEntry, error)

// Order book
client.Clob.GetBook(ctx, tokenID)                           // (*OrderBook, error)
client.Clob.GetBooks(ctx, []BookParams{...})                // ([]OrderBook, error)

// Market info
client.Clob.GetTickSize(ctx, tokenID)                       // (TickSize, error)
client.Clob.GetNegRisk(ctx, tokenID)                        // (bool, error)
client.Clob.GetFeeRateBPS(ctx, tokenID)                     // (float64, error)
client.Clob.GetPriceHistory(ctx, PriceHistoryParams{...})   // (*PriceHistoryResponse, error)
client.Clob.GetMarketTradeEvents(ctx, conditionID)          // ([]MarketTradeEvent, error)

// Authenticated — Orders (requires WithCredentials)
client.Clob.PostOrder(ctx, signedOrder, orderType)           // (*PostOrderResponse, error)
client.Clob.GetOrder(ctx, orderID)                          // (*Order, error)
client.Clob.GetOrders(ctx, &OrdersParams{...})              // ([]OpenOrder, error)
client.Clob.CancelOrder(ctx, orderID)                       // error
client.Clob.CancelOrders(ctx, []string{...})                // ([]string, error)
client.Clob.CancelAll(ctx)                                  // ([]string, error)

// Authenticated — Trades, Balance, Notifications
client.Clob.GetTrades(ctx, &ClobTradesParams{...})          // ([]UserTrade, error)
client.Clob.GetBalanceAllowance(ctx, &BalanceParams{...})   // (*BalanceAllowance, error)
client.Clob.GetNotifications(ctx)                           // ([]Notification, error)

Gamma Client — Market Metadata

// Markets
client.Gamma.ListMarkets(ctx, &GammaMarketParams{...})     // ([]GammaMarket, error)
client.Gamma.GetMarketByID(ctx, id)                         // (*GammaMarket, error)
client.Gamma.GetMarketBySlug(ctx, slug)                     // (*GammaMarket, error)

// Events
client.Gamma.ListEvents(ctx, &GammaEventParams{...})        // ([]GammaEvent, error)
client.Gamma.GetEventByID(ctx, id)                          // (*GammaEvent, error)
client.Gamma.GetEventBySlug(ctx, slug)                      // (*GammaEvent, error)

// Search & tags
client.Gamma.Search(ctx, query)                             // (SearchResponse, error)
client.Gamma.ListTags(ctx)                                  // ([]GammaTag, error)
client.Gamma.GetTagByID(ctx, id)                            // (*GammaTag, error)
client.Gamma.GetTagBySlug(ctx, slug)                        // (*GammaTag, error)

Data Client — Trades & Positions

client.Data.ListTrades(ctx, &TradesParams{...})             // ([]Trade, error)
client.Data.ListPositions(ctx, &PositionsParams{...})       // ([]Position, error)
client.Data.ListActivity(ctx, &ActivityParams{...})         // ([]Activity, error)
client.Data.ListHolders(ctx, &HoldersParams{...})           // ([]TokenHolders, error)
client.Data.GetOpenInterest(ctx, market)                    // ([]OpenInterest, error)

Pagination

CLOB list endpoints use cursor-based pagination. You can page manually or use the iterator:

Manual paging

page, err := client.Clob.ListMarkets(ctx, "") // first page
for _, m := range page.Data {
    fmt.Println(m.Question)
}

if page.NextCursor != "" {
    page, err = client.Clob.ListMarkets(ctx, page.NextCursor)
}

Iterator (automatic paging)

iter := client.Clob.AllMarkets(ctx)
for iter.Next(ctx) {
    market := iter.Item()
    fmt.Println(market.Question)
}
if err := iter.Err(); err != nil {
    log.Fatal(err)
}

Gamma and Data endpoints use limit/offset pagination managed via their respective param structs:

limit, offset := 10, 0
markets, err := client.Gamma.ListMarkets(ctx, &polymarket.GammaMarketParams{
    Limit:  &limit,
    Offset: &offset,
})

Error Handling

All API errors are returned as *polymarket.APIError with the HTTP status code, status text, and response body:

book, err := client.Clob.GetBook(ctx, "invalid-token")
if err != nil {
    var apiErr *polymarket.APIError
    if errors.As(err, &apiErr) {
        fmt.Printf("Status: %d, Body: %s\n", apiErr.StatusCode, apiErr.Body)
    }
}

Convenience helpers for common error checks:

if polymarket.IsNotFound(err) {
    // 404 — resource doesn't exist
}

if polymarket.IsRateLimited(err) {
    // 429 — back off and retry
}

Timeouts, Retries & Resilience

The library intentionally does not embed timeout, retry, or circuit breaker logic. Instead, use the standard Go approach — pass a configured *http.Client:

// Simple timeout
client := polymarket.NewClient(
    polymarket.WithHTTPClient(&http.Client{
        Timeout: 10 * time.Second,
    }),
)

// Per-request timeout via context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
book, err := client.Clob.GetBook(ctx, tokenID)

For retries, circuit breakers, or rate limiting, wrap the HTTP transport:

// Example with hashicorp/go-retryablehttp
retryClient := retryablehttp.NewClient()
retryClient.RetryMax = 3

client := polymarket.NewClient(
    polymarket.WithHTTPClient(retryClient.StandardClient()),
)

Examples

See the examples/ directory for runnable programs:

Example Description
examples/markets List active markets with volume and pricing
examples/prices Fetch CLOB prices, midpoints, and spreads
examples/orderbook Display order book depth for a market
examples/orders Authenticated: list orders, balance, trades
examples/search Search markets and events by keyword

Run an example:

go run ./examples/markets

Design Notes

  • String prices in CLOB — The CLOB API returns prices as strings to avoid floating-point precision loss. This library preserves that.
  • Number type for Gamma floats — The Gamma API inconsistently returns numeric fields as either JSON numbers (123.45) or JSON strings ("123.45"). The Number type (underlying float64) handles both. Use float64(m.Liquidity) or m.Liquidity.Float64() when you need a plain float64.
  • Separate market types — ClobMarket and GammaMarket are distinct types because the two APIs return different field sets with different JSON naming conventions.
  • StringSlice unmarshaler — The Gamma API inconsistently serializes array fields (like outcomes) as either a JSON array ["Yes","No"] or a JSON string "[\"Yes\",\"No\"]". The custom StringSlice type handles both transparently.
  • Pointer fields for optional params — Query parameter structs use *int, *bool, etc. to distinguish "not set" from zero values.
  • Generic cursor pagination — CursorPage[T] and CursorIterator[T] eliminate boilerplate across CLOB list endpoints.
  • Pure-Go EIP-712 signing — Includes Keccak-256 (pre-NIST) and secp256k1 ECDSA implementations with zero external dependencies. The OrderSigner handles the full EIP-712 typed data hashing and signing flow for CLOB order placement.

Project Structure

polymarket-go/
  client.go          # Client, NewClient, functional options, shared HTTP
  auth.go            # Credentials, HMAC signing, request signing
  crypto.go          # Keccak-256, secp256k1 curve, ECDSA signing
  signer.go          # EIP-712 order signing, OrderSigner, ChainConfig
  errors.go          # APIError, IsNotFound, IsRateLimited
  models.go          # Shared types: Side, Interval, TickSize, BookParams
  pagination.go      # CursorPage[T], CursorIterator[T]
  clob.go            # ClobClient (~29 methods, public + authenticated)
  clob_models.go     # CLOB request/response types
  gamma.go           # GammaClient (12 methods)
  gamma_models.go    # Gamma types, Number, StringSlice unmarshalers
  data.go            # DataClient (5 methods)
  data_models.go     # Data types
  *_test.go          # Tests
  examples/          # Runnable examples

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages