How to Build a Claude-Powered Interview Prep Tool
Building a Claude-powered interview prep tool means using Claude to generate role-specific questions, run a conversational mock interview, and score the candidate's answers against a rubric — all through an API you call from your app. The core architecture is simple: a prompt that sets interviewer behavior, a loop that keeps conversation state, and a structured scoring step that turns Claude's feedback into something your UI can render (a score, strengths, gaps).
This guide walks through the practical pieces: designing the interviewer persona, generating tailored questions from a job description or resume, running the back-and-forth interview, extracting structured feedback, and keeping latency and cost under control when you ship it to real users.
Define what "interview prep" actually means in your product
Before writing any code, decide which of these three modes you're building — each needs a different prompt strategy:
- Question bank generator — paste a job description, get 10–15 relevant questions (behavioral, technical, system design).
- Live mock interview — Claude plays the interviewer, asks follow-ups, reacts to weak answers.
- Answer grader — candidate pastes an answer, Claude scores it and explains what's missing.
Most tools combine all three: generate questions, run the mock interview, then grade transcripts at the end. Build them as separate, composable calls rather than one giant prompt — it's easier to test and cheaper to run.
Step 1: Generate role-specific questions
Feed Claude the job description (or a role + seniority level) and ask for structured output. Keep the system prompt narrow so it doesn't wander into generic advice.
curl https://api.subtoapi.app/v1/messages \
-H "Authorization: Bearer $SUBTOAPI_KEY" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-4",
"max_tokens": 800,
"system": "You are a technical interview question generator. Output only valid JSON, no commentary.",
"messages": [
{"role": "user", "content": "Job description: Senior Backend Engineer, Go, distributed systems, AWS. Generate 8 questions as JSON: [{\"category\":...,\"question\":...,\"difficulty\":...}]"}
]
}'
Parse the JSON on your side and store it against the session — this becomes the question queue for the live interview.
Step 2: Run the mock interview as a stateful conversation
A mock interview is just a multi-turn Claude conversation where you control the system prompt and inject the question queue. The key design decision is who decides when to move to the next question — let Claude decide based on answer quality, or enforce it from your app logic. Letting Claude decide (with a tool call) gives a more natural interview feel.
const res = await fetch("https://api.subtoapi.app/v1/messages", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4",
max_tokens: 600,
system: `You are a senior engineering interviewer. Ask one question at a time from the provided queue.
Ask a follow-up if the answer is vague. Be direct, not encouraging. Never reveal scoring criteria.`,
messages: conversationHistory, // includes prior Q&A turns
}),
});
Stream the response so the candidate sees the interviewer "typing" in real time instead of waiting for the full reply — this matters a lot for perceived quality in a conversational product. See /docs/streaming for the event format and how to wire it into a chat UI.
Step 3: Score answers with structured output, not free text
At the end of the interview (or after each answer), run a separate grading call. Use tool use to force Claude to return a fixed schema instead of prose you'd have to regex-parse. This is the single biggest reliability improvement you can make to a feedback feature.
{
"name": "score_answer",
"description": "Score a candidate's interview answer",
"input_schema": {
"type": "object",
"properties": {
"score": { "type": "integer", "minimum": 1, "maximum": 10 },
"strengths": { "type": "array", "items": { "type": "string" } },
"gaps": { "type": "array", "items": { "type": "string" } },
"follow_up_recommended": { "type": "boolean" }
},
"required": ["score", "strengths", "gaps", "follow_up_recommended"]
}
}
Pass this tool definition in the request and set tool_choice to force its use. Full examples and error-handling patterns for tool calls are in /docs/tools. The payoff is that your frontend never has to parse free text — it just renders the JSON fields directly into a scorecard component.
Step 4: Manage session state and transcripts
Store each interview as a transcript: question, answer, score, gaps. This is useful beyond the single session — it lets you show progress over multiple practice interviews, which is the feature that actually keeps users coming back. You don't need a vector database for this; a normal table with session_id, turn_index, role, content, and score columns is enough for most products.
Step 5: Ship it without building your own API layer
If you're prototyping or shipping a small product, you don't need to manage Anthropic account setup, billing, or rate-limit handling yourself. SubToAPI turns an existing Claude subscription into a standard HTTPS API with sub_live_... keys, so you can call /v1/messages directly from your backend or serverless functions without separate API billing infrastructure.
Why this matters for an interview prep tool specifically:
- Streaming is built in, so the live mock-interview UI feels conversational rather than batchy — see /docs/streaming.
- Tool use works the same way as above for scoring, with per-call usage metadata so you can track cost per interview session.
- Team seats matter if you're building this inside a company (e.g., an internal recruiting tool) — Team plans at €19/seat let multiple recruiters or engineers share one account with individual keys and usage visibility.
Get started with a free trial at /signup, check /pricing for plan details, and the fastest path to a working call is /docs/quickstart.
Common pitfalls to avoid
- Letting the interviewer prompt leak scoring criteria. Keep the rubric in a separate system prompt used only for the grading call, never the interview call.
- Not capping turn length. Set
max_tokensconservatively per turn (400–700 is usually enough) so a single rambling answer doesn't eat your budget. - Skipping structured output for scores. Free-text feedback looks nice in a demo and breaks the moment you try to build a dashboard on top of it.
- Treating one long conversation as infinite context. For long interviews, summarize earlier turns into a compact history after 8–10 exchanges to keep token costs predictable — see /docs/messages for how message arrays are structured.
Questions
Do I need my own Anthropic API account to build this? No — SubToAPI lets you expose Claude access you already have as a standard API, so you can build the interview tool without separately provisioning Anthropic API billing.
Should the mock interview and the scoring use the same API call? No. Keep them separate: the interview call should sound human and conversational, while the scoring call should use tool use to return strict JSON. Mixing the two makes both worse.
How do I keep interview sessions from feeling repetitive? Vary question order and follow-ups based on prior answers rather than always running the full question bank in sequence, and track transcripts across sessions so returning users get different scenarios each time.