The IMSS Certificate of Contributed Weeks through an API. You get the official PDF with folio and digital seal, the job history as JSON, and the signals your risk model needs: tenure, stability and salary evolution.
Technical questions? Talk to an engineer on WhatsApp — 0% bots.
Pick a scenario and see what your system receives:
Fictional data with the exact structure the API returns. None of it belongs to a real person.
Knowing whether someone works today is useful. Knowing how many years they have worked, for how many employers, how much their salary has grown and how long they have been without a job changes a credit decision.
It is common for the contributed weeks certificate to appear in a catalog or in some documentation and not exist when it is time to integrate. So before talking to us, there are three things you can check on your own.
So your risk team and your auditor know exactly where every number comes from, the response separates what comes from the document from what we calculate.
files[0].derivedSignals, and nowhere else. They are measurable facts —months, counts, percentages—, never a score or a recommendation.| What you get | Field | Origin | What it contains |
|---|---|---|---|
| Contributed weeks | contributionWeeks | Official | Total, contributed, discounted and reinstated, exactly as IMSS prints them. |
| Job history | employmentHistory[] | Official | One row per job, most recent first: employer, employer registration, state, start date, end date and base salary. |
| Identity | personalInfo · nss | Official | Name, CURP, date of birth and NSS, as IMSS holds them. |
| Document details | reportDetails | Official | Folio, issue date, serial number and IMSS digital seal. |
| The PDF | files[0] | Official | The full official certificate, in base64. |
| Signals | derivedSignals | Calculated | Current status, tenure, stability, salary and salary timeline. |
Currently employed? You can tell in two ways. The quick one is the type: SUCCESS if there is a current job, INACTIVE_EMPLOYMENT if there is not. And to know where they work today, it is the history row with no end date (endDate: null). Every field is always present; the ones that do not apply come as null.
No black boxes. These are the four rules that apply to everything, followed by every signal with its exact operation. You can recompute any of them from the official history and get the same number.
A person with three jobs. The certificate was issued on October 1, 2026.
| Employer | Start | End | Daily salary | Length |
|---|---|---|---|---|
| EMPRESA EJEMPLO UNO | 2015-03-02 | 2017-08-31 | $250.00 | 30 months |
| EMPRESA EJEMPLO DOS | 2018-02-01 | 2021-12-15 | $380.00 | 46.4 months |
| EMPRESA EJEMPLO TRES | 2022-03-01 | current | $520.00 | 55 months |
Between the first and second job there were 5.1 months without formal employment; between the second and third, 2.5.
| Signal | How it is calculated | In the example |
|---|---|---|
| Official weeks | Contributed − discounted + reinstated, using the numbers printed on the certificate. | 560 − 4 + 0 = 556 |
| Current status | ||
| Employed? | Yes, if there is a row with no end date. | Yes |
| Months in current status | Employed: from the current job's start date to the issue date. Not employed: from the last end date to the issue date. | Mar 1, 2022 → Oct 1, 2026 = 55 months |
| Latest monthly salary | Daily salary of the current (or most recent) job × 30.44. | $520.00 × 30.44 = $15,828.80 |
| Salary at the legal cap | True if the daily salary reached the maximum the law allows to be reported: 25 daily UMA, which in 2026 is $2,932.75 per day. The UMA in force on the certificate's issue date is used. | $520.00 is below $2,932.75 → No |
| Tenure | ||
| Average per job | Sum of each job's length ÷ number of jobs. | (30 + 46.4 + 55) ÷ 3 = 43.8 months |
| Longest and shortest job | The maximum and minimum of those lengths. | 55 and 30 months |
| Months since the first job | From the first start date to the issue date. | Mar 2, 2015 → Oct 1, 2026 = 139 months |
| Stability | ||
| Jobs and distinct employers | Rows in the history, and how many different employer registrations appear among them. | 3 and 3 |
| Average months per employer | Months since the first job ÷ number of jobs. | 139 ÷ 3 = 46.3 months |
| Job changes | Number of jobs − 1. In the last 5 years: jobs whose start date falls within 5 years before the issue date. | 2 total · 1 in 5 years |
| Periods without a job | Each gap between the end of one worked period and the start of the next. We report how many, their sum, the longest one and how long ago the last one ended. | 2 gaps · 5.1 + 2.5 = 7.6 months |
| Months actually employed | Calendar time with at least one job, merging the periods that overlap. | 139 − 7.6 = 131.4 months |
| % of time employed | Months actually employed ÷ months since the first job. | 131.4 ÷ 139 = 94.6% |
| Simultaneous jobs | Jobs whose period overlaps another one. An end and a start on the same day count as continuity, not as simultaneous. | 0 |
| Salary | ||
| First, latest, highest and lowest | Daily salaries in the history, ordered by start date. | $250 · $520 · $520 · $250 |
| Change from first to latest | (Latest − first) ÷ first × 100. | (520 − 250) ÷ 250 = +108% |
| Employer changes with a raise or a cut | Every time the employer changes, the new salary is compared with the previous one. | 2 with a raise · 0 with a cut |
| Salary 1, 3 and 5 years ago | The salary of the job held on that date. If unemployed then, the most recent earlier one. Includes the % change against the current one. | $520 · $520 · $380 (+36.8% since then) |
These are facts, not a score. We do not return a credit rating or a recommendation: that decision is yours.
Salary has a maximum. What you see is the base salary the employer reports to IMSS, and the law limits it to 25 daily UMA: $2,932.75 per day in 2026, about $89,272.91 per month. Nobody shows a salary above that, no matter how much they earn. So when salaryAtLegalCap is true, the person reached the maximum and their real income may be higher than what you see. The value changes every year, on February 1.
The type field names the case with a stable code. Your backend runs a switch, it does not interpret text.
| type | What happened | What to do |
|---|---|---|
SUCCESS | The certificate was obtained and the person is currently employed. | Proceed. Feed the history and the signals into your model. |
INACTIVE_EMPLOYMENT | The certificate was obtained, but there is no current job. | Review. You have the full track record and how long they have been without formal employment. |
CURP_NOT_FOUND | IMSS could not find that CURP. | Review. Check the CURP against RENAPO. |
PERSON_WITHOUT_NSS | The CURP exists, but has no NSS assigned. | Review. Never registered with IMSS; there is no history. |
IMSS_INCONSISTENT_DATA | The CURP exists, but its data is inconsistent at IMSS. | Review. The person must clarify it with the institute. |
IMSS_OPERATION_LIMIT | IMSS applied a temporary limit to that query. | Retry later. |
IMSS_REPORT_UNAVAILABLE | IMSS could not generate the report for that record. | Retry another day. Not charged. |
IMSS_RECORD_ERROR | IMSS returned an unexpected error for that record. | Retry another day. Not charged. |
SERVICE_UNAVAILABLE | IMSS is not available. | Retry. Not charged. |
INVALID_REQUEST | The CURP is malformed or the webhook is missing. Answered immediately, when you create the query. | Fix the request. Not charged. |
Gathering a full work history takes longer than it makes sense to keep a request open. So you do not wait for the result: it is delivered to an address of yours, called a webhook, as soon as it is ready.
First time using a webhook? We explain it step by step →
We try the delivery three times. If your address could not receive it, for 24 hours you can ask for the result by sending the ID we gave you in the receipt. This query is free.
| type | What it means | What to do |
|---|---|---|
RESULT_FOUND | The result is ready. It comes complete inside data, identical to the one sent to your webhook. | Use it. It is the same result, with no extra charge. |
RESULT_PENDING | The query is still in progress. | Wait. The result will be sent to your webhook when it is ready. |
RESULT_EXPIRED | More than 24 hours have passed since it resolved. | Query the CURP again if you still need the data. |
INVALID_REQUEST | The ID does not exist or belongs to another account. | Check the ID you stored from the receipt. |
It is a fallback, not the normal path: the result is always sent to your webhook, so there is no need to ask again and again to know whether it is ready.
const res = await fetch("https://api.origoid.com/mex/social-security/v1/imss-contribution-weeks-results", { method: "POST", headers: { "x-api-key": process.env.ORIGOID_API_KEY, "content-type": "application/json", }, // the transaction ID you got in the receipt body: JSON.stringify({ transactionId: "975cf12f-f270-51cc-9037-d710134940c9" }), }); const r = await res.json(); if (r.type === "RESULT_FOUND") process(r.data.type, r.data.data);
curl -X POST https://api.origoid.com/mex/social-security/v1/imss-contribution-weeks-results \ -H "x-api-key: $ORIGOID_API_KEY" \ -H "content-type: application/json" \ -d '{"transactionId": "975cf12f-f270-51cc-9037-d710134940c9"}'
{
"status": "OK",
"type": "RESULT_FOUND",
"data": {
"type": "SUCCESS",
"data": { "contributionWeeks": { … }, "employmentHistory": [ … ] },
"transactionId": "975cf12f-f270-51cc-9037-d710134940c9",
"billable": true
},
"billable": false
}
You send the CURP and your webhook address. Same envelope as every OrigoID service.
const res = await fetch("https://api.origoid.com/mex/social-security/v1/imss-contribution-weeks", { method: "POST", headers: { "x-api-key": process.env.ORIGOID_API_KEY, "content-type": "application/json", }, body: JSON.stringify({ curp: "XEXX010101HNEXXXA4", webhook: { url: "https://your-domain.com/hooks/origoid" }, }), }); const receipt = await res.json(); // type: "ACCEPTED"
import os, requests res = requests.post( "https://api.origoid.com/mex/social-security/v1/imss-contribution-weeks", headers={"x-api-key": os.environ["ORIGOID_API_KEY"]}, json={ "curp": "XEXX010101HNEXXXA4", "webhook": {"url": "https://your-domain.com/hooks/origoid"}, }, timeout=30, ) receipt = res.json() # type: "ACCEPTED"
curl -X POST https://api.origoid.com/mex/social-security/v1/imss-contribution-weeks \ -H "x-api-key: $ORIGOID_API_KEY" \ -H "content-type: application/json" \ -d '{"curp": "XEXX010101HNEXXXA4", "webhook": {"url": "https://your-domain.com/hooks/origoid"}}'
{
"status": "OK",
"type": "ACCEPTED",
"transactionId": "975cf12f-f270-51cc-9037-d710134940c9",
"billable": false
}
{
"type": "SUCCESS",
"data": {
"contributionWeeks": { "total": 556, "contributed": 560, "discounted": 4, "reinstated": 0 },
"employmentHistory": [{
"employerName": "EMPRESA EJEMPLO TRES S.A. DE C.V.",
"startDate": "2022-03-01", "endDate": null,
"baseSalary": { "amount": "520.00", "currency": "MXN" }
}],
"derivedSignals": {
"currentStatus": { "employed": true, "monthsInCurrentStatus": 55 },
"stability": { "percentOfTimeEmployed": 94.6, "jobChanges": 2 }
},
"reportDetails": { "folio": "0000…", "digitalSeal": "…" },
"files": [{ "contentType": "application/pdf", "content": "JVBERi0x…" }]
},
"transactionId": "975cf12f-f270-51cc-9037-d710134940c9",
"billable": true
}
We prepared a guide that explains it without jargon: what it is, what your system needs to receive the result and how to get it working.
A PDF certificate is evidence, but it does not go into a risk model. The difference is delivering it already read, with the math done and the origin of every number in plain sight.
OrigoID is not IMSS and does not act on its behalf: it is a technical means of querying and cross-checking against that source. The accuracy and currency of the information depend on the institute itself.
The official IMSS document that summarizes a person's working life: how many weeks they have contributed and, job by job, with which employer, on which dates and with what base salary.
Only the person's CURP and a webhook address to receive the result. You do not need the NSS.
From IMSS come the PDF, the weeks, the job history, the identity and the document details. We calculate only what is in derivedSignals: current status, tenure, stability and salary. Every signal has its formula published on this page.
No. We return measurable facts —months, counts, percentages and amounts—. The rating and the decision are yours.
It is the daily base salary the employer reports to IMSS. The monthly equivalent is that salary times 30.44. It has a legal maximum of 25 daily UMA, $2,932.75 per day in 2026: when salaryAtLegalCap is true, the person reached that maximum and their real income may be higher.
At your webhook. When you create the query you get a receipt with an ID; when the result is ready we send it to your address with that same ID. If your webhook failed, you can fetch it for 24 hours.
3 credits per resolved query. The receipt, the fallback query and the cases where IMSS could not generate the report are not charged. Plans are published.
The result stays available for 24 hours as a delivery fallback and is then deleted. We do not use it for any other purpose.
Courtesy credits to query in production. Access is enabled after a brief validation of your company.