LangChainGo: connecting a Go application to an OpenAI-compatible API

The langchaingo library accepts any OpenAI-compatible address. We show the client, a request, streaming and a templated chain in Go, and estimate call costs.

5 min · · development · API keys

LangChain for Go, known as langchaingo, talks to models over the same protocol as the official OpenAI SDK. That means you can point it at any OpenAI-compatible gateway by changing the address, key and model name. Below is a step-by-step langchain go setup: the client, a simple request, messages with a system role, streaming and a chain with a template. At the end we calculate what such calls cost.

The code is checked against the sources of github.com/tmc/langchaingo on 18 October 2026. The library's API keeps evolving, so before you start, compare with your version on pkg.go.dev. The library's go.mod at the time of checking specified Go 1.26, so make sure your toolchain fits.

Installation and key

bash
go get github.com/tmc/langchaingo

Create the fetch_... key in the Zukko dashboard. The full key is shown once, so save it and pass it to the app through an environment variable:

bash
export ZUKKO_API_KEY=fetch_your_key

The client: base URL, key and model

The llms/openai package creates a client with the openai.New function and options. We need three: WithBaseURL, WithToken and WithModel.

bash
package main

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

	"github.com/tmc/langchaingo/llms"
	"github.com/tmc/langchaingo/llms/openai"
)

func main() {
	ctx := context.Background()

The address for the OpenAI format always ends in /v1: https://api.zukko.pro/v1. The library adds the /chat/completions path itself.

The same without code: per the sources, if no options are set the client reads the environment variables OPENAI_BASE_URL, OPENAI_API_KEY and OPENAI_MODEL. Then openai.New() is enough in the program, and the address, key and model are switched from outside.

Messages, the system role and token usage

For a dialogue with a system instruction use GenerateContent:

bash
messages := []llms.MessageContent{
	llms.TextParts(llms.ChatMessageTypeSystem, "You are a Go developer's assistant. Answer briefly."),
	llms.TextParts(llms.ChatMessageTypeHuman, "How does context.WithTimeout differ from context.WithDeadline?"),
}

resp, err := llm.GenerateContent(ctx, messages, llms.WithTemperature(0.2))
if err != nil {
	log.Fatal(err)
}

choice := resp.Choices[0]
fmt.Println(choice.Content)
fmt.Println("input:", choice.GenerationInfo["PromptTokens"],
	"output:", choice.GenerationInfo["CompletionTokens"],
	"cached:", choice.GenerationInfo["PromptCachedTokens"])

The library puts token usage data into GenerationInfo: PromptTokens, CompletionTokens, TotalTokens, and also PromptCachedTokens. For GPT models the gateway sees cache reads, and they arrive in prompt_tokens_details.cached_tokens, which is where the library takes this field from. These numbers are handy to log: they let you reconcile with the charges in the dashboard.

About llms.WithMaxTokens: per the sources, the library sends it as max_completion_tokens. If a particular model or route answers 400 to that parameter, remove the option, check the request with curl, and consult the langchaingo documentation about its mode for the old max_tokens field.

Streaming

bash
_, err = llm.GenerateContent(ctx, messages,
	llms.WithStreamingFunc(func(ctx context.Context, chunk []byte) error {
		fmt.Print(string(chunk))
		return nil
	}),
)

In OpenAI-format streams, the Zukko server always sends a final chunk with usage and an empty choices list. If you write your own stream parser, check that choices is not empty before reading the first element. langchaingo handles such a chunk: the library skips it as a text piece and takes the usage statistics from it.

A chain with a template

In langchaingo, LLMChain ties a prompt template and a model together. In the current version the template is written in Go template syntax:

bash
import (
	"github.com/tmc/langchaingo/chains"
	"github.com/tmc/langchaingo/prompts"
)

prompt := prompts.NewPromptTemplate(
	"Suggest three names for a service that {{.description}}. Answer as a list.",
	[]string{"description"},
)

chain := chains.NewLLMChain(llm, prompt)

out, err := chains.Run(ctx, chain, "turns voice messages into text")
if err != nil {
	log.Fatal(err)
}
fmt.Println(out)

chains.Run fits when the chain has one input parameter and one output. For several parameters use chains.Call and pass a map[string]any. If you port examples from old articles, check the substitution syntax: curly braces with a bare name and no dot may not work.

The official openai-go SDK

If you do not need LangChain and just want an OpenAI-format client in Go, the official github.com/openai/openai-go/v3 library fits. Per its README, the address is changed with the option.WithBaseURL option:

bash
client := openai.NewClient(
	option.WithBaseURL("https://api.zukko.pro/v1"),
	option.WithAPIKey(os.Getenv("ZUKKO_API_KEY")),
)

resp, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
	Messages: []openai.ChatCompletionMessageParamUnion{
		openai.UserMessage("Hi! Answer in one word."),
	},
	Model: "deepseek-v4.1-flash",
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(resp.Choices[0].Message.Content)

The openai and option packages are imported from github.com/openai/openai-go/v3 and github.com/openai/openai-go/v3/option. That library's README at the time of checking required Go 1.26 or newer. For a low-level option with net/http and SSE parsing see Claude API in Go.

What it costs

Prices as of 18 October 2026, dollars per 1M tokens. An example: a service handles 1,000 requests, each with 400 input tokens and 150 output tokens. That is 0.4M input tokens and 0.15M output tokens.

ModelInput / outputFormulaTotal
deepseek-v4.1-flash0.013 / 0.0380.4 × 0.013 + 0.15 × 0.038$0.0109
gpt-6.1-sol0.27 / 1.320.4 × 0.27 + 0.15 × 1.32$0.306
claude-sonnet-5-50.4 / 20.4 × 0.4 + 0.15 × 2$0.46

For Claude through langchaingo and the chat/completions format, all input is billed at the input price, and the cache discount works in the Anthropic format. For GPT, cache reads are visible in both formats. The current list of models and prices is GET https://api.zukko.pro/v1/models, and format details are in the chat/completions reference. So a test loop does not overspend, create a separate key with a daily limit.

If something does not work

Errors come from the library as a regular error with the server's response text. Go by the meaning:

  • 401: wrong key. Check the fetch_ prefix and that the environment variable is set in the process that launches the program.
  • 402: not enough balance.
  • 403 model_not_allowed_for_key: the model is not in the key's allowed list.
  • 404 model_not_found: wrong model name, compare with GET /v1/models. If 404 comes on every request, check the address: it must end in /v1.
  • 503 model_disabled: the model is turned off, switch to another from the list.

Access from Russia goes through the gateway, with payment by SBP (the payment page adds 8.5% on top), MasterCard, or crypto. Error breakdown: 401, 402, model not found.

FAQ

Can I use Claude or DeepSeek in langchaingo, not just GPT?

Yes. The gateway accepts any of its models in the OpenAI format, so changing the string in WithModel is enough. Before switching, check that the feature you need (tools, JSON mode) works with the chosen model.

Do I need /v1 in the address?

Yes, for the OpenAI format the address is https://api.zukko.pro/v1. Without /v1 requests go to the wrong path.

How do I find out how many tokens a request used?

Read GenerationInfo in the response: the PromptTokens, CompletionTokens and PromptCachedTokens fields. See spend history in the dashboard under "Usage" or via GET /v1/usage.

Does langchaingo have a ready-made agent with tools?

The library has agents and tools, but their set and API have changed, so look at the examples in the repository's examples folder for your version.

zukko

Pay 25×
less

for Claude Code & ChatGPT

One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.

Get API keyDocumentation

api.zukko.pro · fetch_*

LangChain Go: connecting to an OpenAI-compatible API · Zukko