1. The call, with HttpClient
There is no C# SDK, and you do not need one — the API is a single GET request with one header.
using System.Net.Http.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "gak_your_key_here");
var result = await client.GetFromJsonAsync<GstinResult>(
"https://gstinapi.in/v1/gstin/33AAACC1206D1ZN");
Console.WriteLine($"{result!.LegalName} — {result.Status}");
public record GstinResult(
[property: JsonPropertyName("gstin")] string Gstin,
[property: JsonPropertyName("legal_name")] string LegalName,
[property: JsonPropertyName("trade_name")] string? TradeName,
[property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("taxpayer_type")] string TaxpayerType,
[property: JsonPropertyName("registration_date")] string? RegistrationDate,
[property: JsonPropertyName("address")] string? 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 const string Codes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static bool IsValidGstin(string? gstin)
{
var value = (gstin ?? string.Empty).Trim().ToUpperInvariant();
if (value.Length != 15) return false;
var total = 0;
for (var i = 0; i < 14; i++)
{
var index = Codes.IndexOf(value[i]);
if (index < 0) return false;
var product = index * (i % 2 == 1 ? 2 : 1);
total += product / 36 + product % 36;
}
var 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.
private static readonly HashSet<int> Retryable = new() { 429, 502 };
public static async Task<GstinResult?> VerifyAsync(
HttpClient client, string gstin, int attempts = 3)
{
for (var attempt = 0; attempt < attempts; attempt++)
{
var res = await client.GetAsync($"https://gstinapi.in/v1/gstin/{gstin}");
if (res.IsSuccessStatusCode)
return await res.Content.ReadFromJsonAsync<GstinResult>();
var status = (int)res.StatusCode;
if (status == 404) return null; // not registered
if (status == 402) throw new InvalidOperationException("Out of credits");
if (!Retryable.Contains(status))
throw new HttpRequestException($"Lookup failed with HTTP {status}");
if (attempt < attempts - 1)
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));
}
throw new HttpRequestException("GST provider unavailable after retries");
}4. Wiring it into ASP.NET Core
Register a named HttpClient in DI rather than newing one up per call — a fresh HttpClient per request exhausts sockets under load. Read the key from configuration, not from source.
builder.Services.AddHttpClient("gstin", client =>
{
client.BaseAddress = new Uri("https://gstinapi.in/");
client.DefaultRequestHeaders.Add(
"x-api-key", builder.Configuration["Gstin:ApiKey"]);
client.Timeout = TimeSpan.FromSeconds(10);
});Frequently asked questions
Is there a .NET NuGet package for GST verification?
Not an official one. The API is a single GET with one header, so HttpClient plus the record type on this page is the entire integration. Official libraries exist for Node.js and Python.
Should I create a new HttpClient per lookup?
No. Creating HttpClient per call exhausts sockets under load — a well-known .NET pitfall. Register it through IHttpClientFactory with AddHttpClient, as shown above, and inject it.
How do I verify GST numbers in bulk from C#?
Iterate your list against the same endpoint, keeping total throughput under the 60 requests/minute limit. If you parallelise, bound it with a SemaphoreSlim rather than firing every task at once, and treat 429 as a signal to back off.
Is the free tier available for .NET?
Yes — the free tier is per account, not per language. Up to 100 lookups: 25 on signup and 25 for each setup step. They never expire, and no credit card is needed.
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.