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.
Building with a coding agent?
Paste this whole reference with integration rules into Claude Code, Codex or Cursor, or add it to your agent as a skill.
Add skill copies this command. Run it in your project:
$ npx skills add dolevhayut/resurfaceWorks with Claude Code, Codex, Cursor and other agents. In the Claude apps, download the skill and upload it under Skills in settings. In Claude Code, the Resurface plugin adds the skill and MCP tools that score resumes directly.
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_idquestionsRetrying after a timeout? Send an Idempotency-Key header with a unique value per resume. Repeating the request with the same key within 24 hours returns the original result (with Idempotent-Replayed: true) instead of scoring and charging again.
curl 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
import { readFile } from "node:fs/promises";
const form = new FormData();
form.append("job_title", "Senior Full Stack Engineer");
form.append("job_description", await readFile("job.txt", "utf8"));
// PDF, DOCX, HTML or TXT
form.append("resume", new Blob([await readFile("candidate.pdf")]), "candidate.pdf");
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 },
"model": "jev-1.13.0",
"billing": { "billable": false, "price_usd": 0, "free_remaining": 49 },
"latency_ms": 640
}scorematchcoveragerangequestions[].verdictquestions[].evidencequestions[].probabilitiesquestions[].computedmust_have_gapsmust_have_contradictedrubricresumemodelbillinglatency_msPOST /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, "credits_remaining": 500, "scores": { "total": 13, "this_month": 13, "billable": 0 }, "price_per_score_usd": 0.1, "amount_due_usd": 0 }Errors
400401402404413 / 415429409502504{ "error": { "code": "free_tier_exhausted", "message": "…" } }Claude & MCP
Score resumes from Claude Code or any MCP client. The MCP server calls the same API with the same key, so free scores, billing, limits and errors are the same as above.
Claude plugin
Resurface is in Claude's plugin directory: in Claude, open Customize, then Plugins, and search for Resurface. In Claude Code you can also install it with the commands below.
The plugin adds the MCP server and two skills: screen-candidates ranks a batch of resumes with quoted evidence, and resurface-api helps you build the API into your product. The first time Claude uses it, you sign in with your Resurface account (the same one as the dashboard); there is no key to copy.
/plugin marketplace add dolevhayut/resurface /plugin install resurface@resurface
Then ask, for example: “Rank the resumes in ./candidates against this job description.”
Any MCP client
Streamable HTTP at https://getresurface.dev/api/mcp. Clients that support MCP authorization sign in with your Resurface account (OAuth). Scripts, CI and other clients send your API key in the Authorization header instead.
claude mcp add --transport http resurface https://getresurface.dev/api/mcp
claude mcp add --transport http resurface https://getresurface.dev/api/mcp \ --header "Authorization: Bearer rs_live_…"
{
"mcpServers": {
"resurface": {
"url": "https://getresurface.dev/api/mcp"
}
}
}score_resumedraft_screening_questionsget_usagePricing & limits
50 free resume scores per developer, then prepaid score packs: 100 scores for $10, 500 for $40 or 2,500 for $150 (bought in the dashboard; prepaid scores are used after free ones). 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. Two-column PDFs are extracted in reading order.