OKX Golang SDK
August 29, 2026 ยท View on GitHub

Go client for the OKX v5 API. Covers 335 REST endpoints and 53 WebSocket channels.
Package: pkg.go.dev/github.com/tigusigalpa/okx-go
Install
go get github.com/tigusigalpa/okx-go
Important
Breaking change in v1.1.0: OKXError has been renamed to Error to follow Go naming conventions. Update type assertions and errors.As targets from *okx.OKXError to *okx.Error when upgrading.
What's inside
- 335 REST endpoints across 16 categories
- 53 WebSocket channels (public, private, business)
- Demo trading mode (
x-simulated-trading: 1) context.Contexteverywhere- Typed request/response structs
- WebSocket reconnect with exponential backoff
- Goroutine-safe
- Dependencies: stdlib +
gorilla/websocket
Quick start
Runnable examples:
go run ./examples/rest
go run ./examples/websocket
REST
package main
import (
"context"
"fmt"
"log"
"github.com/tigusigalpa/okx-go"
)
func main() {
client := okx.NewRestClient(
"your-api-key",
"your-secret-key",
"your-passphrase",
okx.WithDemoTrading(),
)
ctx := context.Background()
balances, err := client.Account.GetBalance(ctx, nil)
if err != nil {
log.Fatal(err)
}
for _, b := range balances {
fmt.Printf("Total Equity: %s\n", *b.TotalEq)
}
// Place a limit order
px := "30000"
order := models.PlaceOrderRequest{
InstID: "BTC-USDT",
TdMode: "cash",
Side: "buy",
OrdType: "limit",
Px: &px,
Sz: "0.01",
}
result, err := client.Trade.PlaceOrder(ctx, order)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Order ID: %s\n", result[0].OrdID)
}
WebSocket (public)
ws := okx.NewWSClient("", "", "", okx.WSPublicURL)
ctx := context.Background()
if err := ws.Connect(ctx); err != nil {
log.Fatal(err)
}
defer ws.Close()
ch, err := ws.Subscribe(ctx, "tickers", map[string]interface{}{
"instId": "BTC-USDT",
})
if err != nil {
log.Fatal(err)
}
for msg := range ch {
fmt.Printf("%s\n", msg)
}
WebSocket (private)
ws := okx.NewWSClient(
"your-api-key",
"your-secret-key",
"your-passphrase",
okx.WSPrivateURL,
)
ctx := context.Background()
if err := ws.Connect(ctx); err != nil {
log.Fatal(err)
}
defer ws.Close()
if err := ws.Login(ctx); err != nil {
log.Fatal(err)
}
ch, err := ws.Subscribe(ctx, "account", map[string]interface{}{
"ccy": "BTC",
})
if err != nil {
log.Fatal(err)
}
for msg := range ch {
fmt.Printf("%s\n", msg)
}
Attached TP/SL (attachAlgoOrds)
OKX rejects the legacy inline tpTriggerPx/slTriggerPx fields on POST /api/v5/trade/order
for some instruments/scenarios with sCode=54070 ("use the attachAlgoOrds array to place
orders via Open API"). Use models.PlaceOrderRequest.AttachAlgoOrds โ the current Open API
mechanism for attaching TP/SL to the main order (as opposed to a separate, unlinked algo order
via PlaceAlgoOrder):
tpTriggerPx := "64000"
tpOrdPx := "-1" // "-1" = execute TP as a market order once triggered
slTriggerPx := "66000"
slOrdPx := "-1" // "-1" = execute SL as a market order once triggered
triggerPxType := "last" // "last", "index", or "mark"
order := models.PlaceOrderRequest{
InstID: "BTC-USDT-SWAP",
TdMode: "isolated",
Side: "sell",
OrdType: "market",
Sz: "1",
PosSide: strPtr("short"),
AttachAlgoOrds: []models.AttachAlgoOrderRequest{
{
TpTriggerPx: &tpTriggerPx,
TpOrdPx: &tpOrdPx,
TpTriggerPxType: &triggerPxType,
SlTriggerPx: &slTriggerPx,
SlOrdPx: &slOrdPx,
SlTriggerPxType: &triggerPxType,
},
},
}
result, err := client.Trade.PlaceOrder(ctx, order)
Notes:
AttachAlgoOrderRequestis a request-only DTO (all fields are pointers, so unset fields are omitted from the JSON payload). It is distinct frommodels.AttachAlgoOrder, which is the response shape returned insideOrder.AttachAlgoOrdsand has non-optional string fields.- Legacy inline
TpTriggerPx/TpOrdPx/SlTriggerPx/SlOrdPxfields onPlaceOrderRequestare kept for backward compatibility, but preferAttachAlgoOrdsgoing forward.
Options
| Option | Description | Default |
|---|---|---|
WithHTTPClient(c) | Custom *http.Client | &http.Client{Timeout: 30s} |
WithBaseURL(url) | Override base URL | https://www.okx.com |
WithDemoTrading() | Demo mode | off |
WithTimeout(d) | Request timeout | 30s |
WithRateLimiter(true) | Rate limiter | off |
WithLogger(l) | Custom Logger | no-op |
REST endpoints
| Category | Count | Docs |
|---|---|---|
| Account | 53 | link |
| Trade | 32 | link |
| Market Data | 24 | link |
| Public Data | 24 | link |
| Asset | 26 | link |
| Sub-account | 8 | link |
| Trading Bot | 44 | link |
| Copy Trading | 26 | link |
| Block Trading | 20 | link |
| Spread Trading | 13 | link |
| Financial Products | 33 | link |
| Fiat | 13 | link |
| Trading Statistics | 15 | link |
| System | 1 | link |
| Announcement | 2 | link |
| Affiliate | 1 | link |
| Total | 335 |
WebSocket channels
Public (31):
tickers, candle1D, candle1H, candle30m, trades, books, books5, bbo-tbt, opt-summary,
estimated-price, mark-price, mark-price-candle1D, price-limit, open-interest, funding-rate,
index-candle30m, index-tickers, status, public-struc-block-trades, block-tickers, block-trades,
liquidation-orders, sprd-tickers, sprd-books5, sprd-books-l2-tbt, sprd-public-trades, sprd-candle1D,
economic-calendar, call-auction-details, instruments, trades-all
Private (22):
account, positions, balance_and_position, orders, orders-algo, algo-advance, liquidation-warning,
account-greeks, rfqs, quotes, sprd-orders, sprd-trades, adl-warning, fills, deposit-info,
withdrawal-info, grid-orders-spot, grid-orders-contract, grid-positions, grid-sub-orders,
algo-recurring-buy, copytrading-lead-notification
Demo trading
REST โ pass okx.WithDemoTrading():
client := okx.NewRestClient(apiKey, secret, passphrase, okx.WithDemoTrading())
WebSocket โ use demo URLs:
okx.WSDemoPublicURLokx.WSDemoPrivateURLokx.WSDemoBusinessURL
Errors
balances, err := client.Account.GetBalance(ctx, nil)
if err != nil {
if errors.Is(err, okx.ErrUnauthorized) {
// bad credentials
} else if errors.Is(err, okx.ErrRateLimited) {
// slow down
} else if okxErr, ok := err.(*okx.Error); ok {
fmt.Printf("code=%s msg=%s\n", okxErr.Code, okxErr.Message)
}
}
Pagination
OKX uses cursor-based pagination (before/after). There's a generic Paginator[T] helper:
paginator := models.NewPaginator(func(after string) ([]models.Order, string, error) {
orders, err := client.Trade.GetOrdersHistory(ctx, "SPOT", nil, nil, nil, nil, nil, nil, &after, nil, nil, nil, nil)
if err != nil {
return nil, "", err
}
var next string
if len(orders) > 0 {
next = orders[len(orders)-1].OrdID
}
return orders, next, nil
})
allOrders, err := paginator.All()
Tests
# unit
go test ./...
# integration (demo env)
OKX_API_KEY=... OKX_SECRET_KEY=... OKX_PASSPHRASE=... go test -tags=integration ./...
Contributing
Fork, branch, PR. Make sure go test ./... passes and new code has tests. See CONTRIBUTING.md.
License
MIT. See LICENSE.
Author
Igor Sazonov โ @tigusigalpa โ sovletig@gmail.com
Links
Not affiliated with OKX. Test on demo before going live. Golang library.