Affinda Resume Parser Open console

API reference

The stateless resume parsing API served on resume-parser.affinda.com.

Post a file, get the parsed resume back. Nothing is stored: the API keeps no copy of your file and no record of the result, so there is nothing to poll, list or delete afterwards. One request is one parse, and one successful parse costs one credit no matter how many pages the document has.

The same API is available as a self-hosted container, on the same /v1/resumes/parse path. Moving between the two is a change of base URL and authentication. Where the two differ, the parameter or response below says so.

Base URLs

Your account, its credits and its API keys live in one region. Use the host for that region.

RegionBase URL
Asia Pacific and globalhttps://resume-parser.affinda.com
United Stateshttps://resume-parser.us1.affinda.com
Europehttps://resume-parser.eu1.affinda.com

Authentication

An API key created on the console's API keys screen, sent as Authorization: Bearer aff_.... A key is shown in full exactly once, when it is created, and you may hold three at a time.

Keys belong to a user and to the region they were created in. A key made on resume-parser.eu1.affinda.com does not authenticate against the Asia Pacific host, and the reverse is equally true. Sign in on the host for your region and create the key there.

Do not call this endpoint from browser code. The key would be visible to anyone who opened the page, and it also works against app.affinda.com.

Endpoints

Parse a resume

POST/v1/resumes/parse

Parses one resume and returns the extracted fields as JSON.

Send the file either as a multipart/form-data body with the file in a field named file, or as the raw bytes of the file as the whole request body. The raw form is what the self-hosted container accepts, so accepting it here too means moving between the two is a change of base URL and authentication and nothing else. With a raw body, name the file in a Content-Disposition: attachment; filename="cv.pdf" header; without one the parser is told the file is called resume.

There is no JSON or base64 intake. A file up to 20 MB is accepted; anything larger is rejected before the parser is called and costs no credit.

A raw body needs a content type that is not application/x-www-form-urlencoded. That one is parsed as a form, which carries no file, and is answered 400 missing_file.

The path has no trailing slash. POST /v1/resumes/parse/ is not an alias and is not redirected.

Request body

Accepts application/octet-stream, application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, multipart/form-data, text/plain.

Parameters

NameInRequiredDescription
organizationqueryoptional

The identifier of the organization to charge for the parse. Optional when your account has exactly one Resume Parser organization, which is the usual case; required when it has more than one, so that the charge is never guessed. An identifier belonging to an organization you are not in, or to one of your organizations on the main Affinda platform rather than the Resume Parser, is answered 404 organization_not_found.

languagequeryoptional

Only the literal multi selects the multilingual model. Every other value, including a specific language code such as fr, behaves as en. Omit it unless you want multi.

Content-Dispositionheaderoptional

Names the file when the request body is the raw bytes of the document, as attachment; filename="cv.pdf". Ignored for multipart requests, where the file part carries its own name. The filename is a format hint and is not stored.

Responses

StatusMeaning
200

The parsed resume.

400

The request did not carry a usable file, or names no organization when it had to. missing_file means neither a file part nor a non-empty raw body was present, which includes a zero-byte upload and a body sent as application/x-www-form-urlencoded; organization_required means your account has several Resume Parser organizations and the organization query parameter has to say which one pays.

401

The API key is missing, malformed, unknown, expired, or blocked by your organization's IP allowlist. Also returned when a key from another region is used.

402

The organization has no parse credits left, or its credits have expired. The parser is not called and nothing is charged. Top up in the console's Billing screen.

Note this is a 402 on this API specifically. The same condition is a 403 on the main Affinda platform API.

403

The caller was recognised but not allowed. In practice this is an account that requires multi-factor authentication and has none set up; finish setting up MFA in the console.

404

The organization parameter names an organization that is not one of your Resume Parser organizations. An organization you are not a member of and one of your own platform organizations are answered identically, on purpose.

405

Only POST is implemented on this path.

413

The file is larger than the 20 MB limit. The size is checked against the declared Content-Length, against the multipart part, and again against the bytes actually received, so understating the length does not get a larger file through. The parser is not called and no credit is charged.

file_too_large is this API's own rejection. payload_too_large is the parser's, and reaches you only if a document slips past the first check.

415

The document is not a format the parser reads.

Intake is lenient: if a file might hold text, it is attempted. PDF, DOCX, ODT, XLSX, ODS, legacy .doc and .xls, RTF, HTML, SVG, plain text and the common image formats (PNG, JPEG, GIF, TIFF, BMP, WEBP) are all read. Legacy PowerPoint (.ppt) is the one format rejected outright. A self-hosted container with OCR switched off also answers this for images, because it has nothing to read them with.

422

The file was read but no resume could be extracted from it. A file with no text signal and a password-protected document both land here. Remove the password and retry in the second case. A zero-byte upload never reaches the parser and is answered 400 missing_file instead.

429

Too many requests. The limits are 6,000 parses per minute per organization and 6,000 per minute per API key, and the parser itself sheds load when its queue is full. Wait retryAfterSeconds and retry.

500

Something failed unexpectedly. The detail is recorded on our side, not returned.

502

The parser service could not be reached. The request is retried once on a fresh connection before this is returned, because a parse is idempotent and a dropped connection means it never arrived. No credit is charged.

503

The parser instance is shutting down. Retry.

504

The parse did not finish in time. The cloud waits 30 seconds for the parser, and the parser applies its own 28 second deadline, so in practice the parser's deadline is what you see. Unusually heavy documents may take longer than one attempt allows. No credit is charged.

Parse a resume (alias)

POST/api/v1/resumes/parse

The same endpoint as POST /v1/resumes/parse, for callers whose HTTP client expects an /api prefix. It is an alias and not the canonical path: prefer /v1/resumes/parse, which is also the path the self-hosted container serves.

Request body

Accepts application/octet-stream, multipart/form-data.

Parameters

NameInRequiredDescription
organizationqueryoptional

The identifier of the organization to charge for the parse. Optional when your account has exactly one Resume Parser organization, which is the usual case; required when it has more than one, so that the charge is never guessed. An identifier belonging to an organization you are not in, or to one of your organizations on the main Affinda platform rather than the Resume Parser, is answered 404 organization_not_found.

languagequeryoptional

Only the literal multi selects the multilingual model. Every other value, including a specific language code such as fr, behaves as en. Omit it unless you want multi.

Content-Dispositionheaderoptional

Names the file when the request body is the raw bytes of the document, as attachment; filename="cv.pdf". Ignored for multipart requests, where the file part carries its own name. The filename is a format hint and is not stored.

Responses

StatusMeaning
200

The parsed resume.

400

The request did not carry a usable file, or names no organization when it had to. missing_file means neither a file part nor a non-empty raw body was present, which includes a zero-byte upload and a body sent as application/x-www-form-urlencoded; organization_required means your account has several Resume Parser organizations and the organization query parameter has to say which one pays.

401

The API key is missing, malformed, unknown, expired, or blocked by your organization's IP allowlist. Also returned when a key from another region is used.

402

The organization has no parse credits left, or its credits have expired. The parser is not called and nothing is charged. Top up in the console's Billing screen.

Note this is a 402 on this API specifically. The same condition is a 403 on the main Affinda platform API.

The parsed resume

A successful parse answers with two keys, data and meta. These are the members of data.

Schema v1 is sparse. Null members and objects emptied by trimming are dropped before the response is sent, so a key you do not see was not found rather than found empty. Arrays are always present, including when empty, and data and meta are the only two keys guaranteed at the top level.

Certifications are lifted out of education rather than nested inside it: an education record whose level is a course, certificate or conference is re-emitted as a certifications entry.

rawText, redactedText and headshot are large. Ask for them only if you use them.

FieldTypeNotes
person requiredobject

Name parts, date of birth, nationalities and location.

contact requiredobject

Emails, phone numbers and websites.

summarystring
objectivestring
workExperience requiredarray of object
education requiredarray of object
certifications requiredarray of object
skills requiredarray of object
languages requiredarray of object
projects requiredarray of object
publications requiredarray of object
referees requiredarray of object
achievements requiredarray of string
associations requiredarray of string
interests requiredarray of string
patents requiredarray of string
availabilitystring
workAuthorizationstring
willingToRelocateboolean
expectedSalarystring
preferredWorkLocations requiredarray of Location
employmentMetrics requiredobject

Totals derived from the work history, including tenure figures and employment gaps.

headshotstring

Base64 image, when one was found.

rawTextstring
redactedTextstring

Document metadata

meta describes the parse rather than the person. meta.schemaVersion is the string 1.0, meta.timing.totalMs is the server-measured wall time, and meta.document carries the classification and quality readings below.

How well text came out of this document. Populated by default.

FieldTypeNotes
band requiredone of high, medium, low
score requirednumber

Present only when parsing continued past a quality gate that would otherwise have stopped it.

FieldTypeNotes
code requiredone of degenerate_ocr, low_text_quality, ocr_truncated
message requiredstring

Present when the document went through OCR. OCR reads the first three pages only, and truncated says whether more existed. Text documents contribute fields from their first twenty pages.

FieldTypeNotes
routed requiredboolean
sourcePageCount requiredinteger
processedPageCount requiredinteger
pageLimit requiredinteger
truncated requiredboolean
pixelLimit requiredinteger
downscaledPageCount requiredinteger
downscaledAttemptCount requiredinteger

Errors

error is a message safe to show a person, and code is the stable identifier to branch on. retryAfterSeconds is present when waiting is the right response, and the Retry-After header carries the same number.

FieldTypeNotes
error requiredstring
code requiredone of missing_file, organization_required, invalid_request, unauthorized, no_credits, forbidden, organization_not_found, method_not_allowed, file_too_large, payload_too_large, unsupported_format, password_protected_document, missing_file_part, unparseable_document, rate_limited, queue_full, queue_timeout, ocr_queue_full, internal_error, internal, parser_unavailable, shutting_down, parser_timeout, deadline
retryAfterSecondsinteger