PF_Details_V1 — EPF Passbook API
PF_Details_V1 v1 deprecated
Download as Markdownthe full page as a single .md file — for offline reading or as context for an AI assistantAuthentication
Every request must carry your API key in the X-API-Key header.
Your key is issued to you separately by ops and is never shown in this document.
X-API-Key: <your-key>Base URL
All endpoints are served through the gateway:
https://api.pehchaantech.comEndpoints
/pf-details/v1
| URL | https://api.pehchaantech.com/pf-details/v1 |
|---|---|
| Timeout | 35000 ms |
| Rate limit | 60 req/min · 100000 req/day |
Overview
Retrieve an individual’s EPF (Provident Fund) details from their PAN: employer establishments, month-wise employee and employer contributions, and PF balances. One call returns the whole passbook — the platform handles EPFO connectivity and UAN matching behind the scenes.
PAN is the ONLY accepted identifier. Sending any other field (uan, mobile, month_range, epf_balance) is rejected with 422 by design, so nobody builds against a parameter the API would silently ignore. Note month_range specifically: the HISTORY WINDOW IS FIXED AT 12 MONTHS by the platform and is not a request field. Silently ignoring it would let you ask for 3 months, be billed for a 12-month lookup, and have nothing in the response to tell you which you got — so it is an explicit 422 instead.
Every response — success or not — uses the same envelope, so branch on the status field. Most calls resolve in one round trip, but the source can accept a search and answer later: that returns 202 pending, which is free and worth retrying shortly (cap it at 2–3 attempts). Latency is capped at 30 seconds, so set your client timeout to at least 35 seconds. Look up a PAN only with the individual’s consent — an EPF passbook is personal financial data.
EVERY CALL IS AN INDEPENDENT LOOKUP. There are no sessions and nothing to resume, so send a fresh client_ref_num on every attempt (or omit it and let the platform generate one) — including when retrying a pending. Reusing a ref neither resumes an earlier search nor de-duplicates a charge; it is a trace key only.
BILLING: every outcome except pending and failed is billable, including the ones that return no passbook. A not_found or no_data answer costs the same as a successful lookup — the charge is for querying EPFO, not for finding something. failed (502, the source never answered) and pending (202, not an answer yet) are free. So: validate PANs before calling (a malformed PAN is rejected with 422 before it reaches the source and costs nothing, but a well-formed PAN for the wrong person is a real, billed lookup), don’t loop on no_data, cap your pending retries — the 202 is free but each retry is a brand-new lookup and the one that finally answers is billed — and de-duplicate: two calls for the same PAN are two charges.
Request example
{
"pan": "ABCDE1234F",
"client_ref_num": "hr-onboard-7f3a2b"
}Response example
{
"result": {
"est_details": [
{
"office": "K R PURAM (WHITEFIELD)",
"doc_epf": "03-12-2024",
"doc_eps": "03-12-2024",
"doj_epf": "28-06-2022",
"est_name": "EXAMPLE EMPLOYER PRIVATE LIMITED",
"passbook": [
{
"cr_pen_bal": "0",
"db_cr_flag": "C",
"particular": "Cont. For Due-Month 082022",
"tr_date_my": "01-08-2022",
"wage_month": "072022",
"approved_on": "12-08-2022",
"cr_ee_share": "1800",
"cr_er_share": "1800",
"tr_approved": "03-12-2024"
}
],
"member_id": "PYKRP24601000000010034",
"passbook_status": "Success"
}
],
"employee_details": {
"dob": "1998-10-28",
"uan": "101847123111",
"member_name": "RAUNIT KUMAR"
}
},
"status": "completed",
"success": true,
"description": "Record found successfully",
"client_ref_num": "pehchaan-b5f08841d8f1"
}Fields
| Field | Description |
|---|---|
pan | Required, and the ONLY accepted identifier. Permanent Account Number: 5 letters, 4 digits, 1 letter (^[A-Za-z]{5}[0-9]{4}[A-Za-z]$). Case-insensitive. |
client_ref_num | Optional trace id; 6–64 chars of A-Z a-z 0-9 . _ -. Omit and the platform generates one. A TRACE KEY, NOT A SESSION KEY: use a FRESH value on every attempt, including retries of a `pending` (202) — reusing one neither resumes an earlier search nor de-duplicates a charge. Also returned in the X-Client-Ref-Num response header. |
success | true only when a passbook was delivered. |
status | Machine-readable outcome — branch on this (see Outcomes below). |
result | The passbook — present only when status = completed, otherwise null. Contains employee_details (uan, member_name, dob) and est_details[] (one entry per employer, each with a month-wise passbook[] of cr_ee_share / cr_er_share / cr_pen_bal / db_cr_flag / wage_month / tr_approved). Treat it as an OPEN structure: fields come from the EPFO record, est_details can be empty, optional dates may be absent, and new fields may appear without notice. Read the paths you need defensively rather than validating against a fixed schema. Two specifics worth coding for: passbook[] covers the last 12 months and interleaves EPFO’s annual interest postings, which arrive with an empty tr_date_my and wage_month and a particular like "Int. Updated upto 31/03/2025" — so not every entry has a wage month; and there are NO balance totals — this API returns contribution transactions, not balances, so there is no pf_balance and no overall_pf_balance object. Derive a balance by summing passbook[] over the window if you need one. |
Errors
| Code | Meaning |
|---|---|
completed (HTTP 200) — BILLABLE | Passbook delivered in result — consume it. |
pending (HTTP 202) — not billable | "Search in progress — not billed; retry shortly as a new request." The source accepted the search and is still working, so an attempt a few seconds later has a real chance of landing. Cap it at 2–3 attempts; if it is still pending, treat it as a failure. We are NOT holding your place: the retry starts a fresh search (send a fresh client_ref_num) and the attempt that finally answers is billed. |
not_found (HTTP 200) — BILLABLE | "No data found" — no EPF record is linked to this PAN. Definitive; do NOT retry. Billed the same as a successful lookup. |
no_data (HTTP 200) — BILLABLE | "No result available" — the source holds nothing usable, commonly because the member never activated their EPFO account or their records conflict. Definitive for now; a lookup much later may succeed if they activate it. Do NOT retry on a timer — every attempt is billed and returns the same answer. |
invalid_input (HTTP 200) — BILLABLE | "Invalid input provided" — the identifier was rejected by the source. Re-check the PAN with the customer; do NOT retry unchanged. |
failed (HTTP 502) — not billable | "Source down — please retry later." Temporary upstream issue, or the source was too slow to answer. Retry with exponential backoff (~2 s, max 3 tries) using a FRESH client_ref_num — nothing is resumable here. Free, because the source never answered. |
401 / 403 | Missing or invalid API key, or the key is not granted this API. |
422 | Validation failed — malformed PAN, or a field outside the contract (uan, mobile, month_range and epf_balance are all rejected deliberately). Never retry unchanged. |
429 | Rate limited — respect the Retry-After header. |
503 / 504 | Gateway protecting itself, or the call exceeded the 35 s budget — retry. |
Changelog
- 2026-07-27 Initial release: PAN-only lookup over a fixed 12-month history window, uniform response envelope, and a free pending (202) state for searches the source accepts but has not finished. Every call is an independent lookup — use a fresh client_ref_num per attempt.