Java library to consume the Vast.ai REST API with an idiomatic API based on java.net.http and Gson.
VastIA4J provides a central HTTP client (VastAIClient) wrapped in a high-level façade (VastAI). This façade instantiates several specialized services (account, offers, instances, templates) and exposes convenient constructors to configure the API key and base URL.
- HTTP client based on
HttpClientwith Gson serialization/deserialization and unified error handling viaVastAIException. - Account service to retrieve the current user and balance (
AccountService). - Offer service to query GPU offers through the legacy (
POST /bundles/) and modern (PUT /search/asks/) endpoints using theOfferQuerybuilder. - Instance service to create, start, stop, restart, label, destroy, or list instances, as well as fetch extended details (
InstanceService). - **Event bus & monitor **: listen to instance state changes, SSH availability, and port-mapping availability.
- Template service (deprecated on the API side) kept for compatibility with
TemplateSearchQueryfilters.
- Copy the
.jarinto alibs/directory of your project. - Add a
flatDirrepository and reference the file:
repositories {
mavenLocal()
flatDir { dirs "libs" }
}
dependencies {
implementation files("libs/vastia4j-<version>.jar")
}💡 Alternative: install the
.jarinto your local Maven repository (mvn install:install-file ...) and consume it viamavenLocal().
You can either reference the file directly or install it into your local repository.
Option 1 — Install into the local Maven repository:
mvn install:install-file \
-Dfile=/path/to/vastia4j-<version>.jar \
-DgroupId=fr.mrqsdf \
-DartifactId=vastia4j \
-Dversion=<version> \
-Dpackaging=jarThen declare the dependency:
<dependency>
<groupId>fr.mrqsdf</groupId>
<artifactId>vastia4j</artifactId>
<version>1.0.0</version>
</dependency>Option 2 — Direct file reference:
<dependency>
<groupId>fr.mrqsdf</groupId>
<artifactId>vastia4j</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/vastia4j-<version>.jar</systemPath>
</dependency>import fr.mrqsdf.vastai4j.VastAI;
VastAI vast = new VastAI("YOUR_API_KEY");
// or
VastAI vastCustom = new VastAI("YOUR_API_KEY", "https://console.vast.ai/api/v0");import fr.mrqsdf.vastai4j.model.AccountBalance;
import fr.mrqsdf.vastai4j.model.CurrentUser;
AccountBalance balance = vast.account().getBalance();
System.out.
println("Available credit: "+balance.credit());
CurrentUser currentUser = vast.account().getClient();
System.out.
println("User: "+currentUser.getUsername());import fr.mrqsdf.vastai4j.query.*;
OfferQuery query = new OfferQuery()
.where(OfferField.GPU_NAME, Op.EQ, "RTX 4090")
.where("dlperf", Op.GT, 2000)
.orderBy(OfferField.PRICE_PER_HOUR_USD, Direction.ASC)
.limit(20);
var offers = vast.offers().searchNew(query);
offers.
forEach(o ->System.out.
printf("%s @ %s$/h%n",o.gpuName(),o.
pricePerHourUSD()));import fr.mrqsdf.vastai4j.model.instance.CreateInstanceRequest;
import fr.mrqsdf.vastai4j.model.instance.RunType;
CreateInstanceRequest req = new CreateInstanceRequest()
.templateHashId("your-template-hash")
.disk(35.0)
.runtypeEnum(RunType.SSH)
.targetState("running")
.label("exp-train-01");
var created = vast.instances().createInstance(offerId, req);
System.out.
println("Created instance id: "+created.newContract());
var details = vast.instances().show(created.newContract());
System.out.
println("SSH: "+details.instances().
sshHost() +":"+details.
instances().
sshPort());VastIA4J ships an EventBus and an InstanceMonitor that polls the Vast API and fires events when an instance:
- changes state (
InstanceStateChangeEvent), - becomes reachable via SSH (
InstanceSshReadyEvent), - exposes a Docker-like port mapping (
InstancePortsMappedEvent).
Priorities match Spigot’s order: LOWEST → LOW → NORMAL → HIGH → HIGHEST → MONITOR. Use MONITOR for read-only/logging handlers.
import fr.mrqsdf.vastai4j.event.*;
import fr.mrqsdf.vastai4j.event.instance.*;
public final class MyInstanceListener implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onState(InstanceStateChangeEvent e) {
System.out.printf("[STATE] #%d %s -> %s | actual %s -> %s%n",
e.instanceId(), e.previousCurState(), e.newCurState(),
e.previousActualStatus(), e.newActualStatus());
}
@EventHandler(priority = EventPriority.NORMAL)
public void onSsh(InstanceSshReadyEvent e) {
System.out.printf("[SSH] #%d %s:%d ready%n",
e.instanceId(), e.sshHost(), e.sshPort());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPorts(InstancePortsMappedEvent e) {
System.out.printf("[PORTS] #%d ip=%s map=%s%n",
e.instanceId(), e.publicIpOrHost(), e.mapping());
}
}import fr.mrqsdf.vastai4j.VastAI;
import fr.mrqsdf.vastai4j.event.EventBus;
import fr.mrqsdf.vastai4j.monitor.InstanceMonitor;
import java.util.concurrent.TimeUnit;
public final class EventsQuickStart {
public static void main(String[] args) throws Exception {
VastAI vast = new VastAI("YOUR_API_KEY");
EventBus bus = new EventBus();
bus.register(new MyInstanceListener());
try (InstanceMonitor monitor = new InstanceMonitor(vast, bus)) {
long instanceId = 123456L;
// poll every 2 seconds
monitor.watch(instanceId, 2, TimeUnit.SECONDS);
// demo: run for 60s
Thread.sleep(60_000);
}
}
}- Use
MONITORfor logging/analytics. - To watch multiple instances, call
monitor.watch(id, …)for each id. Stop withmonitor.stop(id). InstanceMonitorimplementsAutoCloseable— use try-with-resources.
Although the Vast.ai template API is currently unstable, TemplateService remains available:
var myTemplates = vast.templates().searchMyTemplates("diffusion", "created_at");Offer,OfferListResponseAccountBalance,CurrentUserCreateInstanceRequest/CreateInstanceResponseInstanceSummary,InstanceDetailsTemplate,TemplateSearchResponse
- Added EventBus + InstanceMonitor (Spigot-style listeners with priorities).
- Extended instance models and services (create/list/show/manage).
- Offer query builder (
OfferQuery,OfferField,Direction,Op).
- Async dispatcher (thread-pool) & per-event executor.
watchAll()helper to auto-discover instances from/instances/.- Webhooks support if/when provided by Vast.ai.
See src/main/java/.../Main.java for a complete end-to-end sample (balance, offers, instance creation, details, and lifecycle).
sequenceDiagram
autonumber
participant App as Votre application
participant VastAI as VastAI (façade)
participant Offers as OfferService
participant Instances as InstanceService
participant Bus as EventBus / InstanceMonitor
participant API as Vast.ai REST API
App->>VastAI: new VastAI(apiKey)
Note right of VastAI: Initialise VastAIClient, services et EventBus
App->>Offers: offers().search(OfferQuery)
Offers->>API: PUT /search/asks (ou POST /bundles/)
API-->>Offers: List<Offer>
Offers-->>App: Offres (triées selon vos critères)
App->>Instances: createInstance(offerId, CreateInstanceRequest)
Instances->>API: POST /instances (création)
API-->>Instances: CreateInstanceResponse(new_contract)
Instances-->>App: instanceId (= new_contract)
App->>Instances: show(instanceId)
Instances->>API: GET /instances/{id}
API-->>Instances: InstanceDetails (public_ip, ssh_host, ssh_port, ports, ...)
Instances-->>App: Détails de l’instance
App->>Bus: register(listener)
App->>Bus: new InstanceMonitor(vast, bus)
App->>Bus: watch(instanceId, every=2s)
loop Polling
Bus->>API: GET /instances/{id}
API-->>Bus: État/SSH/Ports
Bus-->>App: InstanceStateChangeEvent / InstanceSshReadyEvent / InstancePortsMappedEvent
end
App->>Instances: destroy(instanceId)
Instances->>API: DELETE /instances/{id}
API-->>Instances: success=true
Instances-->>App: Confirmation de destruction