Skip to content

Go

There is no published Go module: you generate the Go code yourself with buf, from the voplica/api repository, which holds the configuration for Go. The native gRPC endpoint opens soon; today the JSON API is served, and the generated messages give you type-safe requests and responses for it: protojson turns a request into the JSON body of a call, and the JSON answer back into a response.

You need git, Go 1.25 or later, and the buf CLI, 1.36 or later.

  1. Check out the definitions at the version you want, today v1.0.1:

    Terminal window
    git clone --depth 1 --branch v1.0.1 https://github.com/voplica/api.git
    cd api
  2. From the repository’s root, generate the Go code with the configuration in clients/go/buf.gen.yaml:

    Terminal window
    buf generate --template clients/go/buf.gen.yaml

    It writes a Go package for each package of the definitions under clients/go/gen/, for example github.com/voplica/api/clients/go/gen/voplica/api/payment: the messages and enums, made by protoc-gen-go, and the gRPC client and server of each service, made by protoc-gen-go-grpc. A later run replaces what an earlier one wrote.

    The two generators run on the Buf Schema Registry, at the versions the configuration pins. Without an account it allows 10 buf generate runs an hour from one address; buf registry login, with a free account, raises the limit.

  3. clients/go/ is already a Go module, github.com/voplica/api/clients/go, with its go.mod and go.sum, so the generated code builds at once:

    Terminal window
    cd clients/go
    go build ./...

Your module uses the generated one through a replace directive. From the directory that holds the api checkout:

Terminal window
mkdir balance && cd balance
go mod init example.com/balance
go mod edit -replace=github.com/voplica/api/clients/go=../api/clients/go

Save this as main.go in your module. call sends a request message as JSON to a method of the API, by its full name <package>.<Service>/<Method>, and reads the answer into a response message:

main.go
package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"os"
"time"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"github.com/voplica/api/clients/go/gen/voplica/api/ai/model"
"github.com/voplica/api/clients/go/gen/voplica/api/payment"
"github.com/voplica/api/clients/go/gen/voplica/types/ai/common"
)
const api = "https://api.voplica.com"
var client = &http.Client{Timeout: 30 * time.Second}
// call sends request as JSON to a method of the API and reads the answer into response. apiKey is empty for a method
// that needs no key.
func call(method string, request, response proto.Message, apiKey string) error {
body, err := protojson.Marshal(request)
if err != nil {
return err
}
httpRequest, err := http.NewRequest(http.MethodPost, api+"/"+method, bytes.NewReader(body))
if err != nil {
return err
}
httpRequest.Header.Set("Content-Type", "application/json")
if apiKey != "" {
httpRequest.Header.Set("Authorization", "Bearer "+apiKey)
}
answer, err := client.Do(httpRequest)
if err != nil {
return err
}
defer answer.Body.Close()
data, err := io.ReadAll(answer.Body)
if err != nil {
return err
}
if answer.StatusCode != http.StatusOK {
return fmt.Errorf("%s: %s", answer.Status, data)
}
// A later version of the API may add fields: skip the ones this code does not know.
return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(data, response)
}
func main() {
// A method that needs no key: the model variant Voplica recommends for LoRAs of a family.
recommended := &model.GetRecommendedBaseModelVariantResponse{}
request := &model.GetRecommendedBaseModelVariantRequest{
CompatibleLoraModelFamily: common.ModelFamily_FLUX_2_KLEIN_9B.Enum(),
}
if err := call("voplica.api.ai.model.ModelVariantService/GetRecommendedBaseModelVariant", request, recommended, ""); err != nil {
log.Fatal(err)
}
fmt.Println("Recommended:", recommended.GetModelVariant().GetVariantName())
// A method that needs your key. The key comes from the environment, never from your code.
apiKey := os.Getenv("VOPLICA_API_KEY")
if apiKey == "" {
log.Fatal("Set VOPLICA_API_KEY to your API key first.")
}
info := &payment.GetBillingInfoResponse{}
if err := call("voplica.api.payment.PaymentService/GetBillingInfo", &payment.GetBillingInfoRequest{}, info, apiKey); err != nil {
log.Fatal(err)
}
fmt.Println("Anytime credits, in thousandths:", info.GetBillingInfo().GetPermanentCreditsMilli())
}

Then, in the shell where you set VOPLICA_API_KEY, let Go resolve the imports and run it:

Terminal window
go mod tidy
go run .

It prints the name of the recommended model variant, then your Anytime credits in thousandths of a credit.

  • Requests are the generated structs. An optional field takes a pointer, so an enum value goes in through its Enum() method, as common.ModelFamily_FLUX_2_KLEIN_9B.Enum() above.
  • Answers are read with DiscardUnknown, so the code keeps working when a later version of the API adds a field.
  • 64-bit numbers, which the API sends as JSON strings, arrive in the structs’ int64 and uint64 fields.

A call that fails answers with an HTTP status other than 200, and call returns that status with the body. The body is nearly always the JSON error, with its code and message (the JSON API’s errors); a few refusals are plain text (answers that are not JSON), to be read by their status.

The generated code also holds each service’s gRPC client, such as payment.NewPaymentServiceClient. It needs the native gRPC endpoint, which opens soon; until then, call the API as JSON, as above.

The code you generate is the version of the API you checked out, today v1.0.1. For a later version, check out its tag and generate again; Versioning and changelog lists what each version changed.