Skip to main content
~/projects / wazirx-connector-java

WazirX Java Connector

maintained

An open-source unofficial REST/WebSocket client for the WazirX crypto exchange in Java, acknowledged by WazirX's co-founder.

tech = [Java]
updated =

After building the Go connector for WazirX, I wanted the same thing on the JVM, but WazirX doesn’t publish an official Java client at all, REST or WebSocket. The REST half was a fairly direct port of what I’d already learned building the Go client. The WebSocket half turned out to be its own project.

Letting callers choose their failure granularity

For errors, I didn’t want every caller forced into one giant catch-all exception, but I also didn’t want to force a try/catch at every single call site. I split errors into two, with one extending the other:

public class WazirxApiException extends RuntimeException { ... }
public class WazirxClientException extends WazirxApiException { ... }

WazirxClientException covers mistakes the caller can fix, such as a bad side value or a missing required field, caught before a request is even sent. WazirxApiException covers everything that happens once a request leaves the process: network failures, non-2xx responses. Because one extends the other, a caller who only cares about “did something go wrong” can catch the parent; a caller who wants to distinguish “I made a mistake” from “the network or API failed” can catch both separately.

Keeping a WebSocket connection alive

The REST client was the easy part. WazirX’s WebSocket stream needed periodic pings to stay open, and I didn’t want that logic tangled into the message-handling code. I pulled it into its own thread, started when the socket opens and stopped when it closes or errors:

@Override
public void onOpen(ServerHandshake handshakedata) {
    sendPing = true;
    pingThread = new Thread(new PingMessage(this));
    pingThread.setDaemon(true);
    pingThread.start();
}

@Override
public void onClose(int code, String reason, boolean remote) {
    stopPing();
}

Making it a daemon thread mattered. I didn’t want a forgotten open socket to keep the JVM alive after everything else had shut down.

Not re-authenticating on every subscribe

Private streams (account updates, order updates, your own trades) require an auth token, fetched via a REST call. My first pass fetched a fresh token on every single private subscription, which meant subscribing to three private streams in a row meant three redundant round-trips for a token that hadn’t actually expired.

I fixed it by caching the token and checking its stated expiry before deciding whether to refresh:

private synchronized JsonObject refreshAuthTokenIfNeeded() {
    if (authToken != null) {
        int timeout = authToken.get("timeout_duration").getAsInt();
        long initTime = authToken.get("timestamp").getAsLong();
        if ((System.currentTimeMillis() - initTime) < ((long) timeout * 1000)) {
            return authToken;
        }
    }
    // fetch and cache a new token
}

It’s a small thing, but it turned “every private subscribe hits the REST API” into “the REST API gets hit only when the cached token is actually about to expire.”

Auditing the client against the real API automatically

I built the same checking tool for this connector as for the Go one : a Claude Code skill, wazirx-api-sync, that clones WazirX’s public Postman collection and walks it against the client, checking that every parameter’s name, type, and placement in the request actually match what the API expects, rather than trusting it stayed correct as the API evolved.

Running it here caught the same class of bug as on the Go side: cancel_order, cancel_open_orders, and withdraw were sending their parameters in the wrong part of the request — query string instead of body, or vice versa — which would simply fail against the real API. It also caught a type mismatch (withdrawHistory’s status was an Integer when the API expects a string like "FAILED") and a handful of missing optional parameters. All fixed in one pass, cross-checked endpoint by endpoint against the collection rather than against my own assumptions about what the API does.

Where it’s landed

Like the Go connector, this library has been acknowledged by WazirX’s co-founder, and I’ve kept it updated with new endpoints as the exchange’s API has evolved on both the REST and WebSocket sides.