Quickstart
One request, two ways to run it. The cloud API and the self-hosted container expose the same path and return the same JSON. Only the base URL and the authentication differ.
1. Get an API key
Sign in to the console on the host for your region and open the API keys screen at /keys. On
the Asia Pacific host that is https://resume-parser.affinda.com/keys.
New accounts start with a 14-day trial of 1,000 free documents. A key is shown in full exactly once, when you create it, so copy it then. See Authentication for the details.
2. Send a resume
Post the file to /v1/resumes/parse with your key in the Authorization header. Replace
YOUR_API_KEY with the key you just created.
curl
curl -X POST https://resume-parser.affinda.com/v1/resumes/parse \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@resume.pdf"
Python
import requests
url = "https://resume-parser.affinda.com/v1/resumes/parse"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
with open("resume.pdf", "rb") as f:
response = requests.post(url, headers=headers, files={"file": f})
response.raise_for_status()
resume = response.json()["data"]
name = resume["person"]["name"]
print(name.get("given"), name.get("family"))
TypeScript
const form = new FormData();
form.append("file", file, "resume.pdf");
const response = await fetch("https://resume-parser.affinda.com/v1/resumes/parse", {
method: "POST",
headers: { Authorization: "Bearer YOUR_API_KEY" },
body: form,
});
if (!response.ok) throw new Error(await response.text());
const { data, meta } = await response.json();
console.log(data.person.name, meta.document.classification);
If a multipart form is inconvenient, send the file's bytes as the whole request body instead.
Name the file in a Content-Disposition header and give the body a content type, because a body
sent as application/x-www-form-urlencoded carries no file and is answered 400 missing_file.
curl -X POST https://resume-parser.affinda.com/v1/resumes/parse \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/pdf" \
-H 'Content-Disposition: attachment; filename="resume.pdf"' \
--data-binary @resume.pdf
There is no JSON or base64 intake. Post the file, not a wrapper around it.
3. Read the response
You get { "data": ..., "meta": ... } back. data holds the person and contact block, work
history with date precision, education, certifications, skills and languages. meta holds
meta.document.classification, which says whether the parser believes this was a resume at all,
and meta.document.extractionQuality, which says how cleanly the text came out.
{
"data": {
"person": { "name": { "given": "Alex", "family": "Rivera" } },
"contact": { "emails": ["alex.rivera@example.com"] },
"workExperience": [
{ "jobTitle": "Senior Backend Engineer", "organization": "Northwind Data" }
]
},
"meta": {
"schemaVersion": "1.0",
"document": {
"classification": { "label": "resume", "confidence": 0.683 },
"extractionQuality": { "band": "high", "score": 0.9384 }
}
}
}
That is the real shape, cut down hard. The parse response walks through the whole thing.
Two habits worth forming now:
- Read fields defensively. A field the parser did not find is left out rather than returned
as
null, so use.get()in Python and optional chaining in TypeScript. - Read
X-Credits-Remaining. The response header carries your balance after the parse was charged. One successful parse costs one credit whatever the page count, and a failed parse costs nothing.
Nothing is stored: there is no document to fetch afterwards.
The same request, self-hosted
Point the same code at your container and drop the Authorization header. The container is
licensed at the container, so its requests carry no bearer token.
docker run -d --name affinda-parser \
-e AFFINDA_LICENSE_TOKEN='<your-license-token>' \
-p 8080:8080 \
affinda/resume-parser
curl -X POST http://localhost:8080/v1/resumes/parse \
-F "file=@resume.pdf"
The container answers both /v1/resumes/parse and its own shorter /v1/parse, so the only
edits are the base URL and the missing header. Self-hosted covers the license,
the remaining differences and what the container adds.
Next
- Authentication for keys and regions.
- API reference for every parameter, header and status code.
- Errors and limits for what can go wrong and what it costs.