# Go integration

Run these examples on your server. They use the language runtime HTTP client, not a platform SDK. No request is sent until you run a command. Models must be configured and enabled before use.

## Setup

Runtime: **Go 1.22+**. [Download complete source](/examples/go.go).

Set your deployment base URL, including `/v1` exactly once, and a server-side API key. `https://xxxrouter.com` is the official service domain; for local testing, replace it with your deployment origin. The service is not yet deployed and integration certification is not complete.

```sh
export XXXROUTER_BASE_URL='https://xxxrouter.com/v1'
export XXXROUTER_API_KEY='YOUR_SERVER_SIDE_KEY'
```

## Run one path

`chat` reads SSE deltas and final usage; `image` receives a synchronous image; `async-image` and `video` handle HTTP 202 and query the returned task. Polling is bounded to approximately five minutes plus any in-flight read; a timeout preserves the task for later reads.

```sh
go run go.go chat
# go run go.go image
# go run go.go async-image
# go run go.go video
```

## Idempotency and failure handling

A new run creates a new idempotency key and can incur a new charge. In an application, persist that key with the exact body before submitting. Set `XXXROUTER_REQUEST_ID` only to resume the same logical request with the same model, payload, API key and source. Do not rerun with a new key after a transport failure. These clients perform one generation POST and never retry it automatically.

Record the response `X-Request-ID`. HTTP errors retain the response body for diagnosis. A task with `status=unknown` stops automatic polling and requires later read/reconciliation; it is not a successful or free request. A stream error event or missing `[DONE]` is an interruption. Missing usage is not zero cost. Stopping the connection does not cancel accepted work. Do not log API keys or signed asset URLs.

## Image inputs and result files

For image-to-image or image-to-video, pass a public HTTPS URL in `image` when the model supports reference images. The public inference API takes URLs, not file bytes. Playground can upload temporary input images or videos to private R2 storage; only signed-in, verified users can use it. Generated results are never downloaded, stored or rehosted by XXXRouter. Successful responses return the original generated-media URL unchanged. The original host controls availability, expiry and browser cross-origin behavior. Save results before expiry; querying a completed task returns the saved URL and does not renew it. Request metadata and result URLs may be retained for recovery; generated-media bytes are never retained.

## Complete example

The code is an integration example, not proof that a live model or payment channel is enabled. Do not place it in a browser bundle.

```go
// Go 1.22+, standard library only. Run: go run go.go chat
package main

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

type Object = map[string]any

var base = strings.TrimRight(os.Getenv("XXXROUTER_BASE_URL"), "/") // Include /v1.
var key = os.Getenv("XXXROUTER_API_KEY")
var client = &http.Client{Timeout: 300 * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { return http.ErrUseLastResponse }}

func request(path string, payload Object, idem string) (*http.Response, error) {
	var body io.Reader
	method := "GET"
	if payload != nil {
		raw, err := json.Marshal(payload)
		if err != nil {
			return nil, err
		}
		body = bytes.NewReader(raw)
		method = "POST"
	}
	req, err := http.NewRequest(method, base+"/"+path, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+key)
	if payload != nil {
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Idempotency-Key", idem)
	}
	response, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	fmt.Fprintln(os.Stderr, "request_id=", response.Header.Get("X-Request-ID"))
	if response.StatusCode < 200 || response.StatusCode >= 300 {
		defer response.Body.Close()
		raw, _ := io.ReadAll(io.LimitReader(response.Body, 1<<20))
		return nil, fmt.Errorf("HTTP %d: %s", response.StatusCode, raw)
	}
	return response, nil
}
func decode(response *http.Response) (Object, error) {
	defer response.Body.Close()
	var result Object
	err := json.NewDecoder(response.Body).Decode(&result)
	return result, err
}
func stream(response *http.Response) error {
	defer response.Body.Close()
	scanner := bufio.NewScanner(response.Body)
	scanner.Buffer(make([]byte, 4096), 2<<20)
	var parts []string
	consume := func() (bool, error) {
		data := strings.Join(parts, "\n")
		parts = nil
		if data == "[DONE]" {
			return true, nil
		}
		if data == "" {
			return false, nil
		}
		var chunk struct {
			Error   json.RawMessage `json:"error"`
			Usage   json.RawMessage `json:"usage"`
			Choices []struct {
				Delta struct {
					Content string `json:"content"`
				} `json:"delta"`
			} `json:"choices"`
		}
		if err := json.Unmarshal([]byte(data), &chunk); err != nil {
			return false, err
		}
		if len(chunk.Error) > 0 && string(chunk.Error) != "null" {
			return false, fmt.Errorf("stream error: %s", chunk.Error)
		}
		for _, choice := range chunk.Choices {
			fmt.Print(choice.Delta.Content)
		}
		if len(chunk.Usage) > 0 && string(chunk.Usage) != "null" {
			fmt.Printf("\nusage: %s\n", chunk.Usage)
		}
		return false, nil
	}
	for scanner.Scan() {
		line := scanner.Text()
		if line == "" {
			done, err := consume()
			if err != nil || done {
				return err
			}
		}
		if strings.HasPrefix(line, "data:") {
			parts = append(parts, strings.TrimPrefix(line[5:], " "))
		}
	}
	if err := scanner.Err(); err != nil {
		return err
	}
	done, err := consume()
	if err != nil || done {
		return err
	}
	return fmt.Errorf("stream interrupted; keep request ID and idempotency key, do not resubmit")
}
func finish(result Object) error {
	deadline := time.Now().Add(5 * time.Minute)
	for {
		task, hasTask := result["task_id"].(string)
		status, _ := result["status"].(string)
		if !hasTask || status == "succeeded" || status == "failed" || status == "unknown" {
			break
		}
		if time.Now().After(deadline) {
			return fmt.Errorf("query task %s later; do not resubmit", task)
		}
		time.Sleep(2 * time.Second)
		response, err := request("tasks/"+task, nil, "")
		if err != nil {
			return err
		}
		result, err = decode(response)
		if err != nil {
			return err
		}
	}
	raw, _ := json.MarshalIndent(result, "", "  ")
	fmt.Println(string(raw))
	if result["status"] == "unknown" {
		return fmt.Errorf("outcome unconfirmed; retain IDs and wait for reconciliation")
	}
	if result["status"] == "failed" {
		return fmt.Errorf("generation failed; inspect error and billing")
	}
	return nil
}
func run() error {
	if base == "" || key == "" {
		return fmt.Errorf("set XXXROUTER_BASE_URL and XXXROUTER_API_KEY")
	}
	mode := "chat"
	if len(os.Args) > 1 {
		mode = os.Args[1]
	}
	path := "images/generations"
	var payload Object
	switch mode {
	case "chat":
		path = "chat/completions"
		payload = Object{"model": "deepseek-v4-flash", "messages": []Object{{"role": "user", "content": "Explain HTTP in one sentence."}}, "max_tokens": 256, "stream": true, "stream_options": Object{"include_usage": true}}
	case "image":
		payload = Object{"model": "seedream-4.5", "prompt": "A ceramic cup in natural light", "size": "2048x2048", "n": 1}
	case "async-image":
		payload = Object{"model": "nano-banana-pro", "prompt": "A ceramic cup in natural light", "size": "1024x1024", "n": 1}
	case "video":
		path = "videos/generations"
		payload = Object{"model": "kling-3.0", "prompt": "A slow camera move across a mountain landscape", "duration": 5, "aspect_ratio": "16:9"}
	default:
		return fmt.Errorf("choose chat, image, async-image or video")
	}
	idem := os.Getenv("XXXROUTER_REQUEST_ID")
	if idem == "" {
		b := make([]byte, 16)
		if _, err := rand.Read(b); err != nil {
			return err
		}
		idem = hex.EncodeToString(b)
	}
	fmt.Fprintln(os.Stderr, "idempotency_key=", idem) // Persist with the exact payload before submission.
	response, err := request(path, payload, idem)
	if err != nil {
		return err
	}
	if strings.Contains(response.Header.Get("Content-Type"), "text/event-stream") {
		return stream(response)
	}
	result, err := decode(response)
	if err != nil {
		return err
	}
	return finish(result)
}
func main() {
	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
```

## Related reference

[chat](/docs?page=chat&lang=en) · [images](/docs?page=images&lang=en) · [videos](/docs?page=videos&lang=en) · [tasks](/docs?page=tasks&lang=en) · [uploads](/docs?page=uploads&lang=en) · [errors](/docs?page=errors&lang=en)

[Runtime documentation](https://pkg.go.dev/net/http) · 2026-10-26
