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.