Now live: GSTIN + MCA verification in a single call ◆
API v1

Run checks over HTTP

A REST API over the same engine as the dashboard: same checks, same credit balance, same audit trail. Create a key under Account → API keys.

Authentication

Every request carries a personal access token as a bearer credential. Tokens are shown once at creation — store them in your secret manager, not in source control.

curl https://businesskyc.com/api/v1/me \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Accept: application/json"

Check your balance

GET /api/v1/me

Returns the authenticated account and its current credit balance.

{
  "data": {
    "id": 12,
    "name": "Ananya Raghavan",
    "email": "ananya@example.com",
    "company_name": "Sample Traders Pvt Ltd",
    "credits_balance": 480
  }
}

List available checks

GET /api/v1/services

Returns every active check with its credit cost and the fields it expects, so you can build request payloads dynamically.

{
  "data": [
    {
      "code": "gstin_verify",
      "name": "GSTIN verification",
      "group": "business",
      "credit_cost": 2,
      "fields": [
        { "key": "gstin", "label": "GSTIN", "rules": "required|string|size:15" }
      ]
    }
  ]
}

Run a check

POST /api/v1/verify/{code}

Charges the credit cost, calls the source and returns the normalised result. The token needs the "verify" ability.

curl -X POST https://businesskyc.com/api/v1/verify/gstin_verify \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"gstin":"33AABCS1429B1ZP"}'

PAN to GST

POST /api/v1/verify/pan_to_gst

Every GST registration held against a PAN — a business registers once per state, so one PAN can stand behind dozens. Live registrations come first, then by state. Filter on "is_active"; "status" is the registry's own wording. Any "gstin" can go straight to gstin_verify for the full profile. A PAN with no registrations is a definitive answer, so it returns 422 and is still charged.

curl -X POST https://businesskyc.com/api/v1/verify/pan_to_gst \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"pan":"AAACI4798L"}'

{
  "data": {
    "reference": "BK-7E4B2A9C1F",
    "service": "pan_to_gst",
    "status": "success",
    "credits_charged": 2,
    "result": {
      "pan": "AAACI4798L",
      "registrations": 42,
      "active_registrations": 38,
      "inactive_registrations": 4,
      "states": 17,
      "gstins": [
        {
          "gstin": "33AAACI4798L1Z5",
          "status": "Active",
          "is_active": true,
          "state_code": "33",
          "state_name": "Tamil Nadu"
        },
        {
          "gstin": "06AAACI4798L2Z1",
          "status": "Inactive",
          "is_active": false,
          "state_code": "06",
          "state_name": "Haryana"
        }
      ]
    }
  }
}

Return filing history

POST /api/v1/verify/gst_return_filing

Every return filed by a taxpayer in one financial year, plus the monthly or quarterly (QRMP) election behind them — due dates differ between the two, so lateness cannot be judged without it. One check, one charge. "filing_frequency_source" is "registry" when the election came from GSTN and "inferred" when it was read off the periods actually filed.

curl -X POST https://businesskyc.com/api/v1/verify/gst_return_filing \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"gstin":"33AABCS1429B1ZP","financial_year":"2024-25"}'

{
  "data": {
    "reference": "BK-3C7A9F2B1D",
    "service": "gst_return_filing",
    "status": "success",
    "credits_charged": 2,
    "result": {
      "gstin": "33AABCS1429B1ZP",
      "financial_year": "2024-25",
      "returns_found": 24,
      "returns_filed": 22,
      "filing_frequency": "Monthly",
      "filing_frequency_code": "M",
      "filing_frequency_source": "registry",
      "returns": [
        {
          "type": "GSTR3B",
          "period": "2025-03",
          "period_label": "Mar 2025",
          "filed_on": "2025-04-18",
          "status": "Filed",
          "is_filed": true,
          "arn": "AA330325000001Z"
        }
      ]
    }
  }
}

MSME / Udyam

POST /api/v1/verify/pan_to_msme

Two checks against the Udyam register. "pan_to_msme" finds the registration held against a PAN; "msme_details" takes an Udyam or Udyog Aadhaar number directly and returns the same shape. A PAN with no registration is a definitive answer, so it returns 422 and is still charged.

curl -X POST https://businesskyc.com/api/v1/verify/pan_to_msme \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"pan":"AAACJ5037F"}'

{
  "data": {
    "reference": "BK-5D2E8A1C7F",
    "service": "pan_to_msme",
    "status": "success",
    "credits_charged": 3,
    "result": {
      "udyam_number": "UDYAM-AP-02-0026902",
      "enterprise_name": "Sample Enterprises",
      "enterprise_type": "Small",
      "organisation_type": "Partnership",
      "major_activity": "Services",
      "date_of_registration": "18 Feb 2021",
      "state": "Andhra Pradesh",
      "district": "Krishna",
      "pincode": "520001",
      "nature_of_business": [
        "62011 \u2014 Computer programming"
      ],
      "certificate_url": "https://.../certificate.pdf"
    }
  }
}

GST portal sign-in

POST /api/v1/gst/returns/otp

Reads a taxpayer's own GSTR-2B on their behalf: who supplied them in a return period, with each supplier's GSTIN and trade name. Three calls make one conversation — request an OTP, verify it, then read. The session belongs to the caller: "txn" comes back from the first call and goes into the next two, and nothing about the portal session is stored on this server. Only the third call is billed.

# 1. Send an OTP to the mobile on the GST registration.
curl -X POST https://businesskyc.com/api/v1/gst/returns/otp \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"gstin":"33AABCS1429B1ZP","gst_username":"sampletraders"}'

{ "data": { "txn": "a7f3\u2026", "status": "otp_sent" } }

# 2. Exchange the OTP for a session that can read returns.
curl -X POST https://businesskyc.com/api/v1/gst/returns/verify \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"gstin":"33AABCS1429B1ZP","gst_username":"sampletraders","txn":"a7f3\u2026","otp":"123456"}'

{ "data": { "txn": "a7f3\u2026", "status": "verified" } }

# 3. Read the suppliers for one period (MMYYYY).
curl -X POST https://businesskyc.com/api/v1/gst/returns/suppliers \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"gstin":"33AABCS1429B1ZP","gst_username":"sampletraders","txn":"a7f3\u2026","period":"032025"}'

{
  "data": {
    "gstin": "33AABCS1429B1ZP",
    "period": "032025",
    "suppliers": [
      {
        "gstin": "27AAPFU0939F1ZV",
        "trade_name": "Mumbai Steel Traders",
        "period": "032025"
      }
    ]
  }
}

# Is sign-in configured at all? Free, unthrottled, needs only "read".
curl https://businesskyc.com/api/v1/gst/returns/status \
  -H "Authorization: Bearer bkyc_your_token_here"

{ "data": { "available": true, "vendors": 2 } }

Registry change feed

GET /api/v1/gst/cancelled

Every registration change published for one state on one date — cancellations, amendments and new registrations. Not a per-taxpayer check, so it is not billed and needs only the "read" ability. Compare "returned_count" against "total_count", or read "complete": a short page otherwise looks exactly like a quiet day.

curl "https://businesskyc.com/api/v1/gst/cancelled?state=33&date=2025-06-02" \
  -H "Authorization: Bearer bkyc_your_token_here" \
  -H "Accept: application/json"

{
  "data": {
    "state": "33",
    "date": "2025-06-02",
    "total_count": 412,
    "returned_count": 412,
    "complete": true,
    "cancelled_count": 118,
    "changes": [
      {
        "gstin": "33AABCS1429B1ZP",
        "change_type": "C",
        "changed_on": "2025-06-02",
        "is_cancellation": true,
        "is_new_registration": false
      }
    ]
  }
}

GSTIN response

gstin_verify publishes its own envelope, and it is identical whichever of our sources answered. Every key below is always present: an absent value is null, an empty list is [], and an absent address is {} — so you never need two readers.

{
  "status_cd": "1",
  "vendor": "gcheckv1",
  "vendor_ref": "X123456789",
  "api_ref": "BK-JGTDPZS8S3",
  "calling_date": "2026-09-13",

  "data": {
    "gstin": "33AAACC2461Q1ZL",
    "pan": "AAACC2461Q",
    "legal_name": "CHETTINAD MORIMURA SEMICONDUCTOR MATERIAL PRIVATE LIMITED",
    "trade_name": "CHETTINAD MORIMURA SEMICONDUCTOR MATERIAL PRIVATE LIMITED",
    "status": "Active",
    "registration_date": "2017-07-01",
    "cancellation_date": null,
    "taxpayer_type": "Regular",
    "constitution": "Private Limited Company",
    "state_jurisdiction_code": "TN162",
    "state_jurisdiction": "NUNGAMBAKKAM",
    "central_jurisdiction_code": "TK1004",
    "central_jurisdiction": "RANGE IV",
    "einvoice_enabled": true,

    "nature_of_business": ["Office / Sale Office"],

    "principal_address": {
      "door_number": "5B",
      "floor_number": "2nd Floor",
      "building_name": "Chettinad Towers",
      "street": "Rajaji Salai",
      "location": "Nungambakkam",
      "city": null,
      "district": "Chennai",
      "state": "Tamil Nadu",
      "pincode": "600034",
      "full_address": "5B, 2nd Floor, Chettinad Towers, Rajaji Salai, Nungambakkam, Chennai, Tamil Nadu, 600034",
      "nature_of_business": ["Office / Sale Office"]
    },

    "additional_addresses": [],

    "last_updated_date": "2025-05-29"
  }
}
FieldMeaning
status_cd "1" when the registration was found, "0" when it was not. A string, matching the registry's own convention. A "0" response carries an extra error field saying why, and data is {}.
vendor Which of our sources answered. Safe to log; not safe to depend on — a lookup can move between sources without notice, and the data block is identical either way.
vendor_ref That source's own reference. Quote it with api_ref when raising a query about a specific answer.
api_ref Our reference for the call. Fetch it back any time with GET /api/v1/requests/{api_ref}, which returns this same envelope and is never charged.
calling_date The date we called the registry, as YYYY-MM-DD. Not the date the registry last changed the record — that is data.last_updated_date.
pan Derived from characters 3–12 of the GSTIN, which is where a PAN lives by construction. No separate call, no separate charge.

Dates are always YYYY-MM-DD, never a display format — a parser written against 01 Jul 2017 breaks the first time a source phrases it differently.

Successful response

The shape for every check except gstin_verify, which publishes the envelope above. credits_balance reflects the balance after this call, so you never need a second request to track spend.

{
  "data": {
    "reference": "BK-8F2A1C4D9E",
    "service": "pan_verify",
    "status": "success",
    "credits_charged": 1,
    "credits_balance": 478,
    "input": { "pan": "AABCS1429B" },
    "result": {
      "pan": "AABCS1429B",
      "name": "Sample Traders Private Limited",
      "status": "Active",
      "category": "Company"
    },
    "error": null,
    "duration_ms": 184,
    "created_at": "2026-08-12T10:41:07+05:30"
  }
}

All checks

Every code accepted by POST /api/v1/verify/{code}. Costs are the defaults and can be changed per account, so read them from /api/v1/services rather than hard-coding them.

CodeCheckInputCredits
gstin_verify GSTIN verification gstin 1
pan_to_gst PAN to GST pan 2
gst_return_filing GST return filing history gstin, financial_year 2
gstr2b_download Download GSTR2B gstin, gst_username 5
pan_verify PAN verification pan 1
pan_to_msme PAN to MSME (Udyam) pan 30
msme_details MSME Details udyam_number 15
Download GSTR2B is not run through /verify/{code}: it is a sign-in rather than a lookup. Use the three calls under GST portal sign-in.

Rate limits

60 requests a minute across the API, with tighter limits where a call is expensive or reaches someone's phone. Exceeding one returns 429.

EndpointPer minuteAbility
Everything, overall 60 read
POST /verify/{code} 60 verify
GET /gst/cancelled 10 read
POST /gst/returns/otp 5 verify
POST /gst/returns/verify 10 verify
POST /gst/returns/suppliers 30 verify
GET /gst/returns/status 60 read
Five OTP requests a minute, and that is deliberate: every one of them sends a real text message to the taxpayer's registered mobile.

Errors

Standard HTTP status codes. A failed check still returns its reference so you can reconcile it later.

StatusMeaningCredits charged
401 Missing, revoked or malformed token No
403 Token lacks the "verify" ability, or the account is suspended No
402 Insufficient credits — top up and retry No
422 Validation failed, or the check returned no record Only for "no record"
429 Rate limit exceeded (60 requests per minute) No
500 Upstream provider error — credits are refunded automatically Refunded

Ready to build?

Create an account