1. The call, with java.net.http.HttpClient
There is no Java SDK, and you do not need one — the API is a single GET request with one header.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://gstinapi.in/v1/gstin/33AAACC1206D1ZN"))
.header("x-api-key", "gak_your_key_here")
.timeout(Duration.ofSeconds(10))
.GET()
.build();
HttpResponse<String> response =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body()); // JSON: legal_name, status, address ...2. Validate the GSTIN offline first
A malformed GSTIN returns 400 and costs no credit, but the round trip is still wasted. Validate the check digit locally and typos never reach the network.
private static final String CODES = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static boolean isValidGstin(String gstin) {
String value = gstin == null ? "" : gstin.trim().toUpperCase();
if (value.length() != 15) return false;
int total = 0;
for (int i = 0; i < 14; i++) {
int index = CODES.indexOf(value.charAt(i));
if (index < 0) return false;
int product = index * (i % 2 == 1 ? 2 : 1);
total += product / 36 + product % 36;
}
char expected = CODES.charAt((36 - total % 36) % 36);
return value.charAt(14) == expected;
}
isValidGstin("33AAACC1206D1ZN"); // true
isValidGstin("33AAACC1206D1ZZ"); // falseThis proves the GSTIN is well-formed. It cannot tell you whether the registration exists or is still active — only a live lookup does that, because registrations get cancelled.
3. Handle every status code
| Code | Meaning | What to do |
|---|---|---|
| 400 | Invalid GSTIN format | No credit charged. Validate client-side and this never fires. |
| 401 | Missing or invalid x-api-key | Check the header name and that the key is not truncated. |
| 402 | Out of credits | Different from 404 — the lookup never ran. Surface a recharge prompt, not "not found". |
| 404 | GSTIN not registered | A valid-format GSTIN that the GST network has no record of. |
| 429 | Rate limit exceeded | 60 requests/minute on standard accounts. Retry with backoff. |
| 502 | GST provider unavailable | Upstream GSP hiccup. Safe to retry — nothing was charged. |
Retry policy. Retry only on 429 and 502, with exponential backoff, maximum 3 attempts. Never retry 400, 401, 402, 403 or 404 — the answer will not change.
private static final Set<Integer> RETRYABLE = Set.of(429, 502);
/** Returns the JSON body, or null when the GSTIN is not registered. */
public static String verify(HttpClient client, String gstin, String apiKey)
throws IOException, InterruptedException {
int attempts = 3;
for (int attempt = 0; attempt < attempts; attempt++) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://gstinapi.in/v1/gstin/" + gstin))
.header("x-api-key", apiKey)
.timeout(Duration.ofSeconds(10))
.GET()
.build();
HttpResponse<String> res =
client.send(request, HttpResponse.BodyHandlers.ofString());
int status = res.statusCode();
if (status == 200) return res.body();
if (status == 404) return null; // not registered
if (status == 402) throw new IllegalStateException("Out of credits");
if (!RETRYABLE.contains(status))
throw new IOException("Lookup failed with HTTP " + status);
if (attempt < attempts - 1)
Thread.sleep(1000L << attempt); // 1s, then 2s
}
throw new IOException("GST provider unavailable after retries");
}4. Wiring it into Spring Boot
Build one RestClient bean and inject it, rather than creating a client per call. Read the key from configuration or an environment variable, never from source. Spring maps the JSON onto a record for you, so no parsing code is needed.
@JsonIgnoreProperties(ignoreUnknown = true)
public record GstinResult(
String gstin,
@JsonProperty("legal_name") String legalName,
@JsonProperty("trade_name") String tradeName,
String status,
@JsonProperty("taxpayer_type") String taxpayerType) {}
@Configuration
class GstinConfig {
@Bean
RestClient gstinClient(@Value("${gstin.api-key}") String apiKey) {
return RestClient.builder()
.baseUrl("https://gstinapi.in")
.defaultHeader("x-api-key", apiKey)
.build();
}
}
// In a service:
GstinResult result = gstinClient.get()
.uri("/v1/gstin/{gstin}", gstin)
.retrieve()
.body(GstinResult.class);Frequently asked questions
Is there a Java library or Maven package for GST verification?
Not an official one. The API is a single GET with one header, so the HttpClient built into Java 11 and later is the entire integration. Official libraries exist for Node.js and Python.
Which Java version do I need?
Java 11 or later for java.net.http.HttpClient. The Spring example uses RestClient, which needs Spring Framework 6.1 or later (Spring Boot 3.2 and up). On older Spring versions RestTemplate or WebClient make the same call.
How do I verify GST numbers in bulk from Java?
Iterate your list against the same endpoint, keeping total throughput under the 60 requests/minute limit. If you parallelise, bound it with a Semaphore or a fixed-size executor rather than submitting every task at once, and treat 429 as a signal to back off.
Can I validate a GSTIN in Java without calling an API?
You can validate the format and the check digit offline with the method on this page. That catches typos for free. It cannot tell you whether the GSTIN is registered or still active, which needs a live lookup.
The same guide in another language
Ready to integrate?
Create an account, generate a key, and you start with 25 free lookups — up to 100 once you finish the setup steps. No card, and they never expire.
Need to check a single GSTIN right now? Use the free search tool — no signup.