1. The call, with cURL
There is no PHP SDK, and you do not need one — the API is a single GET request with one header.
<?php
$gstin = "33AAACC1206D1ZN";
$ch = curl_init("https://gstinapi.in/v1/gstin/" . urlencode($gstin));
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["x-api-key: gak_your_key_here"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status !== 200) {
throw new RuntimeException("Lookup failed with HTTP $status");
}
$data = json_decode($body, true);
echo $data["legal_name"], " — ", $data["status"];2. Validate the GSTIN offline first
A malformed GSTIN returns 400 and is not charged, but the round trip is wasted. The check digit is computable in PHP, so reject typos before the request goes out.
<?php
function is_valid_gstin(string $gstin): bool
{
$codes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$gstin = strtoupper(trim($gstin));
if (strlen($gstin) !== 15) {
return false;
}
$total = 0;
for ($i = 0; $i < 14; $i++) {
$index = strpos($codes, $gstin[$i]);
if ($index === false) {
return false;
}
$product = $index * ($i % 2 ? 2 : 1);
$total += intdiv($product, 36) + ($product % 36);
}
$expected = $codes[(36 - $total % 36) % 36];
return $gstin[14] === $expected;
}
var_dump(is_valid_gstin("33AAACC1206D1ZN")); // true
var_dump(is_valid_gstin("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.
<?php
function verify_gstin(string $gstin, string $apiKey, int $attempts = 3): ?array
{
$retryable = [429, 502];
for ($attempt = 0; $attempt < $attempts; $attempt++) {
$ch = curl_init("https://gstinapi.in/v1/gstin/" . urlencode($gstin));
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ["x-api-key: $apiKey"],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status === 200) return json_decode($body, true);
if ($status === 404) return null; // valid format, not registered
if ($status === 402) throw new RuntimeException("Out of credits");
if (!in_array($status, $retryable, true)) {
$error = json_decode($body, true)["error"] ?? "HTTP $status";
throw new RuntimeException($error);
}
if ($attempt < $attempts - 1) {
sleep(2 ** $attempt); // 1s, then 2s
}
}
throw new RuntimeException("GST provider unavailable after retries");
}4. Wiring it into Laravel
Put the key in .env and read it through config(), never inline in a controller. Laravel ships an HTTP client that handles retries for you, so the hand-rolled loop above collapses to a couple of lines.
<?php
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders(['x-api-key' => config('services.gstin.key')])
->timeout(10)
->retry(3, 1000, fn ($e, $r) => in_array($r?->status(), [429, 502], true))
->get("https://gstinapi.in/v1/gstin/{$gstin}");
if ($response->status() === 404) {
return null;
}
return $response->throw()->json();Frequently asked questions
Is there a PHP SDK for GST verification?
No, and it would not buy you much — the API is a single GET with one header. The cURL example on this page is the whole integration. Official client libraries exist for Node.js and Python only.
How do I verify a GST number in Laravel?
Use Laravel's HTTP client with the x-api-key header, as shown above. Its retry() helper handles the 429 and 502 backoff, so you do not need the manual loop. Keep the key in .env and read it via config().
Is GST verification free in PHP?
The language makes no difference to pricing. Every account gets up to 100 free lookups that never expire, and paid credit packs start at ₹199 for 250 credits.
Do I get charged for an invalid GSTIN?
No. A malformed GSTIN returns 400 and consumes no credit. A well-formed GSTIN that simply is not registered returns 404 — that lookup did run against the GST network.
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.