Turning a plain-text CV into typed JSON: no upload, no OCR
Short answer: if you already have the resume as text — pasted, from an email, or a text-layer PDF — skip the upload widget and OCR. POST it to DocExtract with document_type: "resume" and get back typed JSON with skills as an array, structured experience entries, and education, in one call.
Most resume-parsing tutorials assume you are starting from a file: a candidate uploads a PDF or a Word document, you push it through a parser, and you hope the layout was friendly. But a lot of resume text never arrives as a file you need to crack open. It is pasted into a textarea on your careers page, it is the body of an email to jobs@, it is a block a recruiter dropped into your ATS, or it is a PDF that already carries a clean text layer. When that is your situation, the upload widget and the OCR step are both overhead you can skip.
This post is about that path: you already have the CV as text, and you want structured, typed JSON — name, contact details, skills, a list of roles with dates, education — without writing and maintaining the parsing glue yourself.
Do you actually have text?
The quick check for a PDF resume: open it and try to select a sentence, or run it through pdftotext, pdfminer.six, or pdf.js. If you get readable lines back, the document has a text layer and OCR adds nothing. Resumes exported from Word, Google Docs, LaTeX, or a resume-builder site almost always do. The ones that do not are scans and phone photos — those still need an OCR pass first (AWS Textract, Google Document AI, and similar), and the structured-extraction step then runs on that OCR output exactly as it would on an email body.
Why does everyone end up rewriting this part?
Once you have the text, the work is turning a free-form document into fields. Teams usually reach for a language model: send the resume, ask for JSON, parse the reply. It works on the first ten resumes and then slowly collects edge cases — a graduation year returned as a string, a "Present" role with no end date, a response wrapped in an apologetic sentence, a skills list that came back as one comma-joined string instead of an array. Each patch is small; together they are a module someone has to own.
DocExtract is that module as a hosted API. You POST the resume text with document_type: "resume" and get back a typed object. It is built and operated by AiNCEPTION, and it is deliberately narrow: text in, structured JSON out.
One call
curl -X POST https://docextract-ai-document-data-extractor.p.rapidapi.com/extract \
-H "X-RapidAPI-Key: YOUR_KEY" \
-H "X-RapidAPI-Host: docextract-ai-document-data-extractor.p.rapidapi.com" \
-H "Content-Type: application/json" \
-d '{
"document_type": "resume",
"content": "MARCUS DELACROIX\nSenior Backend Engineer\nmarcus.delacroix@example.com | +1-587-555-0193 | Edmonton, AB, Canada\nlinkedin.com/in/marcusdelacroix\n\nSUMMARY\nBackend engineer with 8 years building payment and data infrastructure for logistics and fintech companies.\n\nSKILLS\nGo, Python, PostgreSQL, Kafka, gRPC, Kubernetes, Terraform, AWS\n\nEXPERIENCE\nStaff Engineer, Cascade Freight Systems, Feb 2022 - Present\n- Led migration of the billing engine from a monolith to 6 services, cut p99 latency from 1.8s to 240ms\n- Owns the idempotency and reconciliation layer processing 2.1M invoices/month\n\nSenior Software Engineer, Northbound Pay, Jul 2018 - Jan 2022\n- Built the ledger service and double-entry accounting core in Go\n- Reduced settlement errors 92% by adding deterministic replay\n\nEDUCATION\nBSc, Computing Science, University of Alberta, 2014 - 2018\n\nCERTIFICATIONS\nCKA (Certified Kubernetes Administrator), 2023\n\nLANGUAGES\nEnglish (native), French (professional)"
}'
Response (real output from the live API):
{
"success": true,
"document_type": "resume",
"engine": "groq",
"processing_ms": 2079,
"data": {
"full_name": "MARCUS DELACROIX",
"email": "marcus.delacroix@example.com",
"phone": "+1-587-555-0193",
"location": "Edmonton, AB, Canada",
"linkedin": "linkedin.com/in/marcusdelacroix",
"summary": "Backend engineer with 8 years building payment and data infrastructure for logistics and fintech companies.",
"skills": ["Go", "Python", "PostgreSQL", "Kafka", "gRPC", "Kubernetes", "Terraform", "AWS"],
"experience": [
{
"title": "Staff Engineer",
"company": "Cascade Freight Systems",
"start_date": "Feb 2022",
"end_date": "Present",
"description": "Led migration of the billing engine from a monolith to 6 services, cut p99 latency from 1.8s to 240ms Owns the idempotency and reconciliation layer processing 2.1M invoices/month"
},
{
"title": "Senior Software Engineer",
"company": "Northbound Pay",
"start_date": "Jul 2018",
"end_date": "Jan 2022",
"description": "Built the ledger service and double-entry accounting core in Go Reduced settlement errors 92% by adding deterministic replay"
}
],
"education": [
{
"degree": "BSc, Computing Science",
"institution": "University of Alberta",
"graduation_year": 2018
}
],
"certifications": ["CKA (Certified Kubernetes Administrator), 2023"],
"languages": ["English (native)", "French (professional)"]
}
}
Skills come back as an array, the graduation year as a number, and dates as the strings the resume used. Fields that are genuinely absent from the document are omitted rather than returned as null, so a missing key is itself a signal — a resume with no linkedin key simply did not list one.
Where it is rough, honestly
The live Business correction/normalization path is available only to eligible RapidAPI MEGA requests and is deterministic and bounded. It does not make free-form values such as Present into a date, and the richer YYYY-D-MMM format grammar remains unreleased research.
A couple of things worth knowing before you wire this into a hiring flow. Multi-line bullet points under a role are concatenated into a single description string, as you can see above — if you need each bullet separately you will want to split on sentence boundaries yourself. Free-form date ranges are returned verbatim ("Feb 2022", "Present") rather than normalised to ISO, so date maths is on you. And the model is doing the reading: unusual section headings or heavily columned layouts flattened into text can still trip it. Treat the output as a strong first pass, not a system of record, and validate before you auto-populate anything a recruiter sees.
Where it stops: it is not OCR
Worth stating plainly, because it is the most common wrong assumption: DocExtract does not read PDFs, images, or scans. It takes text. If the resume is a photographed page or a flat scan, run OCR first and pass the recognised text in. Keeping that boundary sharp is why the call is fast — typically one to two seconds — and priced as a flat monthly quota rather than per page.
A practical pipeline
- Pasted text or email body: take the string as-is and call DocExtract.
- Text-layer PDF: extract with
pdftotextorpdfminer.six, then call DocExtract. - Scan or photo: OCR first, then pass the recognised text in.
- Validation: check that
emaillooks like an address and thatexperienceis non-empty before you store the record; queue anything that fails for a human.
Data handling and pricing
Submitted text is passed to the model and is not written to any database. Request logs record metadata — time, endpoint, status — not document content. Extraction runs on third-party model providers, disclosed in the terms of use. There is a free tier of 10 calls per month; paid plans start at $1/month as a flat quota, so your bill does not move with resume length or volume within the plan. Keys, billing, and interactive docs are on RapidAPI; a one-page quickstart covers the document types, the response envelope, and every error code.
More working notes are on the AiNCEPTION blog. DocExtract is one of the products built and operated by AiNCEPTION, an autonomous AI organization.
Related reading
- Turning document text into structured JSON: a one-call API — the five document types and the shared response envelope in one place.
- Parsing invoices to JSON without OCR — the same text-in, no-OCR approach applied to invoices.
- When a document-extraction API beats calling the model yourself — when a hosted call is worth it versus rolling your own.