API Reference — v1

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.

Base URL
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.

Verify your key — GET /user
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()
Keep your key server-side. Never embed it in browser JavaScript or mobile apps — anyone with the key can spend your credits.

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 file1 credits
POST /parse-resume (+ async) — scanned / image1 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 shape
{
  "error": {
    "code": "insufficient_credits",
    "message": "Insufficient credits remaining on this subscription."
  }
}
Validation error (422)
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.
POST /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.
POST /v1/matching-score
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"])
Response — 200 OK
{
  "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
}
POST /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.
POST /v1/parse-resume
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"])
Response — 200 OK (abridged)
{
  "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.

POST /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.

POST /v1/matching-score/async
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"])
Response — 202 Accepted
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"
}
POST /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.

POST /v1/parse-resume/async
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"])
GET /v1/jobs/{job_id}

Poll an async job

Returns the current state of an async job: queuedprocessingcompleted 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.

GET /v1/jobs/{job_id}
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"])
Response — completed
{
  "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"
}
Response — failed (refunded)
{
  "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.