# SmartHire AI — REST API Reference Base URL: `http://localhost:8000` Interactive Docs: `http://localhost:8000/docs` Redoc: `http://localhost:8000/redoc` --- ## Start the API ```bash # 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: ```bash 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):** ```javascript 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:** ```json { "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:** ```json { "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:** ```json { "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) ```javascript // 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`: ```python app.add_middleware( CORSMiddleware, allow_origins=["https://your-frontend.com"], ... ) ```