Skip to content

Clients

The native gRPC endpoint opens soon. Today the JSON API is served, and the client libraries on this page give you type-safe requests and responses for it: every message, enum and service of the published definitions, sent as the JSON the API takes and read back from its JSON answers — by Connect in TypeScript, and by protobuf’s JSON mapping in Java (JsonFormat), Python (json_format) and Go (protojson).

Language What you install Where from
TypeScript @voplica/connect npmjs.com, or GitHub Packages
Java com.voplica:voplica-api Maven Central, or GitHub Packages
Python voplica-api PyPI
Go the code you generate with buf the definitions in voplica/api
Any language an HTTP client, for the JSON API —

Every package carries the API’s version, today 1.0.1, and the install lines below pin it; read a version’s entry in Versioning and changelog before you upgrade. Call the API with your key from a server only (Authentication).

@voplica/connect is the TypeScript client of the Voplica API: every message, enum and service of the published definitions, for Connect clients in Node.

From npmjs.com, with no .npmrc and no token, install the package, Connect and its Node transport:

Terminal window
npm install --save-exact @voplica/connect@1.0.1 @connectrpc/connect @connectrpc/connect-node

The package is also published to GitHub Packages, which needs a GitHub token with the read:packages scope even for a public package. To install from there:

  1. Create a GitHub personal access token (classic) with the read:packages scope, and put it in the environment variable GITHUB_TOKEN.

  2. Add these two lines to your project’s .npmrc:

    .npmrc
    @voplica:registry=https://npm.pkg.github.com
    //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
  3. Run the same npm install.

The .npmrc reads the token from the environment, so the file holds no secret. You can also generate the same code yourself from the published definitions: see Without the package below.

One transport carries every call; an interceptor adds your key to each one. Your project’s package.json needs "type": "module", and Node 22.18 or later runs this file as it is (node client.ts):

client.ts
import { Code, ConnectError, createClient, type Interceptor } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-node";
import { ModelFamily, ModelVariantService, PaymentService } from "@voplica/connect";
// The key comes from the environment, never from your code.
const apiKey = process.env.VOPLICA_API_KEY;
if (!apiKey) {
console.error("Set VOPLICA_API_KEY to your API key first.");
process.exit(1);
}
const withApiKey: Interceptor = (next) => (request) => {
request.header.set("Authorization", `Bearer ${apiKey}`);
return next(request);
};
const transport = createConnectTransport({
baseUrl: "https://api.voplica.com",
httpVersion: "1.1",
useBinaryFormat: false, // send and receive JSON
interceptors: [withApiKey],
});
// One client per service, all on the same transport.
const payments = createClient(PaymentService, transport);
const modelVariants = createClient(ModelVariantService, transport);
try {
const { billingInfo } = await payments.getBillingInfo({});
console.log("Anytime credits, in thousandths:", billingInfo?.permanentCreditsMilli);
const { modelVariant } = await modelVariants.getRecommendedBaseModelVariant({
compatibleLoraModelFamily: ModelFamily.FLUX_2_KLEIN_9B,
});
console.log("Recommended:", modelVariant?.variantName);
} catch (error) {
if (error instanceof ConnectError && error.code === Code.Unauthenticated) {
console.error("Check your API key:", error.rawMessage);
} else {
throw error;
}
}

Requests are plain objects, enums are the package’s enums, and 64-bit numbers are bigint values.

A call that fails throws a ConnectError. Its code is one of Connect’s codes — the code of the JSON API’s errors — and its rawMessage is the message the API sent:

error.code When
Code.Unauthenticated The key is missing, mistyped, expired or revoked.
Code.PermissionDenied The call is not allowed for your account.
Code.InvalidArgument The request is not valid.
Code.NotFound The item does not exist.
Code.FailedPrecondition A run cannot start, for example without enough Anytime or Earned credits.
Code.Unavailable A temporary failure: retry.

Branch on error.code and log error.rawMessage. The API sends no error details for a client to decode.

Every service, message and enum is exported from the package’s root, as above. When two packages of the API declare the same name, the root exports each under its package’s last segment in PascalCase: if voplica.api.alpha and voplica.api.beta, two packages invented for this example, both declared a message Label, the root would export AlphaLabel and BetaLabel, with their schemas AlphaLabelSchema and BetaLabelSchema. The names renamed today are those of the client’s barrel test, clients/typescript/test/client.test.mjs in voplica/api, and the package’s dist/index.d.ts shows each one as Name as PrefixName. A later version can share a name that is unique today, and the root then renames it the same way; a file imported by its own path keeps its plain names in every version:

import { GetBillingInfoRequestSchema } from "@voplica/connect/voplica/api/payment/payment_pb";

The package is made of ES modules: load it with import, or with require() on Node 22.12 or later.

The package is generated with buf, so you can also generate the same code yourself from the published definitions. Install the generator next to Connect:

Terminal window
npm install @bufbuild/protobuf @connectrpc/connect @connectrpc/connect-node
npm install --save-dev @bufbuild/buf @bufbuild/protoc-gen-es
buf.gen.yaml
version: v2
inputs:
- git_repo: https://github.com/voplica/api.git
tag: v1.0.1
plugins:
- local: protoc-gen-es
out: gen
include_imports: true
opt:
- target=js+dts
- import_extension=js

Run npx buf generate, then import from the generated files, for example import { PaymentService } from "./gen/voplica/api/payment/payment_pb.js";.

com.voplica:voplica-api holds every message, enum and service of the published definitions as Java classes, for Java 17 or later: voplica/api/payment/payment.proto is the package com.voplica.api.payment.

From Maven Central, with no token:

build.gradle.kts
dependencies {
implementation("com.voplica:voplica-api:1.0.1")
}

The package is also published to GitHub Packages, which needs your GitHub user name and a token with the read:packages scope, here in the environment variables GITHUB_USERNAME and GITHUB_TOKEN. Add its repository and keep the same dependency:

build.gradle.kts
repositories {
maven {
url = uri("https://maven.pkg.github.com/voplica/api")
credentials {
username = System.getenv("GITHUB_USERNAME")
password = System.getenv("GITHUB_TOKEN")
}
}
}

JsonFormat, which writes a message as JSON and reads it back, is in com.google.protobuf:protobuf-java-util: add it at the version of the protobuf-java that voplica-api depends on, which ./gradlew dependencies or mvn dependency:tree lists. Then send the messages as JSON with Java’s own HTTP client:

Balance.java
import com.google.protobuf.util.JsonFormat;
import com.voplica.api.payment.GetBillingInfoRequest;
import com.voplica.api.payment.GetBillingInfoResponse;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Balance {
public static void main(String[] args) throws Exception {
String apiKey = System.getenv("VOPLICA_API_KEY");
if (apiKey == null || apiKey.isEmpty()) throw new IllegalStateException("Set VOPLICA_API_KEY to your API key first.");
String body = JsonFormat.printer().print(GetBillingInfoRequest.getDefaultInstance());
HttpRequest request = HttpRequest.newBuilder(
URI.create("https://api.voplica.com/voplica.api.payment.PaymentService/GetBillingInfo"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + apiKey)
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> answer =
HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
if (answer.statusCode() != 200) {
throw new IllegalStateException(answer.statusCode() + " " + answer.body());
}
GetBillingInfoResponse.Builder info = GetBillingInfoResponse.newBuilder();
JsonFormat.parser().ignoringUnknownFields().merge(answer.body(), info);
System.out.println(info.getBillingInfo().getPermanentCreditsMilli());
}
}

ignoringUnknownFields() keeps the call working when a later version of the API adds a field to its response. A failed call answers with its HTTP status and, nearly always, the JSON error, with its code and message (or plain text, for the few answers that are not JSON).

The package also holds each service’s gRPC stub, such as PaymentServiceGrpc. The stubs need the native gRPC endpoint, which opens soon; until then, call the API as JSON, as above.

voplica-api holds every message, enum and service of the published definitions as Python modules, with type stubs, for Python 3.10 or later. It installs the runtimes they need: protobuf>=6.33.5,<8 and grpcio>=1.80.0. The import root is voplica, as in the definitions’ paths: voplica/api/payment/payment.proto is the module voplica.api.payment.payment_pb2.

From PyPI:

Terminal window
pip install voplica-api==1.0.1

call sends a request message as JSON to a method of the API, POST /<package>.<Service>/<Method>, with your key, and reads the answer into a response message; json_format writes and reads the JSON:

client.py
import os
import sys
import urllib.request
from google.protobuf import json_format
from voplica.api.ai.model import model_variant_pb2
from voplica.api.payment import payment_pb2
from voplica.types.ai.common import model_family_type_pb2
API = "https://api.voplica.com"
# The key comes from the environment, never from your code.
API_KEY = os.environ.get("VOPLICA_API_KEY")
if not API_KEY:
sys.exit("Set VOPLICA_API_KEY to your API key first.")
def call(method, request, response):
http_request = urllib.request.Request(
f"{API}/{method}",
data=json_format.MessageToJson(request).encode(),
method="POST",
headers={"Content-Type": "application/json", "Authorization": f"Bearer {API_KEY}"},
)
with urllib.request.urlopen(http_request) as answer:
return json_format.Parse(answer.read(), response, ignore_unknown_fields=True)
info = call(
"voplica.api.payment.PaymentService/GetBillingInfo",
payment_pb2.GetBillingInfoRequest(),
payment_pb2.GetBillingInfoResponse(),
)
print("Anytime credits, in thousandths:", info.billing_info.permanent_credits_milli)
recommended = call(
"voplica.api.ai.model.ModelVariantService/GetRecommendedBaseModelVariant",
model_variant_pb2.GetRecommendedBaseModelVariantRequest(
compatible_lora_model_family=model_family_type_pb2.FLUX_2_KLEIN_9B,
),
model_variant_pb2.GetRecommendedBaseModelVariantResponse(),
)
print("Recommended:", recommended.model_variant.variant_name)

Run it with python client.py. Requests are the generated messages, enums are their modules’ constants, and 64-bit numbers are int values. ignore_unknown_fields=True keeps the calls working when a later version of the API adds a field to a response. A failed call raises urllib.error.HTTPError; its body is the JSON error, with its code and message (or plain text, for the few answers that are not JSON).

Each _pb2_grpc module holds its services’ gRPC stubs, such as PaymentServiceStub. They need the native gRPC endpoint, which opens soon; until then, call the API as JSON, as above.

There is no published Go module: you generate the code from voplica/api with buf, then send the messages as JSON with protojson. Go has the commands and a first call.

The JSON API is one HTTPS POST of a JSON body per method, so any HTTP client can call it. To generate the messages for another language, see Protobuf and gRPC.