Work & Workforce Engineering · API

Skill Proficiency Analysis API

Upload resume or documents to extract, score, and band skills into Expert, Advanced, Proficient, and Beginner proficiency levels - assembled on demand from your uploaded documents.

Documentation Pricing
POST /v1/skill-proficiency/assess
Auth Bearer token Content-Type multipart/form-data Status Stable · v1 Max file size 10 MB

When to use this API

Use this API when you need to assess the skill depth of a candidate from their resume - without writing your own extraction or scoring pipeline.

The API accepts PDF, Word, and ZIP files, extracts every skill signal it finds, and returns a structured four-band breakdown (L1-L4) ranked by proficiency score. When multiple documents are submitted, skills are automatically aggregated and the highest score per skill is kept.

Authentication

All requests require a user-scoped API key passed as a bearer token. Generate one in the platform under Settings → API Keys. Keys are prefixed ok_ and expire after 90 days.

Authorization header
Authorization: Bearer ok_xxxxxxxxxxxxxxxxxxxxxxxx
Heads-up. API keys are tied to the user account that generated them. Credit deductions are applied to that user's balance. A key that is revoked or expired returns 401 unauthorized.

Parameters

Form fields

NameTypeRequiredDescription
files file (one or more) required One or more resume or document files. Accepted formats: .pdf, .doc, .docx, .zip. ZIP archives are extracted server-side - every supported document inside is processed individually and results are merged. macOS metadata files (__MACOSX/, ._*, .DS_Store) are ignored automatically.

File constraints

ConstraintLimit
Max file size10 MB per file
Max files per request20 files
Accepted formats.pdf · .doc · .docx · .zip

Request

# Single resume
curl -X POST https://platform.openknowra.ai/api/bff/v1/skill-proficiency/assess \
  -H "Authorization: Bearer $OPENKNOWRA_API_KEY" \
  -F "files=@/path/to/resume.pdf"

# ZIP archive (batch)
curl -X POST https://platform.openknowra.ai/api/bff/v1/skill-proficiency/assess \
  -H "Authorization: Bearer $OPENKNOWRA_API_KEY" \
  -F "files=@candidates_batch.zip"
import os
import requests

API_KEY = os.environ["OPENKNOWRA_API_KEY"]
API_URL = "https://platform.openknowra.ai/api/bff/v1/skill-proficiency/assess"

def analyse_resume(file_path: str) -> dict:
    with open(file_path, "rb") as f:
        res = requests.post(
            API_URL,
            headers={"Authorization": f"Bearer {API_KEY}"},
            files={"files": f},
        )
    if res.status_code == 402:
        err = res.json()["error"]
        raise ValueError(
            f"Insufficient credits: need {err['required']}, have {err['balance']}"
        )
    res.raise_for_status()
    return res.json()

result = analyse_resume("./candidate.pdf")
for band in result["bands"]:
    if band["skills"]:
        print(f"{band['level']} {band['label']}: {', '.join(band['skills'])}")
import fs from "fs";

const API_KEY = process.env.OPENKNOWRA_API_KEY!;
const API_URL = "https://platform.openknowra.ai/api/bff/v1/skill-proficiency/assess";

async function analyseResume(filePath: string) {
  const form = new FormData();
  const buffer = fs.readFileSync(filePath);
  form.append("files", new Blob([buffer]), "resume.pdf");

  const res = await fetch(API_URL, {
    method: "POST",
    headers: { Authorization: `Bearer ${API_KEY}` },
    body: form,
  });

  if (res.status === 402) {
    const err = await res.json() as { error: { required: number; balance: number } };
    throw new Error(
      `Insufficient credits: need ${err.error.required}, have ${err.error.balance}`
    );
  }

  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

const result = await analyseResume("./candidate.pdf");
console.log(JSON.stringify(result.bands, null, 2));

Response

200 OK - Returns a structured proficiency breakdown across all skills found in the uploaded documents. All four bands are always present even when empty, so it is safe to iterate without checking for missing keys.

application/json
{
  "success": true,
  "bands": [
    {
      "level": "L4",
      "label": "Expert",
      "skills": ["Python", "Machine Learning", "TensorFlow", "Data Modelling"]
    },
    {
      "level": "L3",
      "label": "Advanced",
      "skills": ["React", "Node.js", "PostgreSQL", "REST API Design"]
    },
    {
      "level": "L2",
      "label": "Proficient",
      "skills": ["Docker", "Kubernetes", "Redis"]
    },
    {
      "level": "L1",
      "label": "Beginner",
      "skills": ["Rust", "GraphQL"]
    }
  ],
  "creditsUsed": 2.5,
  "fileCount": 1,
  "lambdaResponse": {}
}

Proficiency bands

LevelLabelMeaning
L4Expert Deep mastery. Extensive hands-on evidence, leadership or specialisation across multiple roles or projects.
L3Advanced Strong, practised competency. Consistent use across multiple contexts with clear ownership and outcomes.
L2Proficient Working knowledge. Skill is applied with clear practical examples but lacks the breadth or depth of higher bands.
L1Beginner Foundational exposure. Skill is mentioned but with limited supporting evidence of hands-on application.
-Not returned Insufficient evidence. Skills scoring below 50 are excluded from the response entirely.

Errors

StatusCodeMeaning
400-No files provided, or no supported files found after ZIP extraction.
401unauthorizedMissing, invalid, expired, or revoked API key.
402insufficient_creditsAccount balance too low. Response includes required (credits needed) and balance (current balance).
413-A file exceeds the 10 MB per-file size limit.
500-Extraction engine error. Safe to retry after a short delay.
503skill_proficiency_not_configuredThe extraction service is not enabled in this environment.
402 - insufficient_credits
{
  "error": {
    "code":     "insufficient_credits",
    "required": 25,
    "balance":  10
  }
}