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.
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"
/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
}
}
/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" }
]
}
]
}
/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"}'
/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"
}
]
}
}
}
/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"
}
]
}
}
}
/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"
}
}
}
/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 } }
/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_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"
}
}
| Field | Meaning |
|---|---|
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.
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"
}
}
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.
| Code | Check | Input | Credits |
|---|---|---|---|
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 |
/verify/{code}: it is a sign-in rather than a lookup.
Use the three calls under GST portal sign-in.
60 requests a minute across the API, with tighter limits where a call is expensive or reaches someone's phone. Exceeding one returns 429.
| Endpoint | Per minute | Ability |
|---|---|---|
| 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 |
Standard HTTP status codes. A failed check still returns its reference so you can reconcile it later.
| Status | Meaning | Credits 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