Quickstart
One endpoint, three ways to send a CV. You will have JSON back in about 2.2 seconds.
Authentication
Subscribe on RapidAPI and send your key on every request. RapidAPI handles the metering and the billing; this service checks that the call actually came through it.
To try the parser without a key, use the free browser tool — same endpoint, same model, smaller limits.
x-rapidapi-key: YOUR_KEY
x-rapidapi-host: resumejson-resume-cv-parser-api.p.rapidapi.comRapidAPI shows the exact x-rapidapi-host for your subscription in the code snippet on the listing. Copy it from there — it is the authoritative value.
A request that reaches the origin without RapidAPI's proxy secret gets 403 not_via_rapidapi. A key that is sent through RapidAPI but is not on an active subscription never reaches us at all: the gateway answers 403 itself, with {"message": "…"} and no error.code.
Three ways to send a resume
1. JSON, when you already have the text
curl -X POST 'https://resumejson-resume-cv-parser-api.p.rapidapi.com/v1/parse' \
-H 'x-rapidapi-key: YOUR_KEY' \
-H 'x-rapidapi-host: resumejson-resume-cv-parser-api.p.rapidapi.com' \
-H 'content-type: application/json' \
-d '{"text":"Sarah Okonkwo\nStaff Engineer ..."}'2. Multipart, straight from an upload form
curl -X POST 'https://resumejson-resume-cv-parser-api.p.rapidapi.com/v1/parse' \
-H 'x-rapidapi-key: YOUR_KEY' \
-H 'x-rapidapi-host: resumejson-resume-cv-parser-api.p.rapidapi.com' \
-F 'file=@cv.pdf'3. Raw bytes, when you are streaming a file through
curl -X POST 'https://resumejson-resume-cv-parser-api.p.rapidapi.com/v1/parse' \
-H 'x-rapidapi-key: YOUR_KEY' \
-H 'x-rapidapi-host: resumejson-resume-cv-parser-api.p.rapidapi.com' \
-H 'content-type: application/pdf' \
--data-binary @cv.pdfPDF, DOCX and image types are recognised from the file's own bytes, so an upload labelled application/octet-stream still works. A JPEG, PNG or WebP of the page goes through the same three routes.
Getting the resume back in another language
By default a CV comes back in its own language: an Indonesian CV returns Indonesian job titles. Add output_language and the text values are written in the language you name instead.
curl -X POST 'https://resumejson-resume-cv-parser-api.p.rapidapi.com/v1/parse?output_language=French' \
-H 'x-rapidapi-key: YOUR_KEY' \
-H 'x-rapidapi-host: resumejson-resume-cv-parser-api.p.rapidapi.com' \
-F 'file=@cv.pdf'Any language the model can write is accepted — there is no fixed list. Send a name (French, Bahasa Indonesia, 日本語) or a code (fr, pt-BR, zh-Hans); a code is expanded for you, and meta.outputLanguage comes back with what was understood, so fr answers "French".
A JSON or multipart body can carry the same value as an output_language field instead. The query string works for all three ways of sending a CV, including raw bytes, which is why it is the one documented here. If you send both, the query string wins.
| Translated | Left exactly as written |
|---|---|
| Job titles, the headline, highlights, skills, degrees, fields of study, locations, and the languages spoken with their proficiency wording. | Names of people, companies and schools. Emails, phone numbers, URLs. Dates, which are normalised rather than translated. |
Translating is not inventing: a field the CV does not state is still null. A value that is not a language — too long, or carrying anything a language name does not — comes back as 400 unsupported_language before the document is read.
Limits
| Limit | Value | What happens past it |
|---|---|---|
| Upload size | 20 MB | 413 too_large |
| Extracted text | 60,000 characters | 413 too_large |
| Upstream model budget | 25 s per attempt, 2 retries | 503 upstream_unavailable with Retry-After |
| Monthly requests | Your plan's quota, in the table below | 429 from the RapidAPI gateway, naming the MONTHLY quota. Final until the window resets. |
| Burst rate | Set by the RapidAPI gateway; it sends no header counting it | 429 {"message": "Too many requests"}. Back off a few seconds and retry; it does not touch the monthly quota. |
| Resumes per request | One | There is no bulk or batch endpoint. Send one CV per call and run your own concurrency. |
| Delivery | Synchronous | The parse returns on the same request. There are no webhooks, no callbacks and no job ids to poll. |
| Handwriting | Not supported | The parser is built for printed pages. A handwritten CV is not refused — it goes to the vision path and comes back looking like any other parse — but its accuracy has never been measured, so it is not promised. |
Every response through RapidAPI carries your position against that quota: x-ratelimit-requests-limit, x-ratelimit-requests-remaining, and x-ratelimit-requests-reset in seconds. The window runs from the day you subscribed, not the calendar month. Read x-ratelimit-requests-remaining to warn before the refusal rather than after it.
What it costs
The same rungs the listing meters, on the page you are reading. Billing and metering are RapidAPI's; these are the numbers it charges.
| Plan | Price | Included | Billing |
|---|---|---|---|
| Basic | $0 | 100 parses a month | A hard cap, so it can never bill you. |
| Pro | $0 | Pay per use — $0.05 a parse | No monthly fee. You are billed only for the parses you make. |
| Ultra | $29 | 1,000 parses a month | Overage beyond the quota rather than a blocked integration. $0.045 a parse past it. |
| Mega | $99 | 5,000 parses a month | Overage beyond the quota rather than a blocked integration. $0.018 a parse past it. |
Basic is a hard cap and cannot bill you. The paid plans allow overage past the quota rather than blocking a working integration at it; every per-parse overage is in the table above. Subscribe on RapidAPI.
Every error, and what to do about it
Retry the transient one. The rest describe something about the request that a retry will not change.
Every row here still costs you a request. RapidAPI meters at its gateway, on the call rather than on the answer: measured 2026-09-16, a 400 decremented x-ratelimit-requests-remaining by exactly the same one as a 200. That includes the 500s, which are our fault and not yours. What costs nothing is a call the gateway turns away itself — the quota 429 below, and a request to a path the listing does not declare; those come back with no x-ratelimit-* headers at all.
| Status | Code | Meaning | Retry? |
|---|---|---|---|
| 400 | bad_request | The request itself could not be read — malformed multipart, or JSON that is not an object. | No |
| 400 | empty_input | No text field, no file part, or an empty body. | No |
| 400 | unsupported_language | output_language was not a language. Send a name like French or a code like fr. | No |
| 403 | not_via_rapidapi | The call did not come through RapidAPI. | No |
| 403 | none | Your subscription is not active — lapsed, cancelled, or never started. RapidAPI sends this, not the parser, so the body is {"message": "…"} with no error.code: branch on the presence of the code, not on the status. Check the subscription on the listing. | No — renew or resubscribe |
| 405 | method_not_allowed | /v1/parse is POST only. | No |
| 413 | too_large | Past the upload or character limit. The body carries limit. | No |
| 415 | unsupported_type | Not a PDF, DOCX, text or supported image. The body carries supported. | No |
| 422 | unreadable_file | The file could not be opened: an encrypted or corrupt PDF, or a DOCX that is not a readable zip. | No |
| 422 | not_a_resume | Nothing in the document reads as a CV. | No |
| 429 | none | Your plan's monthly request quota is spent: the message names the MONTHLY quota. RapidAPI sends this, not the parser, so the body is {"message": "…"} with no error.code: branch on the message. | No — wait for x-ratelimit-requests-reset, or upgrade |
| 429 | none | Too many requests in a short burst: the body is {"message": "Too many requests"}, also from RapidAPI. It clears on its own. | Yes, after a back-off of a few seconds |
| 404 | not_found | No endpoint at that path. The parse endpoint is /v1/parse. | No |
| 500 | internal | Our fault, and unexpected. The body carries request_id, the same value as the x-request-id header. It counts against your quota like any other call. | No — quote the id |
| 500 | gate_misconfigured | Our fault: the deployment is missing a secret, so it refuses rather than serving paid parses ungated. It counts against your quota like any other call. | No — tell support |
| 502 | upstream_invalid | The model answered with something unusable. | Once |
| 503 | upstream_unavailable | The model is down or rate-limited. | Yes, after Retry-After |
Stuck on an error the table does not explain? Write to support@tiraisoft.com with the x-request-id, the status and the error.code, or ask on the RapidAPI listing.
Every response to /v1/parse carries an x-request-id header, and a 500 repeats it in the body as request_id. It identifies that one call. Quote it about a parse that came back wrong as well as one that failed — it saves you reproducing anything.
{
"error": {
"code": "too_large",
"message": "That document is 91,204 characters; the limit is 60,000. Send the resume alone, not a bundle.",
"limit": 60000
}
}Reading the response
Two keys: resume is the document, meta is what happened. Every field of resume is documented in the field reference.
| meta field | What it tells you |
|---|---|
source | text, pdf, docx or image — what the bytes actually were. A scanned PDF is pdf, like any other PDF. |
read | text if the document had a text layer, vision if the model had to read the pages as images. |
characters | How much text was parsed, or null when read is vision and there was no text layer to count. |
outputLanguage | The language you asked for, resolved — send fr and this reads French. null when you asked for none and the CV came back in its own language. |
durationMs | Server-side time, also sent as the X-Parse-Ms header. |