Spaces:
Running
SmartHire AI β REST API Reference
Base URL: http://localhost:8000
Interactive Docs: http://localhost:8000/docs
Redoc: http://localhost:8000/redoc
Start the API
# Install new dependencies first (one time)
pip install fastapi uvicorn[standard] python-multipart
# Start the API server
uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload
# Or double-click RUN_API.bat on Windows
The Streamlit UI still runs separately:
streamlit run app/streamlit_app.py # port 8501
uvicorn api.main:app --port 8000 # port 8000
Endpoints
Health
| Method | Endpoint | Description |
|---|---|---|
| GET | / |
Root β confirms server is running |
| GET | /health |
Health check with timestamp |
| GET | /model/info |
Loaded model metadata |
Core Matching
POST /match
Match resumes against a job description. Returns ranked candidates.
Form fields:
| Field | Type | Required | Description |
|---|---|---|---|
resumes |
File(s) | β | PDF, DOCX, or TXT resume files |
jd_text |
string | one of | JD as plain text |
jd_file |
File | one of | JD as file |
similarity_weight |
float | β | 0.5β0.9, default 0.7 |
Example (JavaScript fetch):
const form = new FormData();
form.append("resumes", resumeFile1);
form.append("resumes", resumeFile2);
form.append("jd_text", "We are looking for a Python ML Engineer...");
form.append("similarity_weight", "0.7");
const res = await fetch("http://localhost:8000/match", {
method: "POST",
body: form,
});
const data = await res.json();
Response:
{
"status": "success",
"duration_sec": 1.23,
"total_candidates": 2,
"summary": {
"total_candidates": 2,
"average_score": 72.5,
"highest_score": 85.0,
"highly_recommended": 1,
"recommended": 1,
"consider": 0,
"not_recommended": 0
},
"candidates": [
{
"rank": 1,
"name": "John_Doe",
"score_pct": 85.0,
"semantic_similarity": 91.2,
"skill_coverage_pct": 75.0,
"recommendation": "Highly Recommended",
"confidence": "High",
"percentile_rank": 100.0,
"matching_skills": ["python", "pytorch", "docker"],
"missing_skills": ["kubernetes"],
"critical_missing": [],
"important_missing": ["kubernetes"],
"resume_only_skills": ["flask", "pandas"],
"ai_insight": "Strong contextual alignment..."
}
],
"parse_errors": []
}
Skills
POST /skills
Extract and compare skills from a single resume vs JD.
Form fields:
| Field | Type | Required | Description |
|---|---|---|---|
resume |
File | β | Resume file |
jd_text |
string | β | JD text |
Response:
{
"status": "success",
"candidate": "John_Doe",
"matching_skills": ["python", "pytorch"],
"missing_skills": ["kubernetes"],
"critical_missing": [],
"skill_coverage_pct": 75.0,
"weighted_coverage_pct": 80.0,
"jd_skills": ["python", "pytorch", "kubernetes"],
"resume_skills": ["python", "pytorch", "flask"]
}
Vector Index
POST /index/build
Encode and store resumes in the persistent vector index.
| Field | Type | Required | Description |
|---|---|---|---|
resumes |
File(s) | β | Resume files to index |
rebuild |
bool | β | Clear index first (default false) |
POST /index/search
Instantly search the index for the best matching resumes.
| Field | Type | Required | Description |
|---|---|---|---|
jd_text |
string | one of | JD text |
jd_file |
File | one of | JD file |
top_k |
int | β | Number of results (default 5, max 20) |
Response:
{
"status": "success",
"duration_ms": 12.4,
"total_found": 2,
"results": [
{
"rank": 1,
"name": "John_Doe",
"similarity_pct": 95.8,
"indexed_at": "2026-07-01T20:29:18",
"text_length": 1763,
"embedding_dim": 768,
"preview": "john doe machine learning engineer..."
}
]
}
GET /index/info
Get index stats (count, backend, dim, etc.)
GET /index/candidates
List all indexed candidates with metadata.
POST /index/add
Add a single resume to the existing index without rebuilding.
DELETE /index/clear
Wipe the entire index.
Utilities
POST /parse
Parse a file and return raw + cleaned text. Good for debugging.
POST /embed
Encode any text and return its raw embedding vector.
Frontend Integration (React/Next.js example)
// api/smarthire.js
const BASE_URL = "http://localhost:8000";
// Match resumes against a JD
export async function matchResumes(resumeFiles, jdText, similarityWeight = 0.7) {
const form = new FormData();
resumeFiles.forEach(f => form.append("resumes", f));
form.append("jd_text", jdText);
form.append("similarity_weight", similarityWeight);
const res = await fetch(`${BASE_URL}/match`, { method: "POST", body: form });
if (!res.ok) throw new Error(await res.text());
return res.json();
}
// Build vector index
export async function buildIndex(resumeFiles, rebuild = false) {
const form = new FormData();
resumeFiles.forEach(f => form.append("resumes", f));
form.append("rebuild", rebuild);
const res = await fetch(`${BASE_URL}/index/build`, { method: "POST", body: form });
if (!res.ok) throw new Error(await res.text());
return res.json();
}
// Search the vector index
export async function searchIndex(jdText, topK = 5) {
const form = new FormData();
form.append("jd_text", jdText);
form.append("top_k", topK);
const res = await fetch(`${BASE_URL}/index/search`, { method: "POST", body: form });
if (!res.ok) throw new Error(await res.text());
return res.json();
}
// Get model info
export async function getModelInfo() {
const res = await fetch(`${BASE_URL}/model/info`);
return res.json();
}
CORS
By default the API allows all origins (*).
For production, update allow_origins in api/main.py:
app.add_middleware(
CORSMiddleware,
allow_origins=["https://your-frontend.com"],
...
)