A PAN verification API in India lets your server send a Permanent Account Number, optionally with the name the customer gave you, and get back a structured answer about whether that PAN exists, what status it carries and whether the name matches the record. With StampMitra it is a single authenticated POST /v1/verify/pan call that returns JSON in real time, so you can gate onboarding, vendor creation or payouts on the result instead of reviewing scanned cards by hand.
The call is deliberately narrow. It checks the PAN as a record, not the person standing behind your signup form. It does not prove that the applicant owns the number, that the number belongs to the right business, or that any bank account is theirs. That is why PAN is usually the first check in a chain, followed by GSTIN, bank account or document checks depending on the risk you carry.
This guide is for developers and product teams building KYC, vendor onboarding, lending, payroll or marketplace flows. It walks through the real request and response, the format and name rules the API applies, every 4xx and 5xx you should handle, consent and data handling, sandbox testing, going live, and a decision guide for when PAN alone is enough.
Key takeaways
- The endpoint is
POST https://api-b2b.stampmitra.in/v1/verify/pan. It needs theverification.pan.readscope on your API key and is metered aspan.verify. panis required and must match the formatABCDE1234F.nameis optional, up to 256 characters, and is what drives name matching.- A valid response carries
valid,panStatus,registeredName,nameMatchResult,nameMatchScore,aadhaarSeedingStatusand areferenceIdyou should store. - Malformed input fails fast with a 400 before any verification is attempted. Negative results (
valid: false) are completed verifications and are billable, so validate on your side first. - PAN proves a record exists and matches a name. It does not prove ownership, liveness, address or bank account control. Pair it with GSTIN for businesses and bank account verification for payouts.
- Sandbox keys (
sk_test_) are free and never debit your wallet. Production keys (sk_live_) need a KYC-approved organisation. - Treat PAN, name and father's name as personal data: collect with consent, use for the stated purpose only, and store the minimum.
What does a PAN verification API actually do?
A Permanent Account Number is a ten-character alphanumeric identifier issued by India's Income Tax Department. Businesses ask for it constantly: to open an account, to pay a vendor, to deduct tax correctly, to meet lender and regulator expectations. The trouble is that a PAN typed into a form is only a string. People mistype it, copy the wrong one, or submit a number that never existed.
A verification API closes that gap by checking the string against an authoritative source and returning a normalised result. Instead of parsing a photographed card, your backend receives fields it can branch on: whether the number is valid, what its status code is, the name on record, and whether the name you supplied matches.
In StampMitra's implementation the flow is short. Your server posts the PAN and an optional name. StampMitra validates the format, performs the verification through its upstream data sources, and returns one JSON object. You never call the upstream source yourself and you do not need to know which one answered. StampMitra's third-party and underlying services policy is explicit that you integrate against the published API contract rather than upstream behaviour.
Three practical consequences follow. First, the call is synchronous, so you can use it inline in a signup request or in a background job. Second, every response carries a referenceId in the format SMV-YYYYMMDD-XXXXXXX, StampMitra's own transaction identifier, which is what you quote to support. Third, because the result is a record lookup, it is only as fresh as the source. The sample response includes lastUpdatedAt, which tells you the date of the record data and is worth surfacing in audit logs.
If you are new to the platform, the quickstart takes about five minutes and uses exactly this endpoint as its first call.
What does PAN verification prove, and what does it not prove?
This is the section most integrations get wrong, so it is worth being precise.
What a successful PAN check establishes
When the API returns valid: true with panStatus: "VALID", you know that the number you submitted corresponds to an existing PAN record and that the record reports an active status (the sample shows status code E, described as "Existing and valid PAN"). When you also pass name, the response gives nameMatchResult and nameMatchScore, which tell you how closely your supplied name lines up with registeredName. The type field (the sample shows Individual) tells you what kind of holder the record describes, which is useful when you expected a company and received a person, or the reverse.
The response also reports aadhaarSeedingStatus with a description ("y" and "Aadhaar linked" in the sample). That is a statement about the PAN record's link status, useful context for downstream compliance teams, but you should confirm with your own compliance owner how, if at all, it affects your decisions.
What it does not establish
- Ownership. Anyone who knows a PAN and the holder's name can submit them. A match shows the pair is consistent, not that the person in front of you is the holder. If impersonation is in your threat model, add a possession or identity step such as a selfie, a document check or a bank penny-drop.
- Business legitimacy. A valid PAN says nothing about whether a business trades, is registered for GST or is solvent. That is the job of GSTIN verification, CIN and Udyam checks.
- Address, date of birth or contact details. The documented response does not return them, and you should not infer them.
- Bank account control. Use bank account verification to confirm that an account exists and matches a name.
- Legal conclusions. A result is information for your decision. Whether it satisfies a specific regulatory requirement is a question for your counsel or compliance officer, not for an API response.
A useful mental model: PAN verification answers "is this a real identifier, and does it agree with the name I was given?" It does not answer "is this the right person or entity, acting honestly?"
How does the StampMitra PAN endpoint work?
The full reference lives at the PAN docs page and the commercial summary at the PAN product page. The table below consolidates the facts you will code against.
| Item | Value |
|---|---|
| Method and path | POST /v1/verify/pan |
| Base URL | https://api-b2b.stampmitra.in |
| Auth header | Authorization: Bearer <key> (the x-api-key header is also accepted) |
| Required scope | verification.pan.read |
| Metered event | pan.verify |
| Required field | pan, format ABCDE1234F |
| Optional field | name, up to 256 characters |
| Success body | JSON with referenceId, valid, panStatus and name-match fields |
| Error body | { "error": { "code", "message", "requestId" } } |
| Per-call price | ₹5 per successful call, deducted from your wallet in production |
Two behaviours deserve emphasis. Input is validated up front, so a malformed pan returns a 400 immediately instead of being submitted for verification. And usage is recorded, and the wallet debited for priced services, only once verification completes successfully. A clean "not valid" answer still counts as a completed verification, which is why format validation on your side saves real money.
Authentication works the same way across every /v1/* endpoint. Keys are environment-scoped (sk_test_ for sandbox, sk_live_ for production) and carry scopes. A key without verification.pan.read gets a 403 even if it is otherwise valid. Details are in the API key documentation.
How do you build the request, step by step?
Follow these steps in order the first time; later you can collapse them into a helper function.
- Create a sandbox account and key. Sign up at signup with email, mobile and password. The documented quickstart says there is no credit card and no manual approval, and you receive a
sk_test_key immediately. The raw key is shown once, so store it in an environment variable such asSTAMPMITRA_API_KEY. - Collect the PAN and, ideally, the name. Ask for the name exactly as the customer would write it on their PAN card. A name you pass is the only input that produces a meaningful
nameMatchResult. - Normalise on your server. Trim whitespace and uppercase the PAN before sending. The documentation states the expected format as
ABCDE1234Fand does not describe case-insensitive handling, so do not rely on it. - Validate the shape locally. Five letters, four digits, one letter. Reject anything else before you spend a call.
- Send the request from your backend. Never call this endpoint from a browser or mobile app, because that would expose your key.
- Handle the response and store the
referenceId. Branch on HTTP status first, then onvalid, then on the name-match fields.
Here is the real request shape, taken from the documented example.
curl -X POST "https://api-b2b.stampmitra.in/v1/verify/pan" \
-H "Authorization: Bearer $STAMPMITRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pan":"ABCDE1234F","name":"Rahul Kumar"}'The PAN and name above are the sample values from the documentation, not a real person's data. Use the same placeholders in tests and in screenshots you share with colleagues.
What does the response contain, field by field?
A successful call returns the object below. This is the exact documented shape.
{
"referenceId": "SMV-20260927-8F42K7",
"pan": "ABCDE1234F",
"type": "Individual",
"valid": true,
"registeredName": "RAHUL KUMAR",
"fatherName": "SURESH KUMAR",
"panStatus": "VALID",
"panStatusCode": "E",
"panStatusDescription": "Existing and valid PAN",
"nameMatchResult": "MATCH",
"nameMatchScore": "100",
"aadhaarSeedingStatus": "y",
"aadhaarSeedingStatusDescription": "Aadhaar linked",
"lastUpdatedAt": "2023-04-01",
"message": "PAN is Valid"
}| Field | Meaning and how to use it |
|---|---|
referenceId | StampMitra's transaction id, always present, even on negative results. Store it. It is not an idempotency key. |
pan | Echo of the number verified. Compare it to what you sent. |
type | Holder category, such as Individual. Check it against the entity type you expected. |
valid | Top-level boolean. Branch on this first. |
registeredName | Name on record. Treat as personal data. |
fatherName | Father's name on record. Store only if you have a clear need. |
panStatus, panStatusCode, panStatusDescription | Status of the PAN, as a label, a code and a sentence. |
nameMatchResult | Outcome of comparing your name with the record. MATCH in the sample. |
nameMatchScore | Score as a string ("100" in the sample). Parse it before comparing. |
aadhaarSeedingStatus and description | Link status reported on the record. |
lastUpdatedAt | Date associated with the record data. |
message | Human-readable summary such as "PAN is Valid". Do not parse it for logic. |
Two cautions. The documentation shows only one example value for several enumerated fields, including nameMatchResult, panStatusCode and aadhaarSeedingStatus. Do not hardcode an exhaustive list. Treat unknown values as "needs review" and log the full response. Second, note that nameMatchScore is a string here, while the bank account endpoint returns nameMatchScore as a number (the documented sample is 0.95) and nameMatch as a boolean. If you build one shared matching helper, normalise both shapes explicitly.
What name and format validation rules does the API apply?
Format validation
The documented validation message is pan must match the format ABCDE1234F. That pattern is five uppercase letters, four digits and a final letter. A regular expression you can use on your side is:
^[A-Z]{5}[0-9]{4}[A-Z]$This catches the large majority of typing errors, such as a missing character, a letter where a digit belongs, or an extra space. It does not prove the PAN exists, since many strings fit the pattern.
Beyond the shape, the structure of a PAN carries meaning. The fourth character indicates the type of holder (commonly P for an individual) and the fifth is derived from the holder's name. These conventions are published by the Income Tax Department, but StampMitra's documentation does not say that the API checks them locally, so do not assume a 400 for an unexpected fourth letter. Use the returned type and registeredName for that comparison instead. If you want to rely on the structure rules in your own pre-checks, confirm them against the current Income Tax Department guidance first.
Name rules
nameis optional. Omit it and you still get existence and status, but name matching is not meaningful.- It is limited to 256 characters.
- It is a free-text string matched against the record. The documentation does not publish the matching algorithm or a threshold, so do not publish or hardcode one as fact. You choose your own acceptance rule over
nameMatchResultandnameMatchScore.
Practical name hygiene
Indian names vary in order, initials, spacing and transliteration. Before sending, remove leading and trailing spaces, collapse repeated spaces and avoid honorifics such as "Mr" or "Shri" if the customer typed them. Send the name the way it would print on the card, not a nickname or trade name. For a company PAN, send the legal name, not the brand.
When the match is not clean, do not auto-reject. A sensible policy is: accept on MATCH, route unknown or partial outcomes to a human or ask the user to re-enter, and reject only on repeated failure or an explicit mismatch combined with other risk signals. Keep that policy in configuration, not code, so compliance can adjust it.
What do working Node.js and Python clients look like?
The samples below use the documented endpoint, headers and fields. They read the key from the environment and never log the PAN in full.
// verify-pan.mjs (Node.js 18+)
const BASE = "https://api-b2b.stampmitra.in";
const PAN_RE = /^[A-Z]{5}[0-9]{4}[A-Z]$/;
export async function verifyPan(pan, name) {
const normalised = String(pan).trim().toUpperCase();
if (!PAN_RE.test(normalised)) {
return { ok: false, reason: "FORMAT", referenceId: null };
}
const res = await fetch(`${BASE}/v1/verify/pan`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.STAMPMITRA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ pan: normalised, ...(name ? { name: name.trim() } : {}) }),
});
const body = await res.json();
if (!res.ok) {
const { code, message, requestId } = body.error ?? {};
return { ok: false, status: res.status, code, message, requestId };
}
return {
ok: true,
referenceId: body.referenceId,
valid: body.valid === true,
panStatus: body.panStatus,
nameMatchResult: body.nameMatchResult,
nameMatchScore: Number(body.nameMatchScore),
type: body.type,
};
}# verify_pan.py (Python 3.9+, pip install requests)
import os, re, requests
BASE = "https://api-b2b.stampmitra.in"
PAN_RE = re.compile(r"^[A-Z]{5}[0-9]{4}[A-Z]$")
def verify_pan(pan: str, name: str | None = None) -> dict:
pan = pan.strip().upper()
if not PAN_RE.match(pan):
return {"ok": False, "reason": "FORMAT"}
payload = {"pan": pan}
if name:
payload["name"] = name.strip()
r = requests.post(
f"{BASE}/v1/verify/pan",
headers={"Authorization": f"Bearer {os.environ['STAMPMITRA_API_KEY']}"},
json=payload,
timeout=20,
)
body = r.json()
if not r.ok:
err = body.get("error", {})
return {"ok": False, "status": r.status_code, **err}
return {
"ok": True,
"referenceId": body["referenceId"],
"valid": body.get("valid") is True,
"panStatus": body.get("panStatus"),
"nameMatchResult": body.get("nameMatchResult"),
"nameMatchScore": float(body.get("nameMatchScore", 0) or 0),
}A few design choices are worth copying. Both clients check the format before the network call, so a typo costs nothing. Both return the error.requestId on failure, which is the value to include in a support message. And both separate transport success from business outcome: an HTTP 200 with valid: false is a completed verification that your code must handle, not an exception.
For a longer walkthrough that includes retries and a typed wrapper, see the sibling guide on verifying PAN in Node.js and Python. StampMitra also lists libraries on the SDKs page.
A worked example: reading a response in a signup flow
Suppose a customer, Meera, enters her PAN and name on a loan-application form for an invented lender called Lotus Finserve. Your backend uppercases the PAN, passes the regex, and calls the endpoint with her name. Three things can happen, and each needs a different branch.
In the first case the call returns HTTP 200 with valid: true, type: "Individual" and nameMatchResult: "MATCH". You save referenceId, a masked PAN and the outcome, mark the PAN step complete and move her to the next screen. In the second case the call returns HTTP 200 with valid: false. The referenceId is still present, because negative determinations are completed verifications, so you save it, tell Meera that the number could not be verified, and let her re-enter it once or twice before escalating to manual review. In the third case the call returns a 502. Nothing has been decided about Meera's PAN at all, so you queue a retry, keep her application in a "verification pending" state and tell her it may take a few minutes.
The point of the example is that three different outcomes share the same endpoint and only your branching logic separates them. Teams that collapse them into a single "verification failed" message lose customers to what was really a transient upstream problem, and teams that treat every 200 as approval let unverified applicants through.
Keeping a verification audit trail
Whatever you decide, keep an audit record that lets you explain the decision months later without storing more personal data than needed. A compact row works well: your internal customer id, the referenceId, the HTTP status, valid, panStatus, nameMatchResult, the parsed score, the version of your acceptance rule, the consent record id and a timestamp. When a customer disputes a rejection, or an auditor asks how a vendor was approved, that row and the StampMitra referenceId are what you work from.
How should you handle errors and 4xx responses?
Every error from the API is normalised to one shape, regardless of cause:
{
"error": {
"code": "BAD_REQUEST",
"message": "pan must match the format ABCDE1234F",
"requestId": "a1b2c3d4-e5f6-4a1b-8c2d-9f0e1a2b3c4d"
}
}Branch on error.code and the HTTP status, not on message text, which can change. Log requestId with every failure. The statuses documented for the PAN endpoint are:
| Status | error.code | Cause | What to do |
|---|---|---|---|
| 400 | BAD_REQUEST | pan must match the format ABCDE1234F | Fix input. Do not retry unchanged. |
| 401 | UNAUTHORIZED | Missing or invalid API key | Check the header, environment and key rotation. |
| 402 | PAYMENT_REQUIRED | Insufficient fulfilment wallet balance | Top up the wallet, then retry. Alert your team. |
| 403 | FORBIDDEN | Valid key lacking verification.pan.read | Issue or update a key with the right scope. |
| 429 | TOO_MANY_REQUESTS | Rate limit exceeded: 60 requests per 60 seconds for this endpoint. | Back off and retry later. |
| 502 | INTERNAL_ERROR | Upstream verification service unreachable | Retry with backoff, then degrade gracefully. |
Which errors are your bug and which are transient?
400, 401 and 403 are deterministic: retrying the same request will fail the same way, so surface them to a developer, not to the end user. 402 is operational: it means your wallet cannot cover the call, and the fix is a top-up, so wire an alert on it rather than showing customers a vague failure. 429 and 502 are the transient pair. Retry them with exponential backoff and jitter, cap the attempts, and keep the customer's session alive with a "we are verifying, this may take a moment" state instead of a hard error.
What should the user see?
Never forward raw error bodies to end users. Map the cases: a 400 becomes "Please check your PAN, it should look like ABCDE1234F"; a 502 or 429 becomes "We could not verify right now, please try again shortly"; and 401, 402 and 403 become a generic failure for the user plus a pager alert for you. For a catalogue that spans every StampMitra endpoint, read the guide on API errors and handling.
How do retries, rate limits and billing interact?
Three facts shape your retry design. First, the documentation states that referenceId is not an idempotency key. If you retry after a timeout, you may create a second verification and, if both succeed, be billed for both. Second, the documented rate limit is 60 requests per 60 seconds for this endpoint, so a bulk backfill needs a throttle. Third, negative results are completed and billable.
Safe retry rules
- Retry only on 429, 502 and network timeouts where you did not receive a response.
- Use a small cap, for example three attempts, with exponential backoff and jitter.
- Before retrying after a timeout, check your own store for a recent result for the same PAN and name. Caching a successful result for a short, purpose-bound window avoids paying twice for a double submit.
- Never retry 400, 401 or 403 automatically.
Throttling bulk jobs
If you re-verify a vendor master of several thousand PANs, a queue with a worker pacing itself under the documented limit is simpler than reacting to 429s. A token bucket of one request per second keeps you safely under 60 per minute, leaves room for interactive traffic on the same key and avoids tripping the platform's acceptable-use rules on rate-limit evasion. Do not rotate keys to bypass limits: the API acceptable use policy prohibits it.
Budgeting
At ₹5 per successful call, cost scales linearly with volume, so the cheapest optimisation is not calling at all: validate format locally, de-duplicate within a batch, and do not re-verify a PAN you verified recently for the same purpose and still have a valid reason to hold. Current rates for every product are on the pricing page.
When should you use PAN alongside GSTIN and bank account verification?
PAN is rarely the whole story. Each check answers a different question, and stacking them tightens the gaps the previous one leaves.
| Your scenario | PAN | Add | Why |
|---|---|---|---|
| Individual customer, low-value signup | Yes | Nothing | Existence plus name match is often enough for low risk. |
| Individual, payout or refund to a bank account | Yes | Bank account | PAN says nothing about who controls the account. |
| Business vendor onboarding | Yes | GSTIN | Confirms GST registration status and legal and trade name. |
| You only have a PAN and need the business's GST registrations | Yes | PAN to GSTIN | Resolves registrations linked to the PAN. |
| Micro or small enterprise claims | Yes | Udyam or PAN to Udyam | Confirms the enterprise registration. |
| Company director or incorporation checks | Yes | CIN | Confirms the company record. |
A worked vendor flow
Imagine a marketplace called Kaveri Traders Pvt Ltd (an invented name) onboarding a supplier. The supplier submits a PAN, a GSTIN and bank details. Your backend verifies the PAN with the legal name, verifies the GSTIN, then compares: does the PAN embedded in the GSTIN agree with the PAN you verified, and does the GSTIN's legalName align with registeredName? Finally you verify the bank account with name and check accountExists and nameMatch. Three independent calls, each cheap, together raise confidence far beyond any one of them. The business-side detail is in the guide on GSTIN verification and the multi-document view in business verification with GSTIN, CIN and Udyam.
For lending and fintech teams, see how these checks compose in the KYC API for fintech and NBFC use case.
Cross-checking the results against each other
The real value of stacking checks is consistency testing, and each comparison is simple to code. Compare the registeredName from PAN with the legalName from GSTIN for a proprietor or company, allowing for case and punctuation differences. Compare the PAN holder's type with the entity type the user selected on your form, since an individual PAN submitted for a company onboarding deserves a second look. Compare the name you collected with beneficiaryName from the bank account response and the boolean nameMatch there. None of these comparisons is a legal determination, and a mismatch is a reason to ask a question, not automatically to refuse.
A mismatch is often innocent. A sole proprietor may hold a GSTIN in a trade name while their PAN carries a personal name. A bank may store an account holder's name with initials only. Build a review queue with the three data points side by side so that a person can resolve those cases quickly, and record the reviewer's reason.
Ordering matters
Run the cheapest, most deterministic check first, which is local format validation, then PAN, then the dependent checks. If the PAN fails, skip the rest and save the cost. The exception is when you need the PAN-derived lookups (pan-to-gstin, pan-to-udyam), which are additional metered calls and should only run after the PAN is confirmed.
How do consent and data handling work for PAN data?
A PAN, a name and a father's name are personal data about an identifiable person. The API makes the lookup trivial, which is exactly why you must be disciplined about when and why you run it.
Consent and purpose
Under the Digital Personal Data Protection Act, 2023, personal data is processed for a lawful purpose, generally on the basis of the individual's consent or another recognised ground. Commentary describes valid consent as free, specific, informed, unconditional and unambiguous. This guide is general information, not legal advice, and the Act is being brought into force in phases, so check the current commencement position and your obligations with qualified counsel.
In engineering terms that means:
- Show a clear notice before you collect the PAN, stating what you will verify and why.
- Record that consent (who, when, what text was shown) next to the stored
referenceId. - Use the result only for the purpose you stated. Do not reuse a KYC lookup for marketing or profiling.
- Do not run lookups on people who did not give you their PAN for a stated purpose. StampMitra's acceptable use policy requires a lawful and appropriate purpose and prohibits systematic enumeration to discover information you are not authorised to access.
Minimise what you store
You do not need to keep the full response. A common pattern is to persist only: referenceId, a masked PAN (for example the last four characters), valid, panStatus, nameMatchResult, nameMatchScore, the timestamp and the consent record id. Avoid storing fatherName and registeredName unless a documented requirement exists. Do not put PANs in application logs, analytics events or error trackers; log the referenceId and requestId instead.
Handling withdrawal, deletion and data requests
People can change their minds. Design for the day a customer asks what you hold about them or asks you to delete it. Because you stored only a masked PAN, a result and a referenceId, most of that request is answered from your own tables. Keep a mapping from your customer id to the consent record and the verification rows so that a deletion can find everything. Where law or a contract requires you to retain a record, such as for tax or audit, keep exactly what is required and label the reason. The DPDP Act commentary describes erasure once consent is withdrawn or the purpose is served, unless retention is needed under law, but the details and timing depend on the phased commencement and your sector, so take advice from qualified counsel rather than from this guide.
Also decide who inside your company can see verification results. A support agent usually needs the outcome and the referenceId, not the registered name or father's name. Role-based access on the admin screens that display these fields is a cheap control that prevents most accidental exposure.
Protect the key and the pipe
Keep API keys server-side, separate sandbox and production keys, rotate on staff changes, and use the IP allowlisting StampMitra offers for sensitive scopes. Read the API security policy, the developer privacy policy and the data processing addendum for how responsibilities are divided: you generally decide why and how your customers' data is submitted, and the addendum describes StampMitra's processing role. StampMitra's retention schedule says retention periods are not a fixed number of days for every category, so define your own retention on your side rather than assuming ours.
How do you test the PAN API in sandbox?
Sandbox is free. The pricing page states that sandbox calls run against sandbox data, never debit your wallet, and are metered and audit-logged. Use it to prove your integration, not to benchmark accuracy.
What to test
- Happy path. Send the documented sample PAN and name and assert that you parse
valid,panStatus,nameMatchResultandreferenceId. - Format failure. Send
ABCDE1234orabcde1234fand confirm your client-side check blocks it. Then bypass the check once in a test and confirm you handle the 400 body. - Auth failures. Remove the header to see a 401. Use a key without
verification.pan.readto see a 403. - Missing optional name. Call without
nameand confirm your code does not treat the absence of a match as a failure. - Rate limit. Fire a short burst in a test environment and confirm your backoff handles a 429.
- Timeouts. Simulate a slow network and confirm your retry does not create duplicate records.
- Persistence. Confirm your database stores the
referenceIdand masked PAN, and does not store the raw response.
What sandbox cannot tell you
The documentation does not describe which PAN values return which outcomes in sandbox, so do not assume an arbitrary PAN will yield a realistic mismatch. Where you need a particular outcome, check the behaviour in your own sandbox account first, and do not promise stakeholders production-like accuracy from sandbox data. Platform policy also notes that sandbox may carry its own restrictions such as rate limits.
Automating the sandbox suite
Turn the checklist into an automated test that runs in CI against a sandbox key stored as a secret. Keep the number of live calls small: a handful of assertions per run is enough, and the sample PAN is the only input the documentation guarantees. For everything else, stub the HTTP layer with recorded copies of the documented error bodies (400, 401, 402, 403, 429 and 502) so that your branching logic is tested without a network. A good unit test feeds each documented error body into your handler and asserts both the user-facing message and whether a retry was scheduled. Add one contract test that fails loudly if a field you depend on, such as valid or referenceId, disappears from a sandbox response, so a change in the response shape is caught in CI rather than in production.
Environments in code
Keep the key and the base URL in configuration, never constants. The only difference your code should know about between sandbox and production is the key (sk_test_ versus sk_live_). The sibling guide on sandbox versus production API keys covers how to structure that safely, and the Postman page is the place to start for quick manual checks.
What does going to production involve?
Self-serve signup gives you sandbox access only. The authentication documentation states that production keys require a KYC-approved organisation, and the pricing FAQ says you apply for production access from the dashboard; once approved you can create a production API key. StampMitra's onboarding verification policy also notes that completing sandbox testing does not guarantee production approval, so plan for a review window in your launch schedule.
Pre-launch checklist
- Wallet funded. Production calls are deducted from your API credit wallet. A 402 in production is an outage for your onboarding funnel, so top up with margin and alert on low balance.
- Scopes confirmed. The production key must carry
verification.pan.read. Add other scopes only when needed. - Key handling. Store the key in a secrets manager, never in source control or a client app. Restrict by IP where possible.
- Error mapping. Every status in the table above has a user-facing message and an alert path.
- Throttling in place. Your batch jobs respect 60 requests per 60 seconds per endpoint.
- Consent and notice live. The consent screen and records are deployed before the first real PAN is submitted.
- Retention defined. You know what you store, for how long and who can read it.
- Runbook. On-call knows to collect
requestIdandreferenceIdbefore contacting support.
A staged rollout
Ship behind a flag. Start with internal users and real PANs of consenting staff, then a small percentage of live signups, watching the share of 400s, 429s and 502s. Compare your valid: false rate with expectations: a sudden jump usually means a front-end regression in how the PAN is captured rather than a wave of bad customers.
How do you decide when PAN verification is enough?
Use this guide to pick the depth of checking that matches the risk, rather than defaulting to the maximum.
| Situation | Recommendation | Reasoning |
|---|---|---|
| Low-value individual account, no money moves out | PAN with name | Existence and name consistency cover the main data-entry risk. |
| Money moves to the user (refund, payout, salary) | PAN plus bank account verification | You need to know the account belongs to that name. |
| User could be impersonating someone | PAN plus a possession or identity check | A PAN match does not prove who is submitting it. |
| Business customer or supplier | PAN plus GSTIN, and CIN or Udyam when relevant | Business status and registrations are separate records. |
| You only need to know a business's GST registrations from its PAN | PAN to GSTIN | Resolution lookup, not a validity check. |
| Re-verifying existing customers on a schedule | Verify only where a risk trigger or policy requires it | Each call is billable and re-processes personal data. |
| Pure format check on a form | Local regex, no API call | Free and instant. |
| Name unknown or unreliable | PAN without name, and accept that you only learn existence and status | Passing a wrong name produces misleading mismatches. |
When not to use the API
Do not use it to look up people you have no relationship or lawful purpose with, to build lists, or as a general identity search. Do not use it as the sole basis for an irreversible decision such as denying credit without a human route for appeal. And do not treat a match as legal clearance: it is one input among several.
A simple scoring approach
Many teams assign each check a weight and decide by total. For example: format valid (required), PAN valid (required), name match (strong positive), type as expected (positive), bank name match (strong positive). Keep the weights in configuration, version them and log the inputs so you can explain any decision later.
What commonly goes wrong, and how do you troubleshoot it?
Common mistakes
- Verifying with the wrong name. Passing a trade name, a nickname or an order swapped surname produces a poor match on a valid PAN. Collect the name as printed on the PAN.
- Treating HTTP 200 as success. A 200 with
valid: falseis a completed, billable negative result. Branch onvalid. - Parsing
message. The text can change. Use fields. - Comparing
nameMatchScoreas a number without parsing. In the PAN response it is a string. - Hardcoding enum values. Only sample values are documented. Handle unknowns.
- Calling from the browser. This leaks the key and bypasses your throttle.
- Retrying everything. A 400 never improves with retries, and a retried success is billed again.
- Logging full PANs. Logs spread data further than you intend.
- Using a production key in tests. It debits the wallet and processes real personal data.
Troubleshooting by symptom
| Symptom | Likely cause | Fix |
|---|---|---|
| 400 with the format message | Spaces, lowercase, wrong length or digit and letter in the wrong place | Trim, uppercase, apply the regex before calling. |
| 401 | Missing header, wrong environment key, revoked key | Send Authorization: Bearer or x-api-key; check the key prefix. |
| 403 | Key lacks verification.pan.read | Create a key with the scope. |
| 402 | Wallet balance too low | Top up; add a balance alert. |
| 429 | More than 60 requests in 60 seconds | Queue and pace; honour backoff. |
| 502 | Upstream source unreachable | Retry with backoff; show a pending state; alert if sustained. |
| Valid PAN, poor name match | Name order, initials, trade name | Re-collect the name; apply your review policy. |
type is not what you expected | Individual submitted for a company, or the reverse | Ask for the right PAN. |
| Duplicate charges | Retried after timeout | Add short-window caching and de-duplication. |
| Cannot reproduce in sandbox | Sandbox data differs from production | Do not infer production outcomes from sandbox. |
If you are still stuck, gather the requestId from the error body and the referenceId from any successful call, then write to [email protected] or use the support page. Those two identifiers let the team trace a specific call.
How StampMitra Helps
StampMitra is a private LegalTech platform operated by Bani Global Industries LLP. It is not a government agency or a law firm, and nothing in the API or this guide is legal advice. What it provides is a developer-friendly way to run Indian verification checks and e-stamp workflows through one authenticated API.
For PAN, today that means a single documented endpoint, POST /v1/verify/pan, with a scope-gated key, a consistent error envelope, up-front format validation and a referenceId on every response so you can trace and reconcile calls. The same API family includes GSTIN, vehicle RC, Voter ID, driving licence, passport, CIN, Udyam, PAN to GSTIN, PAN to Udyam and bank account verification, so the multi-check flows in this guide use one key, one auth model and one error shape rather than several integrations.
Getting started is self-serve: sign up, receive a sk_test_ sandbox key and make your first call without a card. Sandbox is free and never debits your wallet. Production access is reviewed, requires a KYC-approved organisation and a funded wallet, and is billed per successful call at the rate on the pricing page. The MCP page and the Build with AI page show how to wire these endpoints into AI-assisted workflows, and the webhooks documentation covers event delivery for the parts of the platform that use it.
Some limits are worth stating plainly. StampMitra relies on underlying data sources, so results depend on source availability, freshness and quality, and the platform does not guarantee uninterrupted availability of an external dependency. The documentation does not publish the name-matching algorithm, the full set of status values or the sandbox outcome for a given PAN, and this guide does not invent them. Whether a result meets a regulatory requirement is for you and your advisers to decide.
On what is next, StampMitra plans to keep expanding documentation, SDK coverage and worked examples for verification flows, and to publish more guides on composing checks. These are plans, not commitments, and the current API reference remains the source of truth. To explore products first, browse the products overview or the verification docs index, and for a commercial conversation use contact.
API reference at a glance
POST /v1/verify/panRequest parameters
| Field | Type | Description |
|---|---|---|
Authorizationrequired | header | Bearer <api-key> (e.g. sk_test_... in sandbox, sk_live_... in production). The x-api-key header is also accepted. |
panrequired | string | PAN, format ABCDE1234F. |
name | string | Name to match against records (max 256 chars). |
POST /v1/verify/pan{
"referenceId": "SMV-20260927-8F42K7",
"pan": "ABCDE1234F",
"type": "Individual",
"valid": true,
"registeredName": "RAHUL KUMAR",
"fatherName": "SURESH KUMAR",
"panStatus": "VALID",
"panStatusCode": "E",
"panStatusDescription": "Existing and valid PAN",
"nameMatchResult": "MATCH",
"nameMatchScore": "100",
"aadhaarSeedingStatus": "y",
"aadhaarSeedingStatusDescription": "Aadhaar linked",
"lastUpdatedAt": "2023-04-01",
"message": "PAN is Valid"
}POST /v1/verify/panPAN: ₹5 per successful call, pay-as-you-go (lower on Growth and Scale).
Frequently asked questions
What does a PAN verification API do?
It takes a PAN, and optionally a name, checks them against a record and returns structured JSON. With StampMitra you get fields such as valid, panStatus, registeredName and nameMatchResult, plus a referenceId for tracing the call.
What is the StampMitra PAN verification endpoint?
It is POST https://api-b2b.stampmitra.in/v1/verify/pan. You send an Authorization Bearer key (or x-api-key) with the verification.pan.read scope, a required pan field and an optional name of up to 256 characters.
What format must the PAN follow?
The documented format is ABCDE1234F: five letters, four digits, one letter. A malformed value fails fast with a 400 and the message 'pan must match the format ABCDE1234F' before any verification is attempted.
Does PAN verification prove the applicant owns the PAN?
No. It shows that a PAN record exists and whether a supplied name matches it. Anyone who knows a PAN and name can submit them, so add a possession or identity check, or bank account verification, if impersonation is a risk.
Am I charged for a PAN that turns out to be invalid?
Yes. StampMitra documents that both positive and negative determinations are completed, billable verifications, while format errors rejected with a 400 are not verified. Validate the format on your side first to avoid paying for typos.
Which errors can the PAN endpoint return?
The documented errors are 400 for format problems, 401 for a missing or invalid key, 402 for insufficient wallet balance, 403 for a missing scope, 429 for rate limiting at 60 requests per 60 seconds, and 502 when the upstream service is unreachable.
Is the referenceId an idempotency key?
No. The referenceId, formatted SMV-YYYYMMDD-XXXXXXX, is StampMitra's transaction id for tracing and support. Retrying a request may create a new verification, so cache recent results and retry only on 429, 502 or timeouts.
How do I test PAN verification before going live?
Sign up for a free sandbox account to get an sk_test_ key. Sandbox calls use sandbox data, never debit your wallet and are metered and audit-logged. Test the sample PAN, format errors, auth failures and rate limiting.
How do I get a production key?
Production keys (sk_live_) need a KYC-approved organisation. You apply for production access from the dashboard, and once approved you can create a production key. Sandbox testing alone does not guarantee approval, and the wallet must be funded.
When should I use PAN with GSTIN or bank account verification?
Use PAN alone for low-risk individual signups. Add GSTIN for businesses and suppliers, bank account verification before any payout, and CIN or Udyam where those registrations matter. Each check answers a different question.
How should I handle PAN data under the DPDP Act?
Treat PAN and name as personal data: show a notice, collect consent, use the result only for the stated purpose and store the minimum, such as a masked PAN and the referenceId. Confirm current obligations with qualified counsel.
Sources
- StampMitra, PAN verification API reference,
/docs/verification/panand product page/products/pan(endpoint path, scope, parameters, response, error examples, metering notes). Read from the product data on 2026-10-16. - StampMitra, API key authentication, pricing, quickstart (sandbox and production key model, per-call billing, sandbox behaviour). Read on 2026-10-16.
- StampMitra, API acceptable use policy, data processing addendum, developer privacy policy, API security policy, developer onboarding verification policy. Read on 2026-10-16.
- Income Tax Department of India, guide to the Permanent Account Number (PAN) structure, incometaxindia.gov.in. Located through search on 2026-10-16; the page could not be opened directly from this environment, so only widely known, hedged statements about PAN structure are used. Check the live guidance before relying on structural rules.
- Ministry of Electronics and Information Technology, Digital Personal Data Protection Act, 2023, meity.gov.in. The official text could not be opened on 2026-10-16; consent and purpose points are stated at a general level only. Verify against the official Act and the current commencement notifications.