1. Install the Node.js library
An official client library exists for Node.js, and it handles the auth header and response parsing for you.
npm install gstinapiThen verify a GSTIN in three lines:
import { GstinApi } from 'gstinapi';
const client = new GstinApi({ apiKey: 'gak_your_key_here' });
const result = await client.verify('33AAACC1206D1ZN');
console.log(result.legal_name); // "CHENNAI CORPORATION LIMITED"
console.log(result.status); // "Active"
console.log(result.taxpayer_type); // "Regular"Source on GitHub · package registry
Without the package (native fetch)
The API is a single GET with one header, so you can skip the library entirely if you would rather not add a dependency.
const response = await fetch(
'https://gstinapi.in/v1/gstin/33AAACC1206D1ZN',
{ headers: { 'x-api-key': 'gak_your_key_here' } }
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
console.log(data.legal_name, data.status);2. Validate the GSTIN offline first
A malformed GSTIN returns 400 and is never charged — but you still paid for the round trip. The check digit is computable locally, so reject typos before they leave your server.
const CODES = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
export function isValidGstin(gstin) {
const value = (gstin ?? '').trim().toUpperCase();
if (value.length !== 15) return false;
let total = 0;
for (let i = 0; i < 14; i++) {
const index = CODES.indexOf(value[i]);
if (index === -1) return false;
const product = index * (i % 2 ? 2 : 1);
total += Math.floor(product / 36) + (product % 36);
}
const expected = CODES[(36 - (total % 36)) % 36];
return value[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.
const RETRYABLE = new Set([429, 502]);
const sleep = ms => new Promise(r => setTimeout(r, ms));
export async function verify(gstin, apiKey, attempts = 3) {
for (let attempt = 0; attempt < attempts; attempt++) {
const res = await fetch(`https://gstinapi.in/v1/gstin/${gstin}`, {
headers: { 'x-api-key': apiKey },
});
if (res.ok) return res.json();
if (res.status === 404) return null; // valid format, no such registration
if (res.status === 402) throw new Error('Out of credits — recharge your account');
if (!RETRYABLE.has(res.status)) {
const { error } = await res.json().catch(() => ({}));
throw new Error(error ?? `HTTP ${res.status}`);
}
if (attempt < attempts - 1) await sleep(2 ** attempt * 1000);
}
throw new Error('GST provider unavailable after retries');
}4. Wiring it into Express or Next.js
Read the key from process.env and call the API server-side only — a key shipped to the browser is a key anyone can spend. In Next.js that means a route handler or server action, never a client component. Cache the result against the vendor record rather than re-verifying on every request.
// app/api/verify/route.js
import { GstinApi } from 'gstinapi';
const client = new GstinApi({ apiKey: process.env.GSTIN_API_KEY });
export async function POST(request) {
const { gstin } = await request.json();
const result = await client.verify(gstin);
return Response.json(result);
}Frequently asked questions
Is there a free GST verification API for Node.js?
Yes. Every account gets up to 100 free lookups, and unlike most trials the credits never expire — no clock running against you. No credit card is required to create a key.
Can I call the GST API from the browser?
No — not with your API key. Anything shipped to the browser is readable by the user, and a leaked key spends your credits. Call it from a server route and pass only the result to the frontend. If you want a public-facing lookup with no key, the free search page at /gst-number-search is rate-limited by IP for exactly that reason.
Does the npm package work with TypeScript?
The package is published with the client shown above. If you need strict typing over the raw response, the full field list and every status code are documented on the API reference page.
How do I handle rate limits in Node?
Standard accounts allow 60 requests per minute. When you exceed it you get a 429, which is transient — back off exponentially and retry, capped at about 3 attempts. If you are processing a large vendor list concurrently, cap concurrency well below 60/min rather than firing everything at once.
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.