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).
Five ways in
Section titled “Five ways in”| 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).
TypeScript
Section titled “TypeScript”@voplica/connect is the TypeScript client of the Voplica API: every message, enum and service of the published
definitions, for Connect clients in Node.
Install
Section titled “Install”From npmjs.com, with no .npmrc and no token, install the package, Connect and its Node transport:
npm install --save-exact @voplica/connect@1.0.1 @connectrpc/connect @connectrpc/connect-nodeThe 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:
-
Create a GitHub personal access token (classic) with the
read:packagesscope, and put it in the environment variableGITHUB_TOKEN. -
Add these two lines to your project’s
.npmrc:.npmrc @voplica:registry=https://npm.pkg.github.com//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} -
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.
A client
Section titled “A client”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):
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.
Errors
Section titled “Errors”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.
Imports
Section titled “Imports”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.
Without the package
Section titled “Without the package”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:
npm install @bufbuild/protobuf @connectrpc/connect @connectrpc/connect-nodenpm install --save-dev @bufbuild/buf @bufbuild/protoc-gen-esversion: v2inputs: - git_repo: https://github.com/voplica/api.git tag: v1.0.1plugins: - local: protoc-gen-es out: gen include_imports: true opt: - target=js+dts - import_extension=jsRun 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.
Install
Section titled “Install”From Maven Central, with no token:
dependencies { implementation("com.voplica:voplica-api:1.0.1")}<dependency> <groupId>com.voplica</groupId> <artifactId>voplica-api</artifactId> <version>1.0.1</version></dependency>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:
repositories { maven { url = uri("https://maven.pkg.github.com/voplica/api") credentials { username = System.getenv("GITHUB_USERNAME") password = System.getenv("GITHUB_TOKEN") } }}<repositories> <repository> <id>github</id> <url>https://maven.pkg.github.com/voplica/api</url> </repository></repositories><settings> <servers> <server> <id>github</id> <username>${env.GITHUB_USERNAME}</username> <password>${env.GITHUB_TOKEN}</password> </server> </servers></settings>A call
Section titled “A call”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:
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.
Python
Section titled “Python”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.
Install
Section titled “Install”From PyPI:
pip install voplica-api==1.0.1A client
Section titled “A client”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:
import osimport sysimport urllib.request
from google.protobuf import json_formatfrom voplica.api.ai.model import model_variant_pb2from voplica.api.payment import payment_pb2from 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.
Any other language
Section titled “Any other language”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.