API Documentation

v1

One key, one header, and every response in the same shape. Most people are making live calls within five minutes.

Base URL
https://www.gstinapi.in
Authentication
x-api-key: gak_…
Rate limit
60 req / min
Format
JSON · HTTPS

Environment

Sandbox pre-fills the free test GSTIN and turns on test_mode for e-way bill / e-invoice calls, so nothing is charged or filed. Live uses your own data and credits. Applies to every console and code sample below.

All endpoints & sections

Start here#

  1. 1

    Get your key

    It is on the API Keys page. New accounts get up to 75 free credits — 10 on signup, 65 more across the setup steps — no card needed, and they never expire.

  2. 2

    Make one call

    Paste this in a terminal, with your key swapped in.

    bash
    curl "https://www.gstinapi.in/v1/gstin/33AAACC1206D1ZN" \
      -H "x-api-key: gak_your_api_key_here"
  3. 3

    Branch on success

    It is true or false on every response, success or failure. That is the only check your code strictly needs.

What you can ask

Integrate using AI — no coding needed

Copy the prompt below and paste it into ChatGPT, Claude, Cursor, GitHub Copilot or any AI coding assistant. It contains everything needed to write a working integration — you only have to say what your project is.

  1. Click Copy prompt, then paste it into your AI assistant.
  2. Replace MY PROJECT with what you are building.
  3. Paste your API key where the assistant tells you to — take it from the API Keys page.
I want to add GSTIN (Indian GST number) verification to my project using the
gstinapi.in REST API. Please write the integration code for me.

MY PROJECT: <describe it — e.g. "a Node.js Express app", "a WordPress site",
"a Google Sheet", "a Python script", "a React form">

IF MY PROJECT IS NODE.JS OR PYTHON, USE THE OFFICIAL LIBRARY INSTEAD OF RAW HTTP
Node.js : npm install gstinapi   ->  const { GstinApi } = require('gstinapi')
Python  : pip install gstinapi   ->  from gstinapi import GstinApi
Both take { apiKey } / (api_key=...), expose .lookup(gstin), and already handle
the retry rules and checksum validation described below. For any other language,
call the REST endpoint directly using the details below.

API DETAILS
Base URL   : https://www.gstinapi.in
Endpoint   : GET /v1/gstin/{gstin}
Auth       : header  x-api-key: <MY_API_KEY>
Rate limit : 60 requests per minute per key

EXAMPLE REQUEST
curl "https://www.gstinapi.in/v1/gstin/33AAACC1206D1ZN" -H "x-api-key: MY_API_KEY"

SUCCESS RESPONSE (HTTP 200)
{
  "success": true,
  "gstin": "33AAACC1206D1ZN",
  "credits_remaining": 487,
  "response_ms": 312,
  "data": {
    "gstin": "33AAACC1206D1ZN",
    "legal_name": "CENTRAL WAREHOUSING CORPORATION",
    "trade_name": "CENTRAL WAREHOUSING CORPORATION",
    "status": "Active",
    "taxpayer_type": "Regular",
    "business_constitution": null,
    "registration_date": "2017-07-01",
    "cancellation_date": null,
    "state_code": "33",
    "state_jurisdiction": null,
    "address": "No.4, Thiruvalar Illam, North Avenue, Srinagar Colony, Saidapet, Chennai",
    "city": "Saidapet, Chennai",
    "address_details": {
      "building_number": "No.4",
      "building_name": "Thiruvalar Illam",
      "floor": null,
      "street": "North Avenue",
      "locality": "Srinagar Colony, Saidapet, Chennai",
      "district": null,
      "city": null,
      "state": null,
      "landmark": null,
      "pincode": "600015"
    },
    "pincode": "600015",
    "nature_of_business": null,
    "block_status": "Unblocked"
  }
}

ERROR RESPONSE — same shape for every failure
{ "success": false, "error": "human readable reason" }

STATUS CODES
200  success, 1 credit charged
400  invalid GSTIN format — no credit charged
401  missing or invalid x-api-key
402  out of credits
403  account deactivated
404  GSTIN not registered in the GST database
429  rate limit exceeded
502  GST provider temporarily unavailable — safe to retry

REQUIREMENTS
1. Read the API key from an environment variable. Never hardcode it, and never
   expose it in browser/client-side code — call the API from my server.
2. Validate the format before calling so a typo does not waste a request:
   ^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$
   Uppercase the input first.
3. Handle every status code above with a clear message for the end user.
   Treat 404 (GSTIN does not exist) and 402 (out of credits) as different things.
4. On 429 and 502 only, retry with exponential backoff, maximum 3 attempts.
   Never retry a 400/401/402/403/404.
5. business_constitution, state_jurisdiction and nature_of_business are null
   on the basic lookup. Append ?include=profile to populate them (same credit
   cost) — that also adds einvoice_status ("Yes"/"No"), jurisdiction codes,
   last_updated and additional_addresses, plus a top-level profile_complete
   boolean.
6. city is the registered address's locality as the GST network records it,
   not always a clean single city name — it can be a locality within a metro
   (e.g. the example above returns "Saidapet, Chennai", not just "Chennai"),
   and can itself contain more than one comma-separated part. Do not treat it
   as a strict city-only field without validating against your own address
   data.
7. Only the principal place of business is returned. GSTINs with multiple
   registered addresses (additional places of business) are not covered by
   this endpoint.
8. address_details is the same address split into parts, for filling a
   structured address form. Use it instead of parsing the address string.
   Any part the GST network has no value for is null — floor, landmark and
   district are blank for a lot of real registrations, so render around the
   nulls rather than assuming every part is present. district and city are
   filled only with ?include=profile. There is no latitude/longitude: the
   GST network does not publish coordinates for a registered address.
9. Log credits_remaining after each call so I can watch my balance.

Tell me exactly which file to create, where to put my API key, and how to run it.

Your API key is a password — keep it on your server. Anyone who has it can spend your credits.

Official client libraries#

Zero dependencies, typed, and they verify the GSTIN checksum locally — so a mistyped number fails instantly instead of spending a credit on a request that cannot succeed.

bash
npm install gstinapi
node
const { GstinApi } = require('gstinapi');

const client = new GstinApi({ apiKey: process.env.GSTIN_API_KEY });

const result = await client.lookup('33AAACC1206D1ZN');
console.log(result.data.legal_name);   // CENTRAL WAREHOUSING CORPORATION
console.log(result.credits_remaining); // 9

Using another language? Every example below is plain HTTP — no SDK required.

Step-by-step integration guides

One language each, start to finish — including validating a GSTIN offline so a typo never costs a credit.

Google Sheets#

Check GST numbers straight from a spreadsheet. No code and no server: paste one script into your sheet and use a formula.

bash
=GSTIN(A2)                 legal name
=GSTIN(A2, "status")       Active / Cancelled / Suspended
=GSTIN(A2:A100, "status")  a whole column in one formula
=GSTIN(A2, "all")          name, trade name, status, type, registered on, state, pincode
=GSTIN_VALID(A2)           TRUE or FALSE, offline and free
  1. Create a free key on the sign-up page, then copy it from API keys.
  2. In your sheet, open Extensions, then Apps Script.
  3. Replace the contents of Code.gs with this script and save.
  4. Reload the sheet. In the new GSTIN API menu choose Set API key and paste your key. Google asks you to authorise the script once.

One credit per distinct GSTIN that exists. A wrong format or a wrong check character is caught inside the sheet, so it is free; not-found and failed lookups are free too. If a cell still shows NO_KEY after you set the key, click it and press Enter.

Base URL#

https://www.gstinapi.in

Authentication#

Pass your API key in every request using the x-api-key header.

bash
x-api-key: gak_your_api_key_here

You can also pass it as a query param: ?api_key=gak_... (less secure)

Endpoints

GSTIN lookups

Everything about a GST registration, from one key. A successful lookup costs one credit; a malformed GSTIN, an unregistered one, or a provider outage costs nothing.

Endpoints

Verify a GSTIN#

GEThttps://www.gstinapi.in/v1/gstin/{gstin}

The main endpoint: who a GSTIN belongs to, and whether it is still active. Try it in the browser first →

ParameterTypeDescription
gstinstring15-character GSTIN (case insensitive)

Verify a GSTIN

Code Examples#

curl
curl -X GET \
  "https://www.gstinapi.in/v1/gstin/22AAAAA0000A1Z5" \
  -H "x-api-key: gak_your_api_key_here"

Verify a GSTIN

Success Response#

json
{
  "success": true,
  "gstin": "22AAAAA0000A1Z5",
  "credits_remaining": 487,
  "response_ms": 312,
  "data": {
    "gstin": "22AAAAA0000A1Z5",
    "legal_name": "EXAMPLE PRIVATE LIMITED",
    "trade_name": "EXAMPLE PVT LTD",
    "status": "Active",
    "taxpayer_type": "Regular",
    "business_constitution": null,
    "registration_date": "2017-07-01",
    "cancellation_date": null,
    "state_code": "22",
    "state_jurisdiction": null,
    "address": "SHOP NO. 12, 1ST FLOOR, 123 BUSINESS PARK, RAIPUR",
    "city": "RAIPUR",
    "address_details": {
      "building_number": "SHOP NO. 12",
      "building_name": "123 BUSINESS PARK",
      "floor": "1ST FLOOR",
      "street": null,
      "locality": "RAIPUR",
      "district": null,
      "city": null,
      "state": null,
      "landmark": null,
      "pincode": "492001"
    },
    "pincode": "492001",
    "nature_of_business": null,
    "block_status": "Unblocked"
  }
}

The data schema is stable and identical for every lookup. Fields not available for a given GSTIN are returned as null. Dates are ISO format (YYYY-MM-DD). address and city only cover the principal place of business — additional registered addresses under the same GSTIN are not returned. city is the locality as the GST network records it, not a normalized city name — for a metro address it is often a locality within the city (for example "Saidapet, Chennai", not just "Chennai") and can itself contain more than one comma-separated part.

monitoring_hint appears only when this key has already looked up the same GSTIN before and is not watching it — { gstin, times_checked, message, url }. It means you are polling a vendor by hand; a watchlist re-checks it for you and emails you on change (see Watchlists). Absent otherwise; safe to ignore.

Building a structured address form? Use address_details

Every lookup also returns address_details — the same principal place of business split the way the GST network itself stores it: building_number, building_name, floor, street, locality, district, city, state, landmark, pincode. No extra credit, and address is unchanged — use whichever suits you.

Every key is always present, so the shape never varies. A null part means the GST network holds no value for it, not that we dropped it — floor, landmark and district are blank for a large share of real registrations, so design the form around missing parts. district and city are filled only when you pass ?include=profile. Latitude and longitude are not available: the GST network does not publish coordinates for a registered address, and we do not geocode.

Null on the basic lookup: business_constitution, state_jurisdiction and nature_of_business. The keys are always present so the schema never changes. Add ?include=profile (below) to populate them.

Want more detail? Add ?include=profile

Same endpoint, same credit — the response additionally carries the taxpayer’s profile straight from the GST network. It fills the three null fields above and adds six more:

einvoice_status“Yes” / “No” — whether this taxpayer is enabled for e-invoicing. Useful before accepting a B2B invoice without an IRN.
state_jurisdiction_codee.g. “GJ068”
centre_jurisdictionCentre range, e.g. “RANGE-IV”
centre_jurisdiction_codee.g. “VC0104”
last_updatedWhen the GST network last updated this registration record
additional_addressesAdditional places of business, when registered

It also fills address_details.district and address_details.city, which the basic lookup leaves null — its source has no field for either.

The response then carries profile_complete: true. If the profile source is momentarily unavailable, the verification still succeeds — the extra keys come back as null and profile_complete is false.

Verify a GSTIN

Error Response#

Every failure returns the same shape, so you can branch on success and surface error directly.

json
{
  "success": false,
  "error": "Invalid GSTIN format. Must be 15 characters in pattern: 22AAAAA0000A1Z5"
}

Endpoints

Return Filing Status#

GEThttps://www.gstinapi.in/v1/gstin/{gstin}/returns

Last filed return period, type, status and date for a GSTIN, one entry per return filed in the given financial year. Same auth, credits and rate limit as the main endpoint.

ParameterTypeDescription
gstinstring15-character GSTIN (case insensitive)
fystring, requiredFinancial year, format YYYY-YY (e.g. 2025-26)
typestring, optionalFilter to one return type, e.g. R1 for GSTR-1

return_period is YYYY-MM, the month the return covers. An empty returns array means no return was filed for that GSTIN in that financial year. For whether a GSTIN files Monthly or Quarterly, use the dedicated /v1/gstin/{gstin}/filing-preference endpoint below.

Endpoints

Return Filing Frequency#

GEThttps://www.gstinapi.in/v1/gstin/{gstin}/filing-preference

Whether a GSTIN is on Monthly or Quarterly (QRMP) return filing, one entry per quarter of the financial year — a taxpayer can switch quarter to quarter, so this is not a single fixed value. Same auth, credits and rate limit as the main endpoint.

ParameterTypeDescription
gstinstring15-character GSTIN (case insensitive)
fystring, requiredFinancial year, format YYYY-YY (e.g. 2025-26)

Endpoints

Complete Profile (bundle)#

GEThttps://www.gstinapi.in/v1/gstin/{gstin}/complete

Verification, return filing history and filing frequency in one call, for 2 credits — cheaper than calling /v1/gstin/{gstin}, /returns and /filing-preference separately (3 credits total). Best-effort on filing history and filing frequency: if either cannot be fetched, those fields come back null rather than failing the whole call — you are only ever charged once verification itself succeeds.

ParameterTypeDescription
gstinstring15-character GSTIN (case insensitive)

Only need one field? /v1/gstin/{gstin}/returns and /v1/gstin/{gstin}/filing-preference above are each 1 credit on their own — use this bundle when you want all three together.

Endpoints

Filing Compliance Summary#

GEThttps://www.gstinapi.in/v1/gstin/{gstin}/compliance

The filing history, already summarised: how many returns of each type were filed in the financial year, the last period filed, how many months ago that was, and how long after each period’s end the taxpayer typically files. One call instead of fetching/returnsand computing it yourself.

ParameterTypeDescription
gstinstring15-character GSTIN (case insensitive)
fystring, requiredFinancial year, format YYYY-YY (e.g. 2025-26)

avg_filing_lag_days is measured from the end of the return period, not the statutory due date — it is a habit indicator, not a legal late-filing count. A quarterly (QRMP) filer legitimately shows 2–3 month gaps between GSTR-1 periods, so apply your own thresholds with the taxpayer’s filing frequency in mind.

Endpoints

Registered Mobile and Email#

GEThttps://www.gstinapi.in/v1/gstin/{gstin}/contact

The mobile number and email registered against a GSTIN, together with its legal name, status, address, city, state and PIN code read live from the GST network, so you can see whose contact it is. The GST portal’s public search does not show the contact. The same lookup as the Contact option of Bulk GSTIN Check, one GSTIN per call.

  • Paid accounts only: any credit pack or invoice order enables it. Every account can call the test GSTIN 00AAAAA0000A1ZT for a free sample.
  • Price by your best pack: 4 credits on Scale or Business, 6 on Starter. credits_charged in each response says what this call cost.
  • A GSTIN with no contact on record is charged, because the lookup ran (contact_found is false and note says so). Bad format, not-found GSTINs and provider errors are free.
  • Up to 500 charged lookups per account per day, reset at midnight India time; daily_limit in each response shows where you are. For a long list, upload it to Bulk GSTIN Check instead.
ParameterTypeDescription
gstinstring15-character GSTIN (case insensitive)

Response fields

FieldDescription
data.legal_name … data.addressRegistration details from the live GST network, as in the verify endpoint. Null when that lookup failed; gst_details_available is then false.
data.mobile, data.emailThe contact on the registration record, or null when there is none
contact_foundtrue when a mobile or email came back
noteWhy something is empty: "No contact on record", "GST details temporarily unavailable", or both. Null when everything came back.
credits_chargedWhat this call cost (your pack rate)
daily_limitlimit, used and remaining charged lookups for today (India time)

The contact is the one on the GST registration, which may be a consultant’s, or out of date if the business changed its number without amending its registration. A mobile number and email are personal data under Indian law: use them for the business purpose you looked them up for, such as vendor onboarding, know-your-business checks or collections, and not for unsolicited marketing. More on this: GSTIN to contact details.

Endpoints

Usage Stats#

GEThttps://www.gstinapi.in/v1/gstin/stats/me

Today’s usage for the calling API key. Authenticated the same way, and does not consume a credit.

No 400/404/429/502 on this endpoint — there is no input to validate, no upstream call, and no rate limiter here.

PAN lookup

Find every GSTIN under a PAN

The GST portal has a manual Search by PAN page but we found no public API for it. This checks every GST state code a PAN could be registered under, through the same live lookup /v1/gstin/{gstin} uses, and returns only the GSTINs that actually exist.

PAN lookup

Verify a PAN#

GEThttps://www.gstinapi.in/v1/pan/{pan}/verify

Checks that a PAN exists and returns the registered name of its holder. Pick how much you need with level: basic (the registered name, the default), verified (also date of birth, holder category and whether an Aadhaar is linked) or details (also address and contact, as the source returns them). The masked Aadhaar number is never returned. Date of birth is returned as DD/MM/YYYY. The credit price depends on the level and your pack, from the regular credit balance. Nothing is cached.

LevelScale or Business packStarter pack
basic4 credits6 credits
verified6 credits10 credits
details12 credits18 credits

Monitoring & Webhooks

Watch GSTINs for changes

Instead of re-checking vendors by hand, put them on a watchlist. We re-check each one daily, weekly or monthly and deliver a signed webhook within the hour of any change.

Monitoring & Webhooks

Watchlists#

POSThttps://www.gstinapi.in/v1/watchlists

A watchlist is a named group of GSTINs checked on a shared schedule —daily,weekly ormonthly. Create as many as you like; monitoring is priced per distinct GSTIN watched across your account, not per list. Every route on this page accepts the samex-api-keyheader as GSTIN lookups.

GET /v1/watchlists lists yours (withentry_count and 30-day critical-alert count on each).PATCH /v1/watchlists/:id renames, reschedules, pauses or resumes one. DELETE /v1/watchlists/:id removes it and every entry on it.

Monitoring & Webhooks

Add GSTINs to a watchlist#

POSThttps://www.gstinapi.in/v1/watchlists/{id}/entries

Send one GSTIN or an array of them. A GSTIN already on another one of your watchlists adds for free — you only pay quota for distinct GSTINs across the whole account.vendor_code andlabel are optional free text, yours to key back to your own ERP or CRM.

Up to 5,000 rows per request. Bad rows are reported individually and do not block the good ones in the same request. Over your plan’s quota, the whole request is refused with402 and the response carries your current entitlement (used / quota) rather than a bare error.

Bulk import also accepts a CSV body atPOST /v1/watchlists/:id/entries/import(content-type: text/csv, columnsgstin,vendor_code,label, header row optional).

Monitoring & Webhooks

Remove a GSTIN from a watchlist#

DELETEhttps://www.gstinapi.in/v1/watchlists/{id}/entries/{entryId}

entryId is the id returned by GET /v1/watchlists/:id/entries, not the GSTIN itself.

Monitoring & Webhooks

Change timeline#

GEThttps://www.gstinapi.in/v1/watchlists/{id}/events

Every detected change for GSTINs on this watchlist — a status flip, a filing gone stale, an address change. Optional severity andsince query params filter it.

severity is one ofinfo, medium,high, critical. A CSV export of the same data is atGET /v1/watchlists/:id/events.csv.

Monitoring & Webhooks

Register a webhook#

POSThttps://www.gstinapi.in/v1/webhooks

Rather than polling /events, register an HTTPS endpoint and we POST to it as soon as changes are detected — typically within an hour. This is what you would point at an n8n, Zapier or Make webhook trigger.

Must be HTTPS and publicly reachable — no localhost or private IPs. Maximum 3 endpoints per account. Save the secret: you will need it to verify incoming payloads. GET /v1/webhooks lists your endpoints (the secret is returned again on every read), DELETE /v1/webhooks/:id removes one, and POST /v1/webhooks/:id/test fires a signed sample event at it immediately — for testing your receiver before real data flows.

Webhook payload & signature#

Every delivery is a signed POST:

http
POST <your url>
Content-Type: application/json
X-Gstinapi-Event: monitoring.changes
X-Gstinapi-Timestamp: 1767520000
X-Gstinapi-Signature: sha256=7d8f3a...
json
{
  "type": "monitoring.changes",
  "sent_at": "2026-09-04T10:15:00.000Z",
  "events": [
    {
      "id": 4821,
      "gstin": "27AAPFU0939F1ZV",
      "event_type": "status_changed",
      "severity": "critical",
      "old_value": "Active",
      "new_value": "Cancelled",
      "detected_at": "2026-09-03 14:05:00",
      "watchlist": "CA Firm Clients",
      "vendor_code": "V-1001",
      "label": "Sharma Traders"
    }
  ]
}

Up to 75 events per delivery; more pending events arrive in the next batch. Possibleevent_type values: status_changed,taxpayer_type_changed, block_status_changed,cancellation_date_changed, legal_name_changed,trade_name_changed, address_changed,monitoring_started, filing_band_changed.

Verify it with your endpoint’s secret (Node.js):

javascript
const crypto = require('crypto');

function verify(secret, timestamp, rawBody, signatureHeader) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

Use the raw request body, before JSON parsing — the signature is computed over the exact bytes sent. We only mark an event delivered once your endpoint answers 2xx; anything else leaves it pending and the next hourly delivery run retries the whole backlog, so a receiver that was down for a few hours gets everything it missed as soon as it is back. After 24 consecutive failures (roughly a day at the hourly cadence) the endpoint is auto-disabled.

HSN / SAC codes

Classification codes

Official descriptions for goods (HSN) and services (SAC) codes, served from a local copy of the GST portal master in about a millisecond. Descriptions and hierarchy only — no tax rate, because a rate is not a function of the code alone.

HSN / SAC codes

Look up an HSN / SAC code#

GEThttps://www.gstinapi.in/v1/hsn/{code}

The official description for any 2 to 8 digit HSN (goods) or SAC (services) code, plus the broader codes it sits under so you can show a breadcrumb without extra requests. Served from our own copy of the GST portal’s published master — typically under 50 ms.

kind is hsn for goods or sac for services. A malformed code is a 400 and an unknown one a 404 — neither is charged. Every response carriessource.refreshed_atso you can see how current the master is.

No GST rate is returned, deliberately. A rate is not a function of the code alone — the same HSN attracts different rates by condition (branded vs unbranded, price thresholds, end use) under different notifications. The government’s own master publishes no rate column either. We would rather return nothing than a number that could end up wrong in your return.

E-way bill

Generate and manage e-way bills

File a real e-way bill with the government network, then keep it current while the goods move: update the vehicle or transporter, extend its validity, cancel or close it. gstinapi.in owns the credit charge here; the filing itself happens on that network.

E-way bill

Test portal credentials#

POSThttps://www.gstinapi.in/v1/ewaybill/credentials/test

Logs in with your portal API user and does nothing else: no document is created and nothing is charged. Run it once before your first live call; most "Invalid login credentials" (error 108) failures are an API user created for a different GSP. For e-invoice, POST the same body to /v1/einvoice/credentials/test with einv_username. Limited to 3 tests per GSTIN every 15 minutes. How to create the API user: credentials setup guide.

E-way bill

Check an e-way bill payload (free)#

POSThttps://www.gstinapi.in/v1/ewaybill/validate

Free: no portal credentials, no credits, and the government network is never contacted, so you can call it as often as you like. It checks the mistakes that most often get a document refused: document number and date rules, GSTIN format and state codes, pincodes, HSN, value totals, vehicle number format and missing transport details. Any signed-in key works, paid or not.

An error is a rule the government network applies without exception. A warning is a probable mistake, for example a GSTIN with a wrong check character. Passing does not guarantee acceptance: a cancelled GSTIN, a duplicate document and your portal login are only visible to the government network. When a live call is refused, the response carries a likely_causes list built by this same check, but the check never blocks a live call.

E-way bill

Generate an e-way bill#

POSThttps://www.gstinapi.in/v1/ewaybill

Files a real e-way bill against the government network and returns the e-way bill number.

Costs 3 credits per successful e-way bill — not per attempt.

Your GSTIN needs e-way bill API access set up first — a one-time step, separate from this API key:
  1. Log in to the e-way bill portal (ewaybillgst.gov.in) as the taxpayer or transporter for that GSTIN.
  2. Open Registration and choose For GSP.
  3. Select the GSP named in the credentials setup guide.
  4. Create the API username and password there — separate from your normal portal login.
  5. Check the username and password with POST /v1/ewaybill/credentials/test (free, nothing is generated). Nothing needs enabling on our side. Until the test passes, build with test_mode.

gstin_username and password below are the API username and password from step 4, not your gstinapi.in key. Each GSTIN needs its own. Nothing you send here is stored.

FieldTypeDescription
gstinstringYour GSTIN, with e-way bill API access set up
gstin_usernamestringAPI username from that setup
passwordstringAPI password from that setup
payloadobjectOne NIC e-way bill JSON payload

Response fields

FieldDescription
ewaybill_noThe 12-digit e-way bill number issued by the government system
ewaybill_dateWhen it was generated, as the government system returns it
valid_uptoLast date the bill is valid. Empty when Part-B (vehicle) was not given: the bill is not valid for movement until you add it with update-vehicle.
doc_noYour document number, echoed from the payload
distance_kmDistance the government system accepted for the bill
already_existedtrue when a retry found the bill already generated for this document, instead of creating a second one (see safe retries)
billed_to, credits_remaining, free_remainingWhat was charged and what is left

POST /v1/ewaybill/cancel and POST /v1/ewaybill/close take the same credentials plus ewaybill_no. A request the government network rejects (expired registration, malformed payload) is never charged — the exact error_code from the filing system is returned so you know which.

E-way bill

Look up an e-way bill#

POSThttps://www.gstinapi.in/v1/ewaybill/lookup

Direct lookup by document type and number — not date-scoped, so it works regardless of when the document was generated. Useful when a generate call answered “already generated” (see Safe retries below) and you want the real e-way bill number back, without guessing a date to search.

Costs 1 credit per lookup that actually reaches the government network — well under a generate call, since this is a read, not a filing. A clean "no matching document" answer is billed the same as a match, unlike a rejection elsewhere in this API, because it is still a real completed call against rate-limited upstream infrastructure. Only a request that never reaches the government network (missing fields, insufficient credits) is free.

FieldTypeDescription
gstinstringSame GSTIN as generate
gstin_usernamestringSame credentials as generate
passwordstringSame credentials as generate
doc_typestringNIC document type exactly as submitted at generation (e.g. INV, CHL, BIL, BOE)
doc_nostringDocument number exactly as submitted at generation

E-way bill

List bills raised against my GSTIN#

POSThttps://www.gstinapi.in/v1/ewaybill/other-party/list

E-way bill

Update vehicle / Part-B#

POSThttps://www.gstinapi.in/v1/ewaybill/update-vehicle

Updates the vehicle number (and related transit details) on an e-way bill already in transit — for example when the original vehicle breaks down or goods are transhipped mid-journey.

Costs 3 credits per successful update, the same price as generate/cancel/close.

FieldTypeDescription
gstinstringSame GSTIN as generate
gstin_usernamestringSame credentials as generate
passwordstringSame credentials as generate
ewaybill_nostringThe e-way bill number to update
vehicle_nostringNew vehicle number
from_placestringPlace the vehicle is being updated from
from_stateintegerNIC state code
reason_codestringNIC's own reason code — e.g. 1 Break Down, 2 Transhipment, 3 Others, 4 First Time
reason_remark, trans_doc_no, trans_doc_date, trans_modestring, optionalFree-text remark and transporter document details

E-way bill

Update transporter#

POSThttps://www.gstinapi.in/v1/ewaybill/update-transporter

Assigns or changes the transporter on an e-way bill. Only the generator of the e-way bill can change its transporter — this is enforced by the government network.

Costs 3 credits per successful update, the same price as generate/cancel/close.

FieldTypeDescription
gstinstringSame GSTIN as generate
gstin_usernamestringSame credentials as generate
passwordstringSame credentials as generate
ewaybill_nostringThe e-way bill number to update
transporter_idstringThe new transporter's GSTIN (transporter ID)

E-way bill

Extend e-way bill validity#

POSThttps://www.gstinapi.in/v1/ewaybill/extend-validity

Extends the validity of an e-way bill that is about to expire, for example when a consignment is delayed in transit. The government network only allows this within a window around expiry, and requires a Part-B (vehicle) entry to exist first — use update-vehicle beforehand if there is none.

Costs 3 credits per successful extension, the same price as generate/cancel/close.

FieldTypeDescription
gstin, gstin_username, passwordstringSame GSTIN and credentials as generate
ewaybill_nostringThe e-way bill number to extend
from_placestringCurrent place of the consignment
from_stateintegerNIC state code of the current place
from_pincodeintegerPincode of the current place
remaining_distanceintegerKilometres still to travel
extension_reason_codeintegerNIC's own code — 1 Natural Calamity, 2 Law & Order, 4 Transshipment, 5 Accident, 99 Others
extension_remarkstringFree-text reason
consignment_statusstringM (in movement) or T (in transit)
vehicle_nostringRequired when consignment_status is M
transit_type, trans_doc_no, trans_doc_date, trans_mode, address_line1, address_line2, address_line3string, optionaltransit_type (R, W or O) applies when consignment_status is T; the rest are transport document and address details

E-way bill

Get e-way bill details#

POSThttps://www.gstinapi.in/v1/ewaybill/details

Full details of one e-way bill by its number — parties, item list, vehicle-update history. Distinct from Look up an e-way bill above, which looks up by document type + number instead of an already-known e-way bill number.

Costs 1 credit per lookup that actually reaches the government network, same as Look up an e-way bill above — a clean "no such e-way bill" answer is billed the same as a match.

FieldTypeDescription
gstinstringSame GSTIN as generate
gstin_usernamestringSame credentials as generate
passwordstringSame credentials as generate
ewaybill_nostringThe e-way bill number to fetch

E-way bill

Consolidated e-way bill#

POSThttps://www.gstinapi.in/v1/ewaybill/consolidated

Combines several e-way bills moving on one vehicle into a single consolidated e-way bill (trip sheet). You must be the generator or the assigned transporter of every e-way bill on it, and each must still be active.

Costs 3 credits per successful consolidated e-way bill, however many e-way bills it carries. A rejection is never charged.

FieldTypeDescription
gstin, gstin_username, passwordstringSame as generate — the API username and password set up for the GSTIN you act as
ewaybill_nosarrayThe 12-digit e-way bill numbers to combine
from_placestringWhere the vehicle is now
from_stateintegerNIC state code of that place
trans_modestring1 Road, 2 Rail, 3 Air, 4 Ship
vehicle_no, trans_doc_no, trans_doc_datestring, optionalVehicle number for road; transport document number and date (dd/MM/yyyy) for rail, air or ship

E-way bill

Get consolidated e-way bill#

POSThttps://www.gstinapi.in/v1/ewaybill/consolidated/details

The consolidated e-way bill (trip sheet) and every e-way bill on it, by its 10-digit number.

Costs 1 credit per lookup that actually reaches the government network, same as Get e-way bill details above.

FieldTypeDescription
gstin, gstin_username, passwordstringSame as generate
consolidated_ewaybill_nostringThe 10-digit consolidated e-way bill number

E-way bill

Regenerate consolidated e-way bill#

POSThttps://www.gstinapi.in/v1/ewaybill/consolidated/regenerate

Re-issues a consolidated e-way bill after a vehicle change. This is not an in-place edit: it returns a new consolidated e-way bill number and the previous one is cancelled. Only the generator or the current transporter can do this, and only active, unexpired e-way bills carry across.

Costs 3 credits per successful regeneration. A rejection is never charged.

FieldTypeDescription
gstin, gstin_username, passwordstringSame as generate
consolidated_ewaybill_nostringThe current 10-digit consolidated e-way bill number
from_place, from_statestring, integerWhere the vehicle is now, and its NIC state code
trans_modestring1 Road, 2 Rail, 3 Air, 4 Ship
reason_codestringOne character: 1 Break Down, 2 Transhipment, 3 Others, 4 First Time
reason_remarkstringUp to 50 characters
vehicle_no, trans_doc_no, trans_doc_datestring, optionalVehicle number for road; transport document number and date (dd/MM/yyyy) for rail, air or ship

E-way bill

List bills assigned to a transporter#

POSThttps://www.gstinapi.in/v1/ewaybill/transporter/list

The e-way bills assigned to your GSTIN as transporter, by the date they were generated. Optionally narrow to one generator’s GSTIN or state — one filter at a time, not both.

Costs 1 credit per call that actually reaches the government network, including one that returns an empty list.

FieldTypeDescription
gstin, gstin_username, passwordstringSame as generate — the GSTIN acting as transporter
datestringThe date the e-way bills were generated, dd/MM/yyyy
gen_gstinstring, optionalOnly bills generated by this GSTIN
state_codestring, optionalOnly bills whose generator is in this 2-digit state (active bills only; rate-limited upstream)

E-way bill

Reject an e-way bill#

POSThttps://www.gstinapi.in/v1/ewaybill/reject

Rejects an e-way bill another party generated against your GSTIN. Only the recipient can reject a document — never its own generator — and only within the government network's own rejection window, both enforced upstream.

Costs 3 credits per successful rejection, the same price as generate/cancel/close.

FieldTypeDescription
gstinstringSame GSTIN as generate
gstin_usernamestringSame credentials as generate
passwordstringSame credentials as generate
ewaybill_nostringThe e-way bill number to reject — must have been generated by someone else, against your GSTIN

E-way bill

Cancel an e-way bill#

POSThttps://www.gstinapi.in/v1/ewaybill/cancel

Cancels a previously generated e-way bill on the government network.

Costs 3 credits per successful cancellation — the same price as generating one.

FieldTypeDescription
gstinstringYour GSTIN, with e-way bill API access set up
gstin_usernamestringSame credentials as generate
passwordstringSame credentials as generate
ewaybill_nostringThe e-way bill number to cancel
cancel_reason_codestring, optionalDefaults to the government network's own default reason if omitted
cancel_remarkstring, optionalFree-text remark

E-way bill

Close an e-way bill#

POSThttps://www.gstinapi.in/v1/ewaybill/close

Marks an e-way bill as closed once the goods movement it covers has actually happened — different from cancelling one that should never have existed.

Costs 3 credits per successful close.

FieldTypeDescription
gstinstringYour GSTIN, with e-way bill API access set up
gstin_usernamestringSame credentials as generate
passwordstringSame credentials as generate
ewaybill_nostringThe e-way bill number to close
closure_datestringdd/MM/yyyy
remarksstring, optionalFree-text remark

Sandbox / test mode#

Add "test_mode": true to any e-way bill endpoint to build and test your integration before spending a credit or even having your e-way bill API access set up yet. Required fields are still validated, but the real government network is never called and nothing is charged, however low your balance is.

Every reference number returned in test mode is prefixed TEST- so it can never be mistaken for a real government-issued document.
bash
curl -X POST "https://www.gstinapi.in/v1/ewaybill" \
  -H "x-api-key: gak_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "gstin": "24AAAAA0000A1Z5",
    "gstin_username": "your_ewb_api_username",
    "password": "your_ewb_api_password",
    "payload": { "supplyType": "O", "docType": "INV", "docNo": "INV-1001" },
    "test_mode": true
  }'
json
{
  "success": true,
  "test_mode": true,
  "ewaybill_no": "TEST-EWB-9F3A2B10",
  "ewaybill_date": "13/09/2026",
  "valid_upto": "15/09/2026",
  "doc_no": "INV-1001",
  "distance_km": "10",
  "message": "Synthetic response — test_mode, no real e-way bill was generated.",
  "billed_to": "test_mode",
  "credits_remaining": 25,
  "free_remaining": 0
}

Test mode proves your integration end-to-end — authentication, request shape, response shape, billing behaviour. It does not simulate what the government network would actually do with a given payload (a malformed field, an inactive GSTIN), since that validation only exists on the real network.

Safe retries after a timeout#

A government-network round trip can occasionally run long enough to hit this API's own timeout before a response comes back — leaving it genuinely unclear whether the e-way bill was filed. The safe recovery is to retry with the exact same document number, never a new one.

  • /v1/ewaybill — a retry with the same docNo returns the already-generated e-way bill as a normal success, with already_existed: true, billed the same as a fresh one.
  • /v1/ewaybill/lookup — if you would rather check than retry, look the document up directly by doc_type and doc_no.

Retrying with a different document number instead is what actually risks a duplicate filing — the government network has no way to know the two calls were meant to be the same attempt.

Integrate using AI — e-way bill

Same idea as the main AI prompt above, scoped to generating and managing e-way bills — including the one rule that matters most here: how to safely retry after a timeout without risking a duplicate government filing.

  1. Click Copy prompt, then paste it into your AI assistant.
  2. Replace MY PROJECT with what you are building.
  3. Paste your API key and your e-way bill credentials where the assistant tells you to.
I want to add e-way bill generation and management (Indian GST filing) to my
project using the gstinapi.in REST API. Please write the integration code for me.

MY PROJECT: <describe it — e.g. "a Node.js Express app", "a WordPress site",
"a Google Sheet", "a Python script", "a React form">

There is no official client library for this yet (the gstinapi Node.js/Python
packages only cover GSTIN lookup) — call the REST endpoints directly with
whatever HTTP client my language/framework normally uses.

API DETAILS
Base URL   : https://www.gstinapi.in
Auth       : header  x-api-key: <MY_API_KEY>
Rate limit : 60 requests per minute per key

ENDPOINTS
POST /v1/ewaybill                    generate an e-way bill
POST /v1/ewaybill/lookup             find one already generated, by doc_type + doc_no
POST /v1/ewaybill/details            full details of one, by e-way bill number
POST /v1/ewaybill/consolidated       combine several into one consolidated e-way bill (trip sheet)
POST /v1/ewaybill/consolidated/details       read a consolidated e-way bill, by its 10-digit number
POST /v1/ewaybill/consolidated/regenerate    re-issue one after a vehicle change (returns a NEW number)
POST /v1/ewaybill/transporter/list   e-way bills assigned to my GSTIN as transporter, by generated date
POST /v1/ewaybill/other-party/list   e-way bills other parties raised against my GSTIN, by generated date
POST /v1/ewaybill/update-vehicle     update the vehicle / Part-B
POST /v1/ewaybill/update-transporter assign or change the transporter
POST /v1/ewaybill/extend-validity    extend the validity of one about to expire
POST /v1/ewaybill/reject             reject one raised against my GSTIN
POST /v1/ewaybill/cancel             cancel one
POST /v1/ewaybill/close              close one (goods movement completed)

EXAMPLE REQUEST — generate an e-way bill, in sandbox (test_mode)
curl -X POST "https://www.gstinapi.in/v1/ewaybill" \
  -H "x-api-key: MY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "gstin": "24AAAAA0000A1Z5",
    "gstin_username": "ewb_api_username",
    "password": "ewb_api_password",
    "payload": { "supplyType": "O", "docType": "INV", "docNo": "INV-1001" },
    "test_mode": true
  }'

The other endpoints take the same gstin / gstin_username / password plus
ewaybill_no, and their own fields (vehicle_no, transporter_id,
remaining_distance, ...). Send "test_mode": true on any of them to try it free.

SUCCESS RESPONSE (HTTP 200) — generate
{
  "success": true,
  "ewaybill_no": "351000000123",
  "ewaybill_date": "14/09/2026",
  "valid_upto": "16/09/2026",
  "doc_no": "INV-1001",
  "already_existed": false,
  "billed_to": "paid",
  "credits_remaining": 197
}

ERROR RESPONSE — a genuine rejection from the filing system (bad payload,
expired registration) — never charged
{ "success": false, "error": "human readable reason", "error_code": "upstream code, or null" }

STATUS CODES
200  success — real filing, or a synthetic test_mode response
400  missing required fields, or the filing system rejected the document — never charged
401  missing or invalid x-api-key
402  insufficient credits for this call (costs more than 1 credit)
403  account deactivated
429  rate limit exceeded
502  filing system temporarily unavailable — safe to retry, never charged

REQUIREMENTS
1. Read the gstinapi.in API key AND the gstin_username/password from environment
   variables. Never hardcode any of them, and never expose them in
   browser/client-side code — call this API from my server only.
2. Build and test everything with "test_mode": true first. It skips the
   credit check and the real government network entirely, returns a
   synthetic response with every reference number TEST-prefixed, and needs
   no real e-way bill registration — only remove it once ready to
   file for real.
3. Handle every status code above with a clear message. Treat a 400
   rejection (bad data, never charged) differently from a 502 (transient,
   safe to retry) differently from a 402 (buy more credits) — do not lump
   them into one generic "failed" state.
4. On 429 and 502 only, retry with exponential backoff, maximum 3 attempts.
   Never retry a 400/401/402/403.
5. The single most important rule: if a request times out and you cannot
   tell whether the document was actually filed, retry with the EXACT SAME
   docNo — never a new one. The API detects this and returns the real
   original document with already_existed: true, billed the same as a fresh
   success. Retrying with a different number is what actually risks a
   duplicate government filing.
6. Parse every response leniently. New fields are added without notice;
   existing fields are never removed or renamed without a new API version.
   Ignore unknown keys rather than validating with a strict/closed schema.
7. My GSTIN needs e-way bill API access set up once, separately from this
   API key, before any non-test_mode call will work — remind me to contact
   gstinapi.in support if a real (non-test_mode) call fails because of this.
8. Log credits_remaining after each real call so I can watch my balance.

Tell me exactly which file to create, where to put my credentials, and how to run it.

Your API key and your e-way bill credentials are passwords — keep them on your server. Anyone who has them can spend your credits or file on your behalf.

E-invoice

Generate and cancel e-invoices (IRN)

Register an invoice with the government e-invoice system and get back its IRN, acknowledgement and signed QR code. Include the transport details in the same call to get the e-way bill too, for one charge. gstinapi.in owns the credit charge; the filing itself happens on that network.

E-invoice

Generate an e-invoice#

POSThttps://www.gstinapi.in/v1/einvoice

Registers the invoice with the government e-invoice system and returns its IRN, acknowledgement and signed QR code. Put EwbDtls in the payload to ask for the e-way bill in the same call; ewaybill_status in the response says what happened to it.

IRN issued, e-way bill refused (ewaybill_status: "failed")

The government system checks the IRN and the e-way bill separately. It can issue the IRN and refuse only the e-way bill, for example over a distance, vehicle number or transporter field. The response is then success: true with the real IRN, ewaybill_no: null, and the reason in ewaybill_error.

  1. Save the IRN, ack number and QR code: the e-invoice is valid and was charged.
  2. Fix what ewaybill_error names.
  3. Generate the e-way bill with POST /v1/ewaybill for the same document number and date. It is charged as a separate e-way bill.

Do not resend the invoice to /v1/einvoice for this: the IRN already exists, so you get the same IRN back (already_existed: true) and the e-way bill is not tried again. Test this branch in your code with test_mode and "test_scenario": "ewaybill_failed".

Costs 3 credits per successful e-invoice — not per attempt.

FieldTypeDescription
gstinstringYour GSTIN, with e-invoice API access set up
einv_usernamestringAPI username from that registration
passwordstringAPI password from that registration
payloadobjectOne NIC e-invoice (INV-01) JSON payload
txn_typestring, optionalINV, CDN or DBN — defaults to INV
test_scenariostring, optionalWith test_mode only. "ewaybill_failed" returns the IRN-issued, e-way-bill-refused response, so you can build for it.

EwbDtls: the e-way bill part of the payload

FieldDescription
DistanceKilometres. 0 lets the government system work it out from the two pincodes; it returns the figure as ewaybill_distance_km.
TransMode1 road, 2 rail, 3 air, 4 ship
VehNo, VehTypeVehicle number (e.g. GJ05AB1234) and R regular or O over-dimensional, for road
TransId, TransNameTransporter GSTIN/ID and name, when a transporter moves the goods
TransDocNo, TransDocDtTransport document (LR, RR, airway bill) number and date, when there is one

Leave a field out entirely when you do not use it. The government schema rejects empty strings, which is a common reason the IRN is issued but the e-way bill is refused.

Response fields

FieldDescription
irnThe 64-character Invoice Reference Number
ack_no, ack_dateAcknowledgement number and time from the e-invoice system
doc_noYour invoice number, as the signed invoice records it
qr_code_image, signed_invoiceSigned QR payload to print, and the full signed invoice (see the note below)
ewaybill_status"generated", "failed" (IRN issued, e-way bill refused) or "not_requested" (no EwbDtls sent)
ewaybill_no, ewaybill_date, ewaybill_valid_uptoThe e-way bill, when ewaybill_status is "generated"; otherwise null
ewaybill_errorWhen ewaybill_status is "failed": code and message from the government system (code can be null if it gave none), and details listing every reason. Otherwise null.
ewaybill_distance_kmOnly when you sent Distance 0 and the system worked it out from the pincodes
fixOnly when ewaybill_status is "failed": what to do next
noticesOnly when the e-invoice system sent extra advisories with the IRN: a list of code and message
already_existedtrue when this invoice number already had an IRN and the existing one was returned (see safe retries)
billed_to, credits_remaining, free_remainingWhat was charged and what is left

qr_code_image and signed_invoice are the filing system’s SignedQRCode and SignedInvoice respectively — passed through as-is, not re-rendered. qr_code_image is just the QR payload meant for printing on the invoice (you’ll need a QR-rendering library to turn it into a scannable image); signed_invoice is the full tamper-evident signed invoice record, for audit and reconciliation.

E-invoice

Check an e-invoice payload (free)#

POSThttps://www.gstinapi.in/v1/einvoice/validate

Free and offline, like the e-way bill check: no portal credentials, no credits, no call to the government network. It checks the INV-01 rules that most often cause a refusal: invoice number and date, seller and buyer GSTIN and state codes, pincodes, HSN, GST slabs, every line and document total, IGST versus CGST and SGST for the place of supply, and any embedded e-way bill details.

Errors are unambiguous rules; warnings are probable mistakes. A payload that passes can still be refused for reasons only the government network can see, and a refused live call returns a likely_causes list from this same check. The check never blocks a live call.

E-invoice

Cancel an e-invoice#

POSThttps://www.gstinapi.in/v1/einvoice/cancel

Cancels a previously generated e-invoice by its IRN.

Costs 3 credits per successful cancellation.

FieldTypeDescription
gstinstringYour GSTIN, with e-invoice API access set up
einv_usernamestringSame e-invoice API user as generate. Older integrations can keep sending it as gstin_username; if both are sent, einv_username is used.
passwordstringSame credentials as generate
irnstringThe invoice reference number to cancel
cancel_reasonstring, optionalNIC's own reason code
cancel_remarkstring, optionalFree-text remark

Sandbox / test mode#

Add "test_mode": true to either e-invoice endpoint to build and test your integration before spending a credit or even having your e-invoice API access set up yet. Required fields are still validated, but the real government network is never called and nothing is charged, however low your balance is.

Every reference number returned in test mode is prefixed TEST- so it can never be mistaken for a real government-issued document.
bash
curl -X POST "https://www.gstinapi.in/v1/einvoice" \
  -H "x-api-key: gak_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "gstin": "24AAAAA0000A1Z5",
    "einv_username": "your_einvoice_portal_username",
    "password": "your_einvoice_portal_password",
    "payload": { "docDtls": { "no": "INV-1001" } },
    "test_mode": true
  }'
json
{
  "success": true,
  "test_mode": true,
  "irn": "TEST-IRN-9F3A2B10",
  "ack_no": "TEST-ACK-1C4D5E20",
  "ack_date": "13/09/2026 12:00:00",
  "doc_no": "INV-1001",
  "qr_code_image": "iVBORw0KG...",
  "signed_invoice": "TEST-HEADER.TEST-PAYLOAD.TEST-SIGNATURE",
  "ewaybill_status": "not_requested",
  "ewaybill_no": null,
  "ewaybill_date": null,
  "ewaybill_valid_upto": null,
  "ewaybill_error": null,
  "already_existed": false,
  "billed_to": "test_mode",
  "credits_remaining": 25,
  "free_remaining": 0
}

Add EwbDtls to the payload and the test response carries a TEST- e-way bill with ewaybill_status "generated". Add "test_scenario": "ewaybill_failed" as well to get the response for an IRN that was issued while the e-way bill was refused, with a sample ewaybill_error and fix, so that branch of your code is tested before it happens live.

Test mode proves your integration end-to-end — authentication, request shape, response shape, billing behaviour. It does not simulate what the government network would actually do with a given payload (a malformed field, an inactive GSTIN), since that validation only exists on the real network.

Safe retries after a timeout#

A government-network round trip can occasionally run long enough to hit this API's own timeout before a response comes back — leaving it genuinely unclear whether the invoice was registered. The safe recovery is to retry with the exact same invoice number, never a new one.

  • /v1/einvoice — a retry with the same invoice number returns the already-generated invoice's real irn/ack_no/qr_code_image/signed_invoice with already_existed: true set, billed the same as a fresh success — never a duplicate filing, and never a lost IRN.

Retrying with a different invoice number instead is what actually risks a duplicate filing — the government network has no way to know the two calls were meant to be the same attempt.

Integrate using AI — e-invoice

Same idea as the main AI prompt above, scoped to generating and cancelling e-invoices — including how to safely retry after a timeout without registering the same invoice twice.

  1. Click Copy prompt, then paste it into your AI assistant.
  2. Replace MY PROJECT with what you are building.
  3. Paste your API key and your e-invoice credentials where the assistant tells you to.
I want to add e-invoice (IRN) generation (Indian GST filing) to my
project using the gstinapi.in REST API. Please write the integration code for me.

MY PROJECT: <describe it — e.g. "a Node.js Express app", "a WordPress site",
"a Google Sheet", "a Python script", "a React form">

There is no official client library for this yet (the gstinapi Node.js/Python
packages only cover GSTIN lookup) — call the REST endpoints directly with
whatever HTTP client my language/framework normally uses.

API DETAILS
Base URL   : https://www.gstinapi.in
Auth       : header  x-api-key: <MY_API_KEY>
Rate limit : 60 requests per minute per key

ENDPOINTS
POST /v1/einvoice          generate an e-invoice (IRN) — include EwbDtls in the
                           payload to also get an e-way bill in the same call
POST /v1/einvoice/cancel   cancel one, by irn

EXAMPLE REQUEST — generate an e-invoice, in sandbox (test_mode)
curl -X POST "https://www.gstinapi.in/v1/einvoice" \
  -H "x-api-key: MY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "gstin": "24AAAAA0000A1Z5",
    "einv_username": "einv_api_username",
    "password": "einv_api_password",
    "payload": { "DocDtls": { "Typ": "INV", "No": "INV-1001", "Dt": "14/09/2026" } },
    "test_mode": true
  }'

Cancel takes gstin / gstin_username / password plus the irn to cancel.

SUCCESS RESPONSE (HTTP 200) — generate
{
  "success": true,
  "irn": "35a1b2c3...",
  "ack_no": "112010012345678",
  "ack_date": "14/09/2026 10:00:00",
  "qr_code_image": "<base64 — the filing system's SignedQRCode, not a rendered image>",
  "signed_invoice": "<JWS string — the filing system's SignedInvoice, the full signed legal record>",
  "ewaybill_no": null,
  "already_existed": false,
  "billed_to": "paid",
  "credits_remaining": 197
}

ERROR RESPONSE — a genuine rejection from the filing system (bad payload,
expired registration) — never charged
{ "success": false, "error": "human readable reason", "error_code": "upstream code, or null" }

STATUS CODES
200  success — real filing, or a synthetic test_mode response
400  missing required fields, or the filing system rejected the document — never charged
401  missing or invalid x-api-key
402  insufficient credits for this call (costs more than 1 credit)
403  account deactivated
429  rate limit exceeded
502  filing system temporarily unavailable — safe to retry, never charged

REQUIREMENTS
1. Read the gstinapi.in API key AND the einv_username/password (gstin_username/password for cancel) from environment
   variables. Never hardcode any of them, and never expose them in
   browser/client-side code — call this API from my server only.
2. Build and test everything with "test_mode": true first. It skips the
   credit check and the real government network entirely, returns a
   synthetic response with every reference number TEST-prefixed, and needs
   no real e-invoice registration — only remove it once ready to
   file for real.
3. Handle every status code above with a clear message. Treat a 400
   rejection (bad data, never charged) differently from a 502 (transient,
   safe to retry) differently from a 402 (buy more credits) — do not lump
   them into one generic "failed" state.
4. On 429 and 502 only, retry with exponential backoff, maximum 3 attempts.
   Never retry a 400/401/402/403.
5. The single most important rule: if a request times out and you cannot
   tell whether the document was actually filed, retry with the EXACT SAME
   invoice number — never a new one. The API detects this and returns the real
   original document with already_existed: true, billed the same as a fresh
   success. Retrying with a different number is what actually risks a
   duplicate government filing.
6. qr_code_image and signed_invoice are passed through exactly as the filing
   system returned them — qr_code_image needs a QR-rendering library on my
   side to become a scannable image; signed_invoice is the full signed
   record, for audit/reconciliation, not something to parse further.
7. If EwbDtls (vehicle number, transport mode, distance) is in the payload,
   the e-way bill is generated in the same call and billed once, not twice.
   Omit the transporter id/name/document fields entirely when unused — the
   schema rejects empty strings for them.
8. Parse every response leniently. New fields are added without notice;
   existing fields are never removed or renamed without a new API version.
   Ignore unknown keys rather than validating with a strict/closed schema.
9. My GSTIN needs e-invoice API access set up once, separately from this
   API key, before any non-test_mode call will work — remind me to contact
   gstinapi.in support if a real (non-test_mode) call fails because of this.
10. Log credits_remaining after each real call so I can watch my balance.

Tell me exactly which file to create, where to put my credentials, and how to run it.

Your API key and your e-invoice credentials are passwords — keep them on your server. Anyone who has them can spend your credits or file on your behalf.

GST return filing: how it works#

Filing a GST return is several calls, because a taxpayer reviews a return before it is final and the last steps cannot be undone. The same pattern applies to GSTR-1 and GSTR-3B: save the return and read the summary, review it, then file with a one-time password.

  1. Connect the GSTIN once. In the dashboard under GST Return Filing, or from your own server with the connect calls below, enter the GST portal username. The GST portal sends a one-time password to the registered mobile or email; confirm it. The connection is kept alive automatically within the API-access duration chosen on the GST portal (6 hours to 30 days) and covers any number of filings. Before that, the GSTIN needs API access set up on the GST portal under My Profile, Manage API Access; since 2026 that includes selecting a GSP-ASP combination on the same page, and the current steps with what to select are in the GST portal API access guide.
  2. Save and preview. Send the return JSON to /save. Nothing is filed or paid. You get the summary and a chksum that identifies exactly what was saved.
  3. GSTR-3B only: pay the liability with /offset. This pays tax from the cash and credit ledgers and cannot be undone.
  4. Request the filing OTP. GSTR-1 asks for it as part of /save; GSTR-3B has its own /evc-otp. It goes to the mobile of the authorised signatory, and is different from the connection OTP.
  5. File with /file, passing the OTP and the checksum you reviewed. If the saved return has changed since you reviewed it, GSTR-3B refuses with 409 CHECKSUM_CHANGED rather than filing something different.

Billing. Saving, previewing, the OTP request and paying the liability are free. A filing is charged only when it succeeds; a failed filing never is. Live filing needs an account that has bought a credit pack.

Try it first. Add "test_mode": true to any call below. It returns a synthetic response in the live shape, never reaches the GST network, files and pays nothing, and costs nothing, so you can build the whole flow before the first real filing.

New and supervised. Return filing is new. Your first real filings should be watched by someone, on a return you can check on the GST portal. Filing by DSC and GSTR-9 are not available.

GST return filing

Connect a GSTIN: request the OTP#

POSThttps://www.gstinapi.in/v1/gst/filing/connections

The same connection the dashboard makes, from your own server. Send the GSTIN and the GST portal username that GSTIN logs in with: not a practitioner login, and not the username of another GSTIN under the same PAN. Before the first connection the GSTIN needs API access set up on the GST portal, including the GSP-ASP combination; the GST portal API access guide has the steps and what to select. There is no test_mode here: this call sends a real OTP.

These four calls take x-api-key like every other endpoint (or the dashboard session token). None of them is charged. One connection covers GSTR-1, GSTR-3B, GSTR-2A, GSTR-2B and IMS for that GSTIN, from any IP.

GST return filing

Connect a GSTIN: verify the OTP#

POSThttps://www.gstinapi.in/v1/gst/filing/connections/{gstin}/verify

Send the 6-digit OTP within 10 minutes of requesting it. A successful verify proves the username and the OTP; whether the GSTIN belongs to that username is checked by the GST network on the first data call, so a connection can show active and still answer RET11402 "API Authorization Failed" on a fetch. That response carries the remedy as fix and is not charged.

GST return filing

Connections: list#

GEThttps://www.gstinapi.in/v1/gst/filing/connections

Every GSTIN connected on this account with its live status. Poll this instead of guessing whether a session is still valid.

GST return filing

Connections: disconnect#

DELETEhttps://www.gstinapi.in/v1/gst/filing/connections/{gstin}

Use it before reconnecting with a different username, or when a GSTIN should no longer be reachable from this account.

GST return filing

GSTR-1: save and preview#

POSThttps://www.gstinapi.in/v1/gstr1/save

ret_period is MMYYYY. payload is the GSTR-1 JSON in the GST network's own schema (gstin, fp, b2b and so on), passed through untouched. pan is the PAN of the authorised signatory who will receive the OTP.

GST return filing

GSTR-1: file#

POSThttps://www.gstinapi.in/v1/gstr1/file

summary is the object /v1/gstr1/save returned, unchanged: its checksum is what the GST network signs off on. evc_otp is the six-digit OTP sent for this filing.

GST return filing

GSTR-3B: save and preview#

POSThttps://www.gstinapi.in/v1/gstr3b/save

payload is the GSTR-3B JSON in the GST network's own schema (sup_details, inter_sup, itc_elg, intr_ltfee and so on), passed through untouched.

GST return filing

GSTR-3B: pay liability#

POSThttps://www.gstinapi.in/v1/gstr3b/offset

payload carries the ledger split in the GST network's schema: pdcash for what is paid in cash and pditc for what is paid from input credit.

GST return filing

GSTR-3B: request the OTP#

POSThttps://www.gstinapi.in/v1/gstr3b/evc-otp

Ask for the OTP just before you file; it is valid for a short time.

GST return filing

GSTR-3B: file#

POSThttps://www.gstinapi.in/v1/gstr3b/file

chksum is the checksum from /save (or from /offset, if you paid first). The return is read again before filing, and a mismatch stops the filing.

GSTR-2B: how it works#

GSTR-2B is the GST network's own auto-drafted statement of input tax credit available for a return period, built from what your suppliers filed. It is read-only — nothing is filed or paid — so unlike GSTR-1/3B above, there is no final step waiting on anything to be switched on.

  1. Connect the GSTIN once. Same one-time OTP connection as GSTR-1/3B, in the dashboard under GSTR-2B & GST Return Filing. One connection covers both, and the GST portal side is the same: Manage API Access with the GSP-ASP combination selected, see the guide.
  2. Generate it, if needed. GSTN normally generates 2B itself once a month; /generate asks for it on demand and returns an int_tran_id to poll.
  3. Poll with /generate/status until generation is done, if you called /generate.
  4. Fetch it with /fetch. This is the only billed call in this flow — charged only on a real success.

Try it first. Add "test_mode": true to any call below for a synthetic response that never reaches the GST network and costs nothing.

New. This is the first release of GSTR-2B on this API. The exact shape of the statement under data can still move as we see more real periods; read it defensively rather than assuming every field shown below is final.

GST return filing

GSTR-2B: generate#

POSThttps://www.gstinapi.in/v1/gstr2b/generate

ret_period is MMYYYY. You only need this if a 2B does not already exist for the period, or you want it regenerated after suppliers filed late.

GST return filing

GSTR-2B: generation status#

POSThttps://www.gstinapi.in/v1/gstr2b/generate/status

int_tran_id is the one /generate returned.

GST return filing

GSTR-2B: fetch statement#

POSThttps://www.gstinapi.in/v1/gstr2b/fetch

ret_period is MMYYYY. file_num (optional) identifies which generation of that period's 2B to read, if it was regenerated more than once; leave it out for the default.

GSTR-2A: how it works#

GSTR-2A is the GST network's live, continuously-updated purchase-side statement, built as your suppliers file — unlike GSTR-2B's frozen monthly snapshot above. It is read-only, same as GSTR-2B, and uses the same one-time OTP-connected GSTIN as GSTR-1/3B/2B.

The GSTIN needs API access set up on the GST portal first, under My Profile, Manage API Access, including the GSP-ASP combination the portal asks for since 2026 (steps and what to select: GST portal API access guide). A fetch that answers RET11402 "API Authorization Failed" right after connecting has two known causes: the GSP-ASP combination not selected on the portal, or a GST portal username that belongs to another GSTIN; the error response carries the remedy under fix, and the call is not charged.

Unlike GSTR-2B's one consolidated statement, GSTR-2A is split into 12 separate tables. One call fetches one table for one GSTIN/period; getting the whole 2A for a period means up to 12 calls.

  • b2b — B2B invoices
  • b2ba — Amended B2B
  • cdn — Credit/debit notes
  • cdna — Amended credit/debit notes
  • impg — Import of goods
  • impgsez — Import from SEZ
  • tcs — Tax collected at source
  • tds — Tax deducted at source
  • amdhist — Import bill-of-entry amendment history
  • ecom — E-commerce
  • ecoma — Amended e-commerce
  • isd — Input Service Distributor credits

Try it first. Add "test_mode": true to the call below for a synthetic response that never reaches the GST network and costs nothing.

Empty is normal. A GSTIN with no entries for a table in that period gets a 400 with error_code RET13509, "No document found for the provided Inputs". It is not charged. Treat it as an empty table rather than a failed call — most accounts will see this on several tables every period.

GST return filing

GSTR-2A: fetch a table#

POSThttps://www.gstinapi.in/v1/gstr2a/fetch

ret_period is MMYYYY. table picks which of the 12 tables above to fetch — one flat price per call, regardless of table. ctin and fromtime are optional filters accepted by most tables; amdhist additionally requires portcode, benumber and bedate.

Reference

Credits, errors and limits

The rules shared by every endpoint on this page.

Credits#

  • Every account gets up to 75 free credits — 10 on signup and 65 more across the setup steps on the dashboard — no card required, and they never expire. Purchased credits are only used once the free ones run out.
  • A successful lookup costs 1 credit. Your balance is returned as credits_remaining on every call.
  • A malformed GSTIN (400) is free — validate client-side and you never waste a credit.
  • Credits never expire.

HTTP Status Codes#

CodeStatusMeaning
200OKSuccess — GSTIN found and returned. 1 credit charged.
400Bad RequestInvalid GSTIN format — no credit charged
401UnauthorizedMissing or invalid x-api-key header
402Payment RequiredInsufficient credits — recharge your account
403ForbiddenAccount deactivated — contact support
404Not FoundGSTIN not registered in GST database
429Too Many RequestsRate limit exceeded (60 req/min)
502Bad GatewayGST provider temporarily unavailable — retry

⚡ Rate Limits#

Default limit is 60 requests per minute per API key. A brand-new account is held to 10 per minute until it has verified its email or holds purchased credits — the cap lifts on its own. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers; on a 429, wait for the reset rather than opening a second key. Need more? Contact support.

Free integration: sample mode & Postman#

Build and test your whole integration before you pay anything: no setup fee, no annual fee, no minimum. Every GSTIN and PAN endpoint answers a test input with a fixed sample in the live response shape, never consumes a credit and never calls the GST network. When your code works, top up and switch the input to a real GSTIN or PAN; nothing else changes.

EndpointTest input
GET /v1/gstin/{gstin}00AAAAA0000A1ZT
GET /v1/gstin/{gstin}/returns?fy=00AAAAA0000A1ZT
GET /v1/gstin/{gstin}/compliance?fy=00AAAAA0000A1ZT
GET /v1/gstin/{gstin}/filing-preference?fy=00AAAAA0000A1ZT
GET /v1/gstin/{gstin}/complete00AAAAA0000A1ZT
GET /v1/pan/{pan}/gstins, /verifyAAAAA0000A
POST /v1/ewaybill, /v1/einvoice"test_mode": true

State code 00 does not exist on the GST network, so the test GSTIN can never collide with a live registration, and the test PAN is the one inside it. A sample call authenticates like any call but skips the balance gate, so it works on an empty wallet (including an empty PAN search wallet) and CI keeps passing:

curl "https://gstinapi.in/v1/gstin/00AAAAA0000A1ZT/returns?fy=2025-26" -H "x-api-key: YOUR_API_KEY"

The response carries "test": true and "billed_to": "test" so it can never be mistaken for live data.

To explore the API in Postman or Insomnia, import the OpenAPI spec straight from /openapi.json (Postman: Import → Link). Every endpoint, parameter and response schema is in there.

Resources & downloads#

Specs, collections, libraries and guides — everything you can import or install.

Ready to try it?

Create a free account and get up to 75 free lookups — no credit card required, and they never expire.

Get your API key