Sending Documents
Every endpoint that takes a résumé or job description accepts the same three
input forms: text, url, or file. Send exactly one.
{ "text": "Jane Doe — Senior Backend Engineer\n..." }
{ "url": "https://example.com/resumes/jane-doe.pdf" }
{ "file": "JVBERi0xLjQKJcOkw7zDtsO..." }This applies to /parse/resume, /parse/jd, /extract/*, and /redact.
/match takes two documents at once, so its fields are prefixed —
see below.
No matter which form you use, the document is processed transiently and deleted the moment the call ends. Nothing résumé-shaped is ever stored.
text — raw text
The simplest form, and the fastest: nothing to fetch or decode.
curl -X POST "$BASE/parse/resume" \
-H "x-access-key: $REZMATCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Jane Doe\njane@example.com | +1 (415) 555-0142 | San Francisco, CA\n\nEXPERIENCE\nSenior Backend Engineer — Stripe, San Francisco, CA\nMar 2021 – Present\n- Own the payments ledger service in Go.\n\nEDUCATION\nUC Berkeley — B.S. Computer Science, 2017\n\nSKILLS\nGo, Python, PostgreSQL, Kubernetes"
}'Remember to JSON-escape newlines as \n. If what you send is actually HTML,
it’s stripped to visible text before processing — you don’t have to clean up
a page you scraped yourself.
url — fetch it for me
Point at a PDF or a web page and Rezmatch.ai fetches it. This is the natural form for job postings: Greenhouse, Lever, Workday, and company career pages all work directly.
curl -X POST "$BASE/parse/jd" \
-H "x-access-key: $REZMATCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://job-boards.greenhouse.io/acme/jobs/123456"}'Fetch behavior:
http(s)only, redirects followed, 25-second timeout.- The fetcher identifies itself as
RezMatchBot/1.0— allowlist it if you’re pointing at a site you control that has bot mitigation. - HTML is reduced to its visible text before anything else happens. A job posting is overwhelmingly markup, scripts, and styles; stripping it first means your size limit is measured against the words that matter, not the page weight.
- The URL must be reachable from the public internet. Documents behind a
login, a signed S3 URL you haven’t shared, or a private network won’t work —
fetch them yourself and send
fileinstead.
A URL that times out, 404s, or refuses the connection comes back as
invalid_request with the reason in the message, and costs no credits.
file — base64 bytes
Send the document itself. Use this for anything you already have in hand, or anything not publicly reachable — the common case for résumés.
curl -X POST "$BASE/parse/resume" \
-H "x-access-key: $REZMATCH_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"file\": \"$(base64 -i resume.pdf)\"}"import base64, requests
with open("resume.pdf", "rb") as f:
encoded = base64.b64encode(f.read()).decode()
res = requests.post(
f"{BASE}/parse/resume",
headers={"x-access-key": API_KEY},
json={"file": encoded},
)import { readFile } from 'node:fs/promises';
const file = (await readFile('resume.pdf')).toString('base64');
const res = await fetch(`${BASE}/parse/resume`, {
method: 'POST',
headers: { 'x-access-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ file }),
});Base64 only — plain JSON, no multipart/form-data. A data-URI prefix
(data:application/pdf;base64,) is not accepted; send just the payload.
Supported: PDF (processed natively, layout and all) and plain text.
Not yet supported: DOCX, RTF, and images, which return
unsupported_format. An image-only scan with no text layer returns
parse_failed.
Size limits
Limits depend on your plan and on which form you used, because each form measures size differently:
| Form | Measured as | Free | Starter | Growth | Scale |
|---|---|---|---|---|---|
file (PDF) | pages | 2 | 10 | 10 | 10 |
text | characters | 40k | 80k | 160k | 320k |
url | characters of extracted text | 40k | 80k | 160k | 320k |
A hard 10 MB ceiling applies to every form on every plan, before the per-plan limit is checked.
Oversized documents fail with invalid_request before any processing
happens — no credits are charged. See
Credits & Pricing for the full plan table.
Precedence
If you send more than one form, they’re resolved in this order and the rest are ignored:
text → file → urlSending none of them returns invalid_request:
One of text, url, or file is required.
Two documents at once: /match
/match and /score/requirements score a candidate against a role, so each
side gets its own prefixed field. Both sides accept all three forms, plus a
fourth option: already-parsed JSON.
| Side | Parsed JSON | Text | URL | File |
|---|---|---|---|---|
| Candidate | candidate | resume_text | resume_url | resume_file |
| Role | role | jd_text | jd_url | jd_file |
Mix them freely — a base64 résumé against a live job posting:
curl -X POST "$BASE/match" \
-H "x-access-key: $REZMATCH_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"resume_file\": \"$(base64 -i resume.pdf)\",
\"jd_url\": \"https://job-boards.greenhouse.io/acme/jobs/123456\"
}"Each document passed inline is parsed as part of the call and adds its full
parse cost — the match above costs 3 + 6 + 4 = 13 credits. Passing
candidate and role JSON you parsed earlier costs just the 3. When you’re
screening a pool against one role, parse once and reuse the JSON; see
Credits & Pricing for the arithmetic.