Claude API Go Language Integration Example
Anthropic doesn't ship an official Go SDK, so integrating Claude into a Go service means talking to the HTTPS API directly with net/http and encoding/json. That's not a problem — Go's standard library handles this cleanly — but you need to get the request shape, headers, and streaming format right. This article walks through a working integration example, from a single request/response call to streaming with Server-Sent Events, plus the error cases you'll hit in production.
If you'd rather skip building the HTTP plumbing yourself, SubToAPI (https://subtoapi.app) exposes the same Claude capabilities behind a standard sub_live_... key with streaming, tool use, and usage metadata already wired up — useful if you want to focus on your Go service logic instead of maintaining low-level request builders.
Setting Up the Request Structs
The Claude Messages API expects a JSON body with a model, a list of messages, and a max_tokens field. In Go, model this with structs that mirror the API shape:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type MessageRequest struct {
Model string `json:"model"`
MaxTokens int `json:"max_tokens"`
Messages []Message `json:"messages"`
}
type MessageResponse struct {
ID string `json:"id"`
Content []struct {
Type string `json:"type"`
Text string `json:"text"`
} `json:"content"`
Usage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
} `json:"usage"`
}
Keep the structs minimal at first — only decode the fields you actually use. You can expand them later for tool calls, stop reasons, or system prompts without breaking existing code.
Making the Call
func callClaude(apiKey, prompt string) (*MessageResponse, error) {
reqBody := MessageRequest{
Model: "claude-sonnet-4",
MaxTokens: 1024,
Messages: []Message{
{Role: "user", Content: prompt},
},
}
body, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", "https://api.anthropic.com/v1/messages", bytes.NewBuffer(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-api-key", apiKey)
req.Header.Set("anthropic-version", "2023-06-01")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("claude api error: %s", string(data))
}
var result MessageResponse
if err := json.Unmarshal(data, &result); err != nil {
return nil, err
}
return &result, nil
}
This is the core pattern you'll reuse everywhere: build the struct, marshal to JSON, set the required headers, send the request, check the status code before decoding. The anthropic-version header is easy to forget and causes a 400 if missing.
If you're using SubToAPI instead, the request body and response shape are the same — you just point at a different host and swap the header:
req.Header.Set("Authorization", "Bearer "+subToApiKey)
req, _ = http.NewRequest("POST", "https://api.subtoapi.app/v1/messages", bytes.NewBuffer(body))
See the quickstart and messages reference for the full request/response schema.
Handling Streaming Responses
For chat interfaces or long completions, streaming avoids making users wait for the full response. Claude streams Server-Sent Events, which means reading the response body line by line and parsing data: prefixed chunks:
func streamClaude(apiKey, prompt string) error {
reqBody := map[string]interface{}{
"model": "claude-sonnet-4",
"max_tokens": 1024,
"stream": true,
"messages": []Message{
{Role: "user", Content: prompt},
},
}
body, _ := json.Marshal(reqBody)
req, err := http.NewRequest("POST", "https://api.anthropic.com/v1/messages", bytes.NewBuffer(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-api-key", apiKey)
req.Header.Set("anthropic-version", "2023-06-01")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
reader := bufio.NewReader(resp.Body)
for {
line, err := reader.ReadString('\n')
if err == io.EOF {
break
}
if err != nil {
return err
}
if strings.HasPrefix(line, "data: ") {
payload := strings.TrimPrefix(line, "data: ")
payload = strings.TrimSpace(payload)
if payload == "[DONE]" || payload == "" {
continue
}
fmt.Println(payload)
}
}
return nil
}
You'd typically parse each payload into an event struct and extract delta.text for content blocks, accumulating it or forwarding it over a WebSocket to a frontend. SubToAPI's streaming docs cover the same event format if you want to validate your parser against a working reference.
Error Handling and Retries
Production Go code needs to handle rate limits (429), overloaded errors (529), and transient network failures. A simple exponential backoff wrapper around callClaude covers most cases:
func callWithRetry(apiKey, prompt string, maxRetries int) (*MessageResponse, error) {
var lastErr error
for i := 0; i < maxRetries; i++ {
resp, err := callClaude(apiKey, prompt)
if err == nil {
return resp, nil
}
lastErr = err
time.Sleep(time.Duration(1<<i) * time.Second)
}
return nil, lastErr
}
Inspect the error string for status codes if you need different behavior per failure type — don't retry on 400s (bad request), only on 429/5xx.
Adding Tool Use
If your Go service needs Claude to call functions — database lookups, calculators, internal APIs — add a tools field to the request struct and handle tool_use content blocks in the response. The shape is the same whether you're calling Anthropic directly or through SubToAPI; see tool use docs for the full schema including input_schema and tool result formatting.
Why Some Teams Add SubToAPI on Top
Writing your own Go client is straightforward, but maintaining it — tracking usage per team, rotating keys, handling billing — adds overhead outside your core product. SubToAPI gives you application-scoped sub_live_... keys, per-key usage metadata, and team seats on top of the same Messages API, so your Go code stays nearly identical while key management and billing move into a dashboard. Plans start at €9/month with a free trial at signup; see pricing for team and scale tiers.
Questions
Does Anthropic provide an official Go SDK for Claude? No. Anthropic maintains official SDKs for Python and TypeScript/JavaScript; Go integrations call the HTTPS API directly using net/http, as shown above.
What's the minimum Go code needed to call Claude? A struct matching the Messages API schema, a POST request with x-api-key and anthropic-version headers, and JSON decoding of the response — roughly 30–40 lines as shown in the example.
Can I use the same Go code with SubToAPI instead of Anthropic directly? Yes. The request and response JSON shapes match; you change the host to api.subtoapi.app and swap the auth header to Authorization: Bearer sub_live_....