Назад в блог

API и SDKИнструкция

Первый запрос на Go через openai-go: base URL, ключ, usage

Готовая программа на Go: вызываем модель CheapTokens через openai-go, печатаем ответ, finish_reason и расход токенов, задаём повторы и таймаут.

Автор: CheapTokens TeamОпубликовано 5 мин чтения

Обложка: первый запрос на Go через SDK openai-go, терминал с go run, ответом, finish_reason и usage
Содержание
  1. Что понадобится
  2. Вся программа
  3. Ответ, finish_reason и usage
  4. Повторы и таймауты: три разных лимита
  5. Сколько стоит тысяча таких вызовов
  6. Если не работает

Допустим, у вас уже есть Go-код для OpenAI-совместимого API, или вы только собираетесь его писать. Чтобы отправить его в CheapTokens, нужно поменять две опции клиента и одну строку, id модели. SDK остаётся тем же: официальный пакет openai-go, v3.

Ниже целая программа, потом разбор строк, чтение ответа и счётчиков токенов и защита от зависшего запроса. Версии сверены с README openai-go 3 ноября 2026 года: последний тег на странице — v3.91.1, нужен Go 1.26 или новее.

Что понадобится#

  • Go 1.26 или новее. На Go 1.25 закрепите v3.74.0, на Go 1.22–1.24 — v3.44.0 (README: у старой ветки нет гарантированных исправлений).
  • Ключ CheapTokens. Храните его в переменной окружения, а не в исходнике.
  • Адрес API: https://api.cheaptokens.dev/v1. Он понимает формат OpenAI Chat Completions, поэтому openai-go работает без обёрток. Тот же адрес и страница с ключом — в документации.
  • Id модели. Берём deepseek-ai/DeepSeek-V4-Flash-0731: контекст 380K и до 32 768 токенов в одном ответе. Нужен полный id; короткое имя из таблиц цен API не принимает.

Шаг за шагом

Подготовьте проект

Установите SDK

В пустой папке:

bash
go mod init example.com/first-request
go get -u 'github.com/openai/openai-go/[email protected]'

Путь импорта — github.com/openai/openai-go/v3, имя пакета — openai.

Положите ключ в окружение

bash
export CHEAPTOKENS_API_KEY="paste-your-key-here"

В Windows PowerShell: $env:CHEAPTOKENS_API_KEY = "paste-your-key-here". Это заглушка, а не настоящий ключ.

Создайте main.go и запустите

Вставьте программу из следующего раздела в main.go и выполните go run ..

Вся программа#

go
package main

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

	"github.com/openai/openai-go/v3"
	"github.com/openai/openai-go/v3/option"
)

func main() {
	client := openai.NewClient(
		option.WithBaseURL("https://api.cheaptokens.dev/v1"),
		option.WithAPIKey(os.Getenv("CHEAPTOKENS_API_KEY")),
		option.WithMaxRetries(2),
		option.WithRequestTimeout(30*time.Second),
	)

	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()

	resp, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
		Model: "deepseek-ai/DeepSeek-V4-Flash-0731",
		Messages: []openai.ChatCompletionMessageParamUnion{
			openai.UserMessage("Explain in two sentences what a token is."),
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	if len(resp.Choices) == 0 {
		log.Fatal("no choices in the response")
	}

	fmt.Println(resp.Choices[0].Message.Content)
	fmt.Println("finish_reason:", resp.Choices[0].FinishReason)
	fmt.Println("input tokens:", resp.Usage.PromptTokens)
	fmt.Println("output tokens:", resp.Usage.CompletionTokens)
	fmt.Println("total tokens:", resp.Usage.TotalTokens)
}
Путь одного запроса: ключ из окружения, клиент openai-go, chat completion, затем content, finish_reason и usage
Что делает программа, по порядку

Что здесь важно:

  • option.WithBaseURL(...) — единственная строка, которая переводит SDK с OpenAI на CheapTokens. Адрес берите ровно как в документации.
  • option.WithAPIKey(os.Getenv(...)) передаёт ключ явно. Без неё клиент читает OPENAI_API_KEY — удобно при переносе готового кода: положите в эту переменную ключ CheapTokens, и код менять не нужно. С OPENAI_BASE_URL так же, но явный WithBaseURL его переопределяет, так что при обеих настройках сюрпризов нет.
  • Model в SDK — обычная строка (ChatModel — алиас string), поэтому подходит любой id модели, а не только константы из README.
  • openai.UserMessage(...) — одна реплика пользователя. В примере README стоят DeveloperMessage и константа GPT; нам хватает пользовательского сообщения и строки с id.

Ответ, finish_reason и usage#

Всё это SDK уже разобрал в поля:

  • resp.Choices[0].Message.Content — текст (string).
  • resp.Choices[0].FinishReason — тоже string. stop — модель закончила сама, length — ответ упёрся в лимит токенов и обрезан. Если видите length, прочитайте почему ответ обрывается на полуслове.
  • resp.Usage — PromptTokens (что вы отправили), CompletionTokens (что написала модель) и TotalTokens, все int64.

На экране будет так: сначала ответ, затем finish_reason: со значением и три счётчика токенов. Числа каждый раз разные, так что логируйте их: именно за них вы платите, причём выходные токены стоят дороже.

Попробуйте самиПопробуйте: что изменится, если сменить модель?Показать ответ

Замените строку Model на zai-org/GLM-5.3-Flash и запустите снова. Остальная программа не меняется. Текст ответа и три числа usage будут другими — это самый быстрый способ сравнить две модели на вашем промпте. У GLM-5.3-Flash контекст 390K и до 8 192 токенов в одном ответе.

Повторы и таймауты: три разных лимита#

Именно здесь Go-сервисы обычно обжигаются: по умолчанию у SDK вообще нет таймаута, и зависшее соединение может ждать вечно. Исправляют это три настройки, и работают они на разных уровнях:

  • option.WithMaxRetries(2): SDK повторяет запрос при ошибках соединения, 408, 409, 429 и 5xx с короткой экспоненциальной паузой. Два повтора — это и так значение по умолчанию; 0 отключает повторы. Задаётся на клиенте или на отдельном вызове.
  • option.WithRequestTimeout(30*time.Second): лимит одной попытки.
  • Дедлайн context: лимит всего вызова вместе с повторами и паузами.

При двух повторах попыток может быть до трёх: 3 × 30 = 90 секунд ожидания в худшем случае плюс короткие паузы между попытками. Контекста на две минуты хватает. Если для веб-обработчика нужно жёсткое «ответить за 20 секунд», сокращайте контекст, а не только таймаут попытки.

Значения по умолчанию и настройки программы: два повтора в обоих случаях, таймаута по умолчанию нет, в программе 30 секунд на попытку
Значения по умолчанию и настройки из программы

О том, как подобрать числа, подробнее — в статье про таймауты и повторы в Python SDK: логика переносится на Go.

Сколько стоит тысяча таких вызовов#

Допустим, небольшой сервис делает 1 000 запросов в день, в каждом около 800 токенов входа и 300 токенов выхода. На deepseek-ai/DeepSeek-V4-Flash-0731 такой день стоит $0.042 (1 000 × 800 токенов входа, 1 000 × 300 токенов выхода). Таблица показывает ту же нагрузку по нашим моделям; в сумме, с учётом входа и выхода, это дешевле OpenRouter.

Модель За запрос 1 000 запросов OpenRouter, 1 000 запросов
GLM-5.3-Flash $0.000028 $0.028 $0.0517
DeepSeek-V4-Flash $0.000042 $0.042 $0.098
MiniMax-M2.7 $0.000085 $0.085 $1.02

800 входных и 300 выходных токенов на запрос. Цены за 1M токенов актуальны и меняются вместе с нашими тарифами; столбец OpenRouter показывает справочные цены для той же нагрузки.

Подставьте свои числа. Первый запуск напечатает настоящие PromptTokens и CompletionTokens, так что среднее гадать не придётся:

Калькулятор стоимости

День небольшого Go-сервиса

Входные токены на запрос: 800 · Выходные токены на запрос: 300 · Запросов в день: 1 000

МодельВ деньВ месяц
GLM-5.3-Flash$0.028$0.84
DeepSeek-V4-Flash$0.042$1.26
MiniMax-M2.7$0.085$2.55

Цены на странице цен и на странице моделей живые: у каждой модели одна цена, без пиковых часов.

Если не работает#

  • В ошибке 401: ключ пустой или неверный. os.Getenv для незаданной переменной возвращает пустую строку, поэтому выполните echo $CHEAPTOKENS_API_KEY в том же терминале, где запускаете go run ..
  • В ошибке 404 или «неизвестная модель»: id. Скопируйте его целиком, с префиксом deepseek-ai/. Список id можно получить запросом списка моделей.
  • go: module requires Go 1.26: тулчейн старше. Закрепите более старый тег SDK, как написано в начале.
  • Вызов висит, потом падает: это таймаут делает свою работу. Сначала проверьте сеть, затем увеличьте WithRequestTimeout, если модели правда нужно больше времени на длинный ответ.

Если идея base URL для вас нова, что такое base_url и как его сменить объяснено без кода.