API reference
Base URL https://getresurface.dev/api/v1. Requests and responses are JSON, except that /score also accepts multipart/form-data for file uploads.
Authentication
Sign up and create a key in the dashboard (50 free scores). Send it on every request:
Authorization: Bearer rs_live_…
POST /v1/score
Scores one resume against a job. Describe the job in one of three ways. With a job_description, questions are drafted once and cached as a rubric; the response returns its rubric.id so you can reuse it.
resumeresume_textjob_descriptionjob_titlerubric_idquestionscurl https://getresurface.dev/api/v1/score \ -H "Authorization: Bearer $RESURFACE_API_KEY" \ -F job_title="Senior Full Stack Engineer" \ -F job_description=@job.txt \ -F resume=@candidate.pdf
const form = new FormData();
form.append("job_title", "Senior Full Stack Engineer");
form.append("job_description", jobDescription);
form.append("resume", resumeFile); // PDF, DOCX, HTML or TXT
const res = await fetch("https://getresurface.dev/api/v1/score", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.RESURFACE_API_KEY}` },
body: form,
});
const { score, match, questions } = await res.json();import os, requests
r = requests.post(
"https://getresurface.dev/api/v1/score",
headers={"Authorization": f"Bearer {os.environ['RESURFACE_API_KEY']}"},
data={"job_title": "Senior Full Stack Engineer", "job_description": open("job.txt").read()},
files={"resume": open("candidate.pdf", "rb")},
)
print(r.json()["score"], r.json()["match"]){
"object": "score",
"id": "scr_Qm3xk2Lp9aRt",
"score": 92,
"match": "strong_evidence",
"coverage": 0.917,
"range": { "lower": 92, "upper": 100 },
"must_have_gaps": [],
"questions": [
{
"id": "react_in_production",
"question": "Has the candidate built production front-end applications with React?",
"requirement": "must",
"weight": 3,
"verdict": "SUPPORTED",
"status": "supported",
"evidence": { "line": "L006", "text": "- Built dashboards in React and TypeScript for 40k users" },
"probabilities": { "SUPPORTED": 0.97, "INSUFFICIENT_EVIDENCE": 0.03, "CONTRADICTED": 0 },
"confidence": 0.96
}
],
"rubric": { "id": "rub_8fKq2", "source": "rubric_cached", "questions": 6 },
"billing": { "billable": false, "price_usd": 0, "free_remaining": 49 },
"latency_ms": 640
}scorematchcoveragerangequestions[].verdictquestions[].evidencequestions[].probabilitiesPOST /v1/rubrics
Drafts 6–8 atomic screening questions from a job description, each tied to a verbatim span of the posting. Free, and cached per job text. Pass the returned id as rubric_id.
curl https://getresurface.dev/api/v1/rubrics \
-H "Authorization: Bearer $RESURFACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_title": "Data Engineer", "job_description": "Requirements:\n- 3+ years in data engineering\n- Advanced SQL\n- Spark or dbt pipelines"}'GET /v1/rubrics/:id returns a saved rubric.
GET /v1/usage
Free scores remaining, scores this month, and the amount due.
{ "free_remaining": 37, "scores": { "total": 13, "this_month": 13, "billable": 0 }, "price_per_score_usd": 0.1, "amount_due_usd": 0 }Errors
400401402404413 / 415429502{ "error": { "code": "free_tier_exhausted", "message": "…" } }Pricing & limits
50 free resume scores per developer, then $0.10 per score. Rubric drafting is free. Limits: 60 scores per minute, files up to 5 MB, up to 12 custom questions. Scanned (image-only) PDFs are not supported yet. Hebrew and other right-to-left resumes are extracted in correct reading order, including PDFs that mix Hebrew and English.