API Documentation
Everything you need to integrate AI match scoring and resume parsing: authentication, request formats, and copy-paste examples in cURL, PHP, Node.js, and Python.
On this page
Introduction
The Hiremium API gives your job board or ATS two AI capabilities over plain HTTPS:
- ✓Match scoring — send a job and an application, receive a 0-100 match score with a written reason.
- ✓Resume parsing — upload a CV file (PDF, DOC, DOCX, TXT, or image) and receive a complete structured candidate profile.
All endpoints accept and return JSON (file uploads use multipart/form-data),
are versioned under /v1, and share one API key and one credit pool.
Always send an Accept: application/json header.
https://hiremium.com/api/v1
Authentication
Every request is authenticated with a Bearer token. Generate your API key from the
dashboard
(email verification required) and send it in the Authorization header.
The key is shown once at creation — store it like a password. Regenerating a key immediately revokes the previous one.
curl https://hiremium.com/api/user \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json"
<?php
$ch = curl_init('https://hiremium.com/api/user');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'Accept: application/json',
],
]);
$account = json_decode(curl_exec($ch), true);
curl_close($ch);
const response = await fetch('https://hiremium.com/api/user', {
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Accept': 'application/json',
},
});
const account = await response.json();
import requests
response = requests.get(
"https://hiremium.com/api/user",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/json",
},
)
account = response.json()
Credits & billing
Your subscription includes a credit balance that both features draw from. Each call deducts
its price up front; if the AI engine fails, the deduction is refunded automatically and the
call is logged as refunded.
Successful responses include your remaining balance.
| Call | Cost |
|---|---|
| POST /matching-score (+ async) | 1 credit |
| POST /parse-resume (+ async) — text file | 1 credits |
| POST /parse-resume (+ async) — scanned / image | 1 credits |
| GET /jobs/{id} | Free |
A resume parse costs 1 credits when text can be extracted directly, and
1 credits when the file needs vision processing (scanned PDFs and images).
The response's meta.extraction_path
tells you which path was used.
Rate limits
The API allows 60 requests per minute per account. Exceeding it returns
429 with code
rate_limited —
back off and retry after a few seconds. For bursts larger than the limit
(e.g. scoring a whole applicant pool), use the async endpoints and poll for results.
Errors
Errors use conventional HTTP status codes and always carry a machine-readable
error.code
plus a human-readable message:
{
"error": {
"code": "insufficient_credits",
"message": "Insufficient credits remaining on this subscription."
}
}
HTTP/1.1 422 Unprocessable Entity
{
"error": {
"code": "validation_failed",
"message": "The given data was invalid.",
"errors": {
"job.title": ["The job.title field is required."]
}
}
}
| Status | Code | When |
|---|---|---|
| 401 | unauthenticated | The API key is missing, invalid, or revoked. |
| 402 | no_active_subscription | The account has no active subscription. |
| 402 | insufficient_credits | The credit balance cannot cover this call. |
| 403 | job_access_denied | The job ID belongs to another account. |
| 404 | job_not_found | No async job exists for that ID. |
| 415 | unsupported_file_type | The uploaded file type is not supported. |
| 422 | validation_failed | The request body failed validation (details in errors). |
| 422 | file_processing_failed | The file was accepted but could not be read (corrupt/empty). |
| 429 | rate_limited | More than 60 requests in a minute. |
| 502 | ai_provider_error | The AI engine failed — the credit is refunded automatically. |
/v1/matching-score
Score an application against a job
Evaluates how well a candidate fits a vacancy and returns a 0-100 score with a short
written reason. Provide the application as text fields, attach a resume file, or both —
at minimum, either application.resume_text
or resume_file is required.
Costs 1 credit.
Request body
| Field | Type | Description |
|---|---|---|
| job.title | string — required | Job title. Max 255 chars. |
| job.description | string — required | Full job description. Max 20,000 chars. |
| job.requirements | string — optional | Requirements text. Max 10,000 chars. |
| job.skills[] | string[] — optional | Required skills, each max 100 chars. |
| application.name | string — optional | Candidate name. Max 255 chars. |
| application.email | string — optional | Candidate email. |
| application.resume_text | string — required* | Resume as plain text, max 30,000 chars. *Required unless resume_file is attached. |
| application.cover_letter | string — optional | Cover letter text. Max 10,000 chars. |
| application.experience_years | number — optional | Years of experience, 0-80. |
| application.skills[] | string[] — optional | Candidate skills, each max 100 chars. |
| resume_file | file — optional | CV file: PDF, DOC, DOCX, or TXT, max 3 MB. Send the request as multipart/form-data when attaching it. |
curl -X POST https://hiremium.com/api/v1/matching-score \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"job": {
"title": "Senior PHP Engineer",
"description": "We are hiring a senior engineer for our SaaS platform...",
"skills": ["PHP", "Redis"]
},
"application": {
"name": "Jane Doe",
"resume_text": "8 years building backend APIs...",
"experience_years": 8,
"skills": ["PHP", "MySQL"]
}
}'
<?php
$payload = [
'job' => [
'title' => 'Senior PHP Engineer',
'description' => 'We are hiring a senior engineer for our SaaS platform...',
'skills' => ['PHP', 'Redis'],
],
'application' => [
'name' => 'Jane Doe',
'resume_text' => '8 years building backend APIs...',
'experience_years' => 8,
'skills' => ['PHP', 'MySQL'],
],
];
$ch = curl_init('https://hiremium.com/api/v1/matching-score');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $result['score']; // 87
echo $result['reason']; // "Strong match: ..."
const response = await fetch('https://hiremium.com/api/v1/matching-score', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
job: {
title: 'Senior PHP Engineer',
description: 'We are hiring a senior engineer for our SaaS platform...',
skills: ['PHP', 'Redis'],
},
application: {
name: 'Jane Doe',
resume_text: '8 years building backend APIs...',
experience_years: 8,
skills: ['PHP', 'MySQL'],
},
}),
});
const result = await response.json();
console.log(result.score, result.reason);
import requests
payload = {
"job": {
"title": "Senior PHP Engineer",
"description": "We are hiring a senior engineer for our SaaS platform...",
"skills": ["PHP", "Redis"],
},
"application": {
"name": "Jane Doe",
"resume_text": "8 years building backend APIs...",
"experience_years": 8,
"skills": ["PHP", "MySQL"],
},
}
response = requests.post(
"https://hiremium.com/api/v1/matching-score",
json=payload,
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/json",
},
)
result = response.json()
print(result["score"], result["reason"])
{
"score": 87,
"reason": "Strong match: 8 years of backend experience covers the core stack; Redis exposure present. Gap: no explicit team-lead experience mentioned.",
"credits_remaining": 99
}
/v1/parse-resume
Parse a resume file into structured data
Upload a CV and receive a complete structured candidate profile. Text-based files are parsed
directly (1 credits); scanned PDFs and images go through vision
processing (1 credits) — the path is chosen automatically and
reported in meta.extraction_path.
Uploaded files are deleted right after processing.
Request body — multipart/form-data
| Field | Type | Description |
|---|---|---|
| file | file — required | The resume: PDF, DOC, DOCX, TXT, JPG, PNG, WebP, or GIF. Max 5 MB. |
curl -X POST https://hiremium.com/api/v1/parse-resume \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json" \ -F "file=@jane-doe-cv.pdf"
<?php
$ch = curl_init('https://hiremium.com/api/v1/parse-resume');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('jane-doe-cv.pdf'),
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $result['data']['fullName']; // "Jane Doe"
echo $result['meta']['credits_remaining']; // 97
import { openAsBlob } from 'node:fs';
const form = new FormData();
form.append('file', await openAsBlob('jane-doe-cv.pdf'), 'jane-doe-cv.pdf');
const response = await fetch('https://hiremium.com/api/v1/parse-resume', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Accept': 'application/json',
},
body: form,
});
const result = await response.json();
console.log(result.data.fullName, result.meta.credits_remaining);
import requests
with open("jane-doe-cv.pdf", "rb") as f:
response = requests.post(
"https://hiremium.com/api/v1/parse-resume",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/json",
},
files={"file": f},
)
result = response.json()
print(result["data"]["fullName"], result["meta"]["credits_remaining"])
{
"data": {
"fullName": "Jane Doe",
"email": "jane@doe.dev",
"phone": "+1 555 010 1234",
"jobTitle": "Senior PHP Engineer",
"gender": "",
"dateOfBirth": "",
"websiteUrl": "https://janedoe.dev",
"linkedinUrl": "https://linkedin.com/in/janedoe",
"summary": "Senior engineer with 8 years of backend engineering...",
"experience": [
{
"company": "Acme Corp",
"title": "Senior PHP Engineer",
"location": "Remote",
"startDate": "2019-03",
"endDate": "Present",
"description": "- Led a major platform migration\n- Cut API p95 latency by 40%"
}
],
"education": [
{
"institution": "State University",
"degree": "BSc",
"field": "Computer Science",
"location": "Boston, MA",
"startDate": "2011-09",
"endDate": "2015-06",
"grade": "3.8 GPA"
}
],
"projects": [ ... ],
"certificates": [ ... ],
"awards": [ ... ],
"expertise": [
{ "name": "Backend Development", "level": 5 },
{ "name": "API Design", "level": 4 }
],
"languages": [
{ "name": "English", "level": 5 },
{ "name": "Spanish", "level": 3 }
],
"skills": ["PHP", "Redis", "MySQL", "Docker"]
},
"meta": {
"extraction_path": "text",
"credits_consumed": 1,
"credits_remaining": 97
}
}
Every field is always present: missing information comes back as an empty string, empty array, or 0 — nothing is invented.
Dates are normalised to YYYY-MM, ongoing roles use
"Present", and
expertise /
languages levels run 1-5.
/v1/matching-score/async
Score in the background
Identical request format to the sync endpoint, but returns immediately with
202 Accepted and a job ID
to poll.
Ideal for scoring a whole applicant pool when a vacancy closes. The credit is charged at dispatch
and refunded automatically if processing ultimately fails.
curl -X POST https://hiremium.com/api/v1/matching-score/async \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"job": { "title": "Senior PHP Engineer", "description": "..." },
"application": { "resume_text": "..." }
}'
<?php
// Same payload as the sync endpoint — only the URL changes.
$ch = curl_init('https://hiremium.com/api/v1/matching-score/async');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $job['job_id']; // poll this
echo $job['status_url']; // ...or GET this URL directly
// Same payload as the sync endpoint — only the URL changes.
const response = await fetch('https://hiremium.com/api/v1/matching-score/async', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(payload),
});
const job = await response.json();
console.log(job.job_id, job.status_url);
# Same payload as the sync endpoint — only the URL changes.
response = requests.post(
"https://hiremium.com/api/v1/matching-score/async",
json=payload,
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/json",
},
)
job = response.json()
print(job["job_id"], job["status_url"])
HTTP/1.1 202 Accepted
{
"job_id": "9c2f6a3e-1d4b-4c8a-9e21-7f3a5b0d8c11",
"endpoint": "matching-score.v1",
"status": "queued",
"queued_at": "2026-07-23T09:15:04+00:00",
"started_at": null,
"finished_at": null,
"result": null,
"error": null,
"credits": { "charged": 1, "refunded": 0 },
"status_url": "https://hiremium.com/api/v1/jobs/9c2f6a3e-1d4b-4c8a-9e21-7f3a5b0d8c11"
}
/v1/parse-resume/async
Parse in the background
Same upload format as the sync endpoint, returning 202 Accepted
with a job ID — perfect for bulk-importing an existing CV archive. The cost
(1 or 1 credits, depending on the file)
is determined and charged at dispatch.
curl -X POST https://hiremium.com/api/v1/parse-resume/async \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json" \ -F "file=@jane-doe-cv.pdf"
<?php
$ch = curl_init('https://hiremium.com/api/v1/parse-resume/async');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('jane-doe-cv.pdf'),
],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $job['job_id'];
import { openAsBlob } from 'node:fs';
const form = new FormData();
form.append('file', await openAsBlob('jane-doe-cv.pdf'), 'jane-doe-cv.pdf');
const response = await fetch('https://hiremium.com/api/v1/parse-resume/async', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Accept': 'application/json',
},
body: form,
});
const job = await response.json();
console.log(job.job_id);
import requests
with open("jane-doe-cv.pdf", "rb") as f:
response = requests.post(
"https://hiremium.com/api/v1/parse-resume/async",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/json",
},
files={"file": f},
)
job = response.json()
print(job["job_id"])
/v1/jobs/{job_id}
Poll an async job
Returns the current state of an async job:
queued →
processing →
completed or
failed.
On completion, result holds the same
payload the sync endpoint would return. On failure, credits are refunded and
error explains why.
Polling is free and works even after your subscription's credits run out —
a 2-5 second interval is plenty.
curl https://hiremium.com/api/v1/jobs/9c2f6a3e-1d4b-4c8a-9e21-7f3a5b0d8c11 \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json"
<?php
$ch = curl_init('https://hiremium.com/api/v1/jobs/'.$jobId);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_KEY',
'Accept: application/json',
],
]);
$job = json_decode(curl_exec($ch), true);
curl_close($ch);
if ($job['status'] === 'completed') {
print_r($job['result']);
}
const response = await fetch(`https://hiremium.com/api/v1/jobs/${jobId}`, {
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Accept': 'application/json',
},
});
const job = await response.json();
if (job.status === 'completed') {
console.log(job.result);
}
import requests
response = requests.get(
f"https://hiremium.com/api/v1/jobs/{job_id}",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Accept": "application/json",
},
)
job = response.json()
if job["status"] == "completed":
print(job["result"])
{
"job_id": "9c2f6a3e-1d4b-4c8a-9e21-7f3a5b0d8c11",
"endpoint": "matching-score.v1",
"status": "completed",
"queued_at": "2026-07-23T09:15:04+00:00",
"started_at": "2026-07-23T09:15:05+00:00",
"finished_at": "2026-07-23T09:15:09+00:00",
"result": {
"score": 91,
"reason": "Excellent skills coverage and seniority...",
"credits_remaining": 98
},
"error": null,
"credits": { "charged": 1, "refunded": 0 },
"status_url": "https://hiremium.com/api/v1/jobs/9c2f6a3e-1d4b-4c8a-9e21-7f3a5b0d8c11"
}
{
"job_id": "f81a2b90-3c5d-4e6f-8a7b-1c2d3e4f5a6b",
"endpoint": "resume-parse.v1",
"status": "failed",
"result": null,
"error": {
"code": "processing_failed",
"message": "The file could not be read. It may be corrupt or empty."
},
"credits": { "charged": 2, "refunded": 2 },
"status_url": "https://hiremium.com/api/v1/jobs/f81a2b90-3c5d-4e6f-8a7b-1c2d3e4f5a6b"
}
Ready to make your first call?
Create an account, generate an API key on the dashboard, and paste any example above.