API Integration

How to Use Binance API with Go? net/http and go-binance Code Examples

Complete Binance API tutorial for Go (Golang): HMAC signing with the standard library crypto/hmac, request encapsulation with net/http, usage of the community-preferred library go-binance, real-time subscriptions with gorilla/websocket, and practical code for concurrent order placement using goroutines.

Accessing the Binance API using the Go programming language offers two major advantages: extremely high performance and zero-dependency deployment. A single binary file running on a Linux server typically exhibits 30-50% lower latency compared to Python or Node.js. The standard approach involves using the crypto/hmac standard library for signature generation combined with net/http for sending requests, or directly employing the popular community library github.com/adshao/go-binance/v2. This article provides complete code modules for native encapsulation, SDK usage, WebSocket subscriptions, and concurrent order placement. If you haven't registered on Binance yet, please complete KYC on the Binance Official Website first; new users can Register for Free.

I. Comparison of Go Language SDKs

Library Path GitHub Stars Features
adshao/go-binance github.com/adshao/go-binance/v2 3.2k Community favorite; full support for Spot + Futures.
go-numb/go-binance github.com/go-numb/go-binance 50+ Lightweight; focused on WebSocket.
Native net/http Standard Library Ultimate control; zero external dependencies.

Recommendation: Choose adshao/go-binance for rapid development; use native net/http for highly customized or High-Frequency Trading (HFT) scenarios.

II. Native net/http Signature Implementation

1. Project Initialization

mkdir binance-go && cd binance-go
go mod init binance-go
go get github.com/joho/godotenv

.env file:

BINANCE_API_KEY=your_key
BINANCE_SECRET_KEY=your_secret

2. Core Signature and Requests

client.go:

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strconv"
    "time"
)

const BaseURL = "https://api.binance.com"

type Client struct {
    APIKey    string
    SecretKey string
    HTTPClient *http.Client
}

func NewClient() *Client {
    return &Client{
        APIKey:    os.Getenv("BINANCE_API_KEY"),
        SecretKey: os.Getenv("BINANCE_SECRET_KEY"),
        HTTPClient: &http.Client{
            Timeout: 10 * time.Second,
            Transport: &http.Transport{
                MaxIdleConns:        100,
                MaxIdleConnsPerHost: 100,
                IdleConnTimeout:     90 * time.Second,
            },
        },
    }
}

func (c *Client) sign(payload string) string {
    h := hmac.New(sha256.New, []byte(c.SecretKey))
    h.Write([]byte(payload))
    return hex.EncodeToString(h.Sum(nil))
}

func (c *Client) Request(method, path string, params url.Values, signed bool) ([]byte, error) {
    if params == nil {
        params = url.Values{}
    }
    if signed {
        params.Set("timestamp", strconv.FormatInt(time.Now().UnixMilli(), 10))
        params.Set("recvWindow", "5000")
        query := params.Encode()
        params.Set("signature", c.sign(query))
    }

    fullURL := BaseURL + path
    var req *http.Request
    var err error

    if method == "GET" || method == "DELETE" {
        fullURL = fullURL + "?" + params.Encode()
        req, err = http.NewRequest(method, fullURL, nil)
    } else {
        req, err = http.NewRequest(method, fullURL, nil)
        req.URL.RawQuery = params.Encode()
    }
    if err != nil {
        return nil, err
    }

    if signed {
        req.Header.Set("X-MBX-APIKEY", c.APIKey)
    }

    resp, err := c.HTTPClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }
    if resp.StatusCode >= 400 {
        return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body))
    }
    return body, nil
}

type Balance struct {
    Asset  string `json:"asset"`
    Free   string `json:"free"`
    Locked string `json:"locked"`
}

type Account struct {
    CanTrade bool      `json:"canTrade"`
    Balances []Balance `json:"balances"`
}

func (c *Client) GetAccount() (*Account, error) {
    body, err := c.Request("GET", "/api/v3/account", nil, true)
    if err != nil {
        return nil, err
    }
    var acc Account
    if err := json.Unmarshal(body, &acc); err != nil {
        return nil, err
    }
    return &acc, nil
}

type Ticker struct {
    Symbol string `json:"symbol"`
    Price  string `json:"price"`
}

func (c *Client) GetPrice(symbol string) (float64, error) {
    params := url.Values{}
    params.Set("symbol", symbol)
    body, err := c.Request("GET", "/api/v3/ticker/price", params, false)
    if err != nil {
        return 0, err
    }
    var t Ticker
    if err := json.Unmarshal(body, &t); err != nil {
        return 0, err
    }
    return strconv.ParseFloat(t.Price, 64)
}

type Order struct {
    Symbol       string `json:"symbol"`
    OrderID      int64  `json:"orderId"`
    ClientOrderID string `json:"clientOrderId"`
    Status       string `json:"status"`
    ExecutedQty  string `json:"executedQty"`
}

func (c *Client) PlaceLimitOrder(symbol, side, quantity, price string) (*Order, error) {
    params := url.Values{}
    params.Set("symbol", symbol)
    params.Set("side", side)
    params.Set("type", "LIMIT")
    params.Set("timeInForce", "GTC")
    params.Set("quantity", quantity)
    params.Set("price", price)

    body, err := c.Request("POST", "/api/v3/order", params, true)
    if err != nil {
        return nil, err
    }
    var o Order
    if err := json.Unmarshal(body, &o); err != nil {
        return nil, err
    }
    return &o, nil
}

3. Main Program

main.go:

package main

import (
    "fmt"
    "log"
    "strconv"

    "github.com/joho/godotenv"
)

func main() {
    if err := godotenv.Load(); err != nil {
        log.Println(".env not found, using environment variables")
    }

    c := NewClient()

    // Query Account
    account, err := c.GetAccount()
    if err != nil {
        log.Fatalln("Failed to query account:", err)
    }
    fmt.Printf("Can Trade: %v\n", account.CanTrade)
    for _, b := range account.Balances {
        free, _ := strconv.ParseFloat(b.Free, 64)
        if free > 0 {
            fmt.Printf("  %s: %s (Locked %s)\n", b.Asset, b.Free, b.Locked)
        }
    }

    // Query Price
    price, _ := c.GetPrice("BTCUSDT")
    fmt.Printf("Latest BTC Price: %.2f\n", price)

    // Place Order (-10% from market price)
    testPrice := fmt.Sprintf("%.2f", price*0.9)
    order, err := c.PlaceLimitOrder("BTCUSDT", "BUY", "0.001", testPrice)
    if err != nil {
        log.Fatalln("Order placement failed:", err)
    }
    fmt.Printf("Order %d created, status: %s\n", order.OrderID, order.Status)
}

Run with:

go run *.go

III. go-binance Community Library

1. Installation

go get github.com/adshao/go-binance/v2

2. Spot Example

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/adshao/go-binance/v2"
)

func main() {
    client := binance.NewClient(
        os.Getenv("BINANCE_API_KEY"),
        os.Getenv("BINANCE_SECRET_KEY"),
    )
    ctx := context.Background()

    // Query Balance
    account, err := client.NewGetAccountService().Do(ctx)
    if err != nil {
        log.Fatal(err)
    }
    for _, b := range account.Balances {
        fmt.Printf("%s: %s\n", b.Asset, b.Free)
    }

    // Query Price
    prices, err := client.NewListPricesService().Symbol("BTCUSDT").Do(ctx)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(prices[0].Price)

    // Limit Buy Order
    order, err := client.NewCreateOrderService().
        Symbol("BTCUSDT").
        Side(binance.SideTypeBuy).
        Type(binance.OrderTypeLimit).
        TimeInForce(binance.TimeInForceTypeGTC).
        Quantity("0.001").
        Price("60000.00").
        Do(ctx)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Order ID: %d\n", order.OrderID)

    // K-lines
    klines, _ := client.NewKlinesService().
        Symbol("BTCUSDT").
        Interval("1h").
        Limit(100).
        Do(ctx)
    fmt.Printf("Retrieved %d K-lines\n", len(klines))
}

3. Futures Example

futuresClient := binance.NewFuturesClient(apiKey, secret)

// Set Leverage
_, err := futuresClient.NewChangeLeverageService().
    Symbol("BTCUSDT").Leverage(10).Do(ctx)

// Open Long
futuresOrder, err := futuresClient.NewCreateOrderService().
    Symbol("BTCUSDT").
    Side(binance.SideTypeBuy).
    Type(binance.OrderTypeLimit).
    TimeInForce(binance.TimeInForceTypeGTC).
    Quantity("0.01").
    Price("60000").
    PositionSide(binance.PositionSideTypeLong).
    Do(ctx)

// Query Positions
positions, _ := futuresClient.NewGetPositionRiskService().Do(ctx)
for _, p := range positions {
    if p.PositionAmt != "0" {
        fmt.Printf("%s Position %s @ %s\n", p.Symbol, p.PositionAmt, p.EntryPrice)
    }
}

IV. WebSocket Subscription (gorilla/websocket)

go get github.com/gorilla/websocket
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "time"

    "github.com/gorilla/websocket"
)

type TickerEvent struct {
    EventType string `json:"e"`
    EventTime int64  `json:"E"`
    Symbol    string `json:"s"`
    Close     string `json:"c"`
    Change    string `json:"P"`
    Volume    string `json:"v"`
}

func main() {
    url := "wss://stream.binance.com:9443/stream?streams=btcusdt@ticker/ethusdt@ticker"

    for {
        conn, _, err := websocket.DefaultDialer.Dial(url, nil)
        if err != nil {
            log.Println("Connection failed:", err, "; retrying in 5s")
            time.Sleep(5 * time.Second)
            continue
        }
        log.Println("WS Connected")

        // Heartbeat goroutine
        stop := make(chan struct{})
        go func() {
            ticker := time.NewTicker(3 * time.Minute)
            defer ticker.Stop()
            for {
                select {
                case <-ticker.C:
                    conn.WriteMessage(websocket.PingMessage, nil)
                case <-stop:
                    return
                }
            }
        }()

        for {
            _, message, err := conn.ReadMessage()
            if err != nil {
                log.Println("Read error:", err)
                close(stop)
                conn.Close()
                break
            }
            var combined struct {
                Stream string      `json:"stream"`
                Data   TickerEvent `json:"data"`
            }
            if err := json.Unmarshal(message, &combined); err != nil {
                continue
            }
            fmt.Printf("%s: %s (24h %s%%)\n",
                combined.Data.Symbol,
                combined.Data.Close,
                combined.Data.Change)
        }
    }
}

V. Concurrent Order Placement with Goroutines

Go's concurrency model is perfectly suited for placing multiple orders simultaneously:

func placeParallel(c *Client, orders []OrderSpec) []error {
    errs := make([]error, len(orders))
    done := make(chan int, len(orders))

    for i, spec := range orders {
        go func(idx int, s OrderSpec) {
            _, err := c.PlaceLimitOrder(s.Symbol, s.Side, s.Quantity, s.Price)
            errs[idx] = err
            done <- idx
        }(i, spec)
    }

    for range orders {
        <-done
    }
    return errs
}

type OrderSpec struct {
    Symbol   string
    Side     string
    Quantity string
    Price    string
}

// Usage
specs := []OrderSpec{
    {"BTCUSDT", "BUY", "0.001", "60000"},
    {"ETHUSDT", "BUY", "0.01", "3000"},
    {"BNBUSDT", "BUY", "0.1", "500"},
}
errors := placeParallel(client, specs)

Note: Binance Spot has a limit of 100 orders per 10 seconds. Keep concurrency under 50 to avoid triggering rate limits.

VI. Common Questions FAQ

Q1: How large is the compiled binary? Are there any dependencies?

A: A program with full Binance API functionality and WebSocket support is typically around 8-15 MB. It runs directly on Linux or Docker without any runtime dependencies (unlike Node.js, which requires a several-hundred-MB node_modules folder).

Q2: How much performance difference is there between go-binance and native net/http?

A: The difference is negligible as go-binance is a thin wrapper. A single REST call takes 5-8ms, and p99 latency for 1,000 concurrent requests is < 30ms. Compared to Python's 80-100ms, Go offers a 10x performance advantage.

Q3: How should I handle errors returned by Binance gracefully?

A: Binance returns errors in the format {"code": -1021, "msg": "..."}. It is recommended to define a BinanceError type for unified handling:

type BinanceError struct {
    Code int    `json:"code"`
    Msg  string `json:"msg"`
}

func (e *BinanceError) Error() string {
    return fmt.Sprintf("Binance %d: %s", e.Code, e.Msg)
}

Q4: Does Go WebSocket need to handle the 24-hour disconnection manually?

A: Yes. Binance forcibly disconnects all WebSockets every 24 hours. gorilla/websocket does not have a built-in auto-reconnect mechanism. Use the for loop + Dial pattern shown in Section IV for seamless reconnection.

Q5: How do I deploy in Docker?

A: Use a multi-stage build; the final image is only about 20 MB:

FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o bot

FROM alpine:3.19
RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/bot /bot
ENTRYPOINT ["/bot"]

After reviewing the Go implementation, return to the [Category Navigation](/en/vault/API Integration/) to explore technical tutorials for other language SDKs.

Keep reading

Still have Binance questions? Head back to the category page for more tutorials on the same topic.

Categories

Related tutorials

How to Apply for Binance API? Common Guide for Key Generation and Signatures 2026-04-14 How to Use Binance Spot API? Executable Code from Zero to Your First Order 2026-04-14 What are the Differences Between Binance Futures and Spot APIs? Endpoint, Parameter, and Weight Comparison 2026-04-14 Will My IP Get Banned? Detailed Explanation of Binance API Rate Limits and Weights 2026-04-14