How to verify a GST number in C# / .NET

There is no .NET SDK — the API is one GET with one header, so HttpClient covers it. This shows the call, a typed model for the response, offline check-digit validation, and the retry policy that matters in production.

Up to 100 free lookups — they never expire. No credit card.

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"); // false

This 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

CodeMeaningWhat to do
400Invalid GSTIN formatNo credit charged. Validate client-side and this never fires.
401Missing or invalid x-api-keyCheck the header name and that the key is not truncated.
402Out of creditsDifferent from 404 — the lookup never ran. Surface a recharge prompt, not "not found".
404GSTIN not registeredA valid-format GSTIN that the GST network has no record of.
429Rate limit exceeded60 requests/minute on standard accounts. Retry with backoff.
502GST provider unavailableUpstream 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.