# Finetune Resume API — Full Documentation > AI-powered resume tailoring API for developers and AI agents ## Introduction Finetune Resume is an AI-powered service that tailors resumes to specific job descriptions. Upload your base resume once through the dashboard, then use the API to generate optimized, ATS-friendly versions for every job you apply to. The API is designed for programmatic access by developers, CI/CD pipelines, and AI agents. It accepts JSON, returns JSON, and follows REST conventions. ## Base URL ``` https://api.finetuneresume.app/api/v1 ``` All endpoints are relative to this base URL. ## Authentication Authenticate every request with an API key sent in the `X-API-Key` header. ``` X-API-Key: ft_live_abc123... ``` **Key formats:** - `ft_live_*` — Production keys. Calls the LLM, deducts credits, generates real PDFs. - `ft_test_*` — Test keys. Returns mock data without calling the LLM or deducting credits. Use for development and CI. Create and manage API keys at: https://finetuneresume.app/developers/keys Each key is scoped to your account. All resumes generated via API are visible in your dashboard. --- ## Endpoints ### POST /resumes/generate Tailor your base resume to a job description. This is the primary endpoint. **Headers:** ``` X-API-Key: ft_live_xxx Content-Type: application/json Idempotency-Key: optional-unique-string ``` **Request body:** ```json { "job_description": "We are looking for a Senior Software Engineer to join our Cloud Platform team at Google. You'll design and build distributed systems...", "company_name": "Google", "position": "Senior Software Engineer", "finetune_level": "good", "include_pdf": true } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `job_description` | string | Yes | Full job description text. Min 50 characters. | | `company_name` | string | Yes | Target company name. | | `position` | string | Yes | Target job title. | | `finetune_level` | string | No | One of `basic`, `good`, `super`. Default: `good`. | | `include_pdf` | boolean | No | Generate a PDF immediately. Default: `false`. | **Response (200):** ```json { "object": "resume", "id": "res_a1b2c3d4e5f6", "company_name": "Google", "position": "Senior Software Engineer", "finetune_level": "good", "ats_score": 87, "resume_data": { "basics": { "name": "Jane Doe", "headline": "Senior Software Engineer", "...": "..." }, "sections": { "summary": { "...": "..." }, "experience": { "...": "..." }, "skills": { "...": "..." }, "education": { "...": "..." } } }, "pdf_url": "https://api.finetuneresume.app/files/res_a1b2c3d4e5f6.pdf", "credits_remaining": 42, "created_at": "2026-02-21T10:30:00Z" } ``` **Idempotency:** Include an `Idempotency-Key` header (any unique string, max 256 chars) to safely retry requests. If a request with the same idempotency key was already processed, the original response is returned without deducting additional credits. Keys expire after 24 hours. --- ### GET /resumes List your generated resumes, newest first. **Query parameters:** | Param | Type | Default | Description | |-------|------|---------|-------------| | `limit` | integer | 20 | Number of results (1-100). | | `after` | string | — | Cursor for next page. Pass the `id` of the last item. | | `before` | string | — | Cursor for previous page. Pass the `id` of the first item. | **Response (200):** ```json { "object": "list", "data": [ { "object": "resume", "id": "res_a1b2c3d4e5f6", "company_name": "Google", "position": "Senior Software Engineer", "finetune_level": "good", "ats_score": 87, "pdf_url": "https://api.finetuneresume.app/files/res_a1b2c3d4e5f6.pdf", "created_at": "2026-02-21T10:30:00Z" } ], "has_more": true } ``` To paginate, pass `after` with the `id` of the last item in `data`: ``` GET /resumes?limit=20&after=res_a1b2c3d4e5f6 ``` --- ### GET /resumes/:id Retrieve a specific resume with full data. **Response (200):** ```json { "object": "resume", "id": "res_a1b2c3d4e5f6", "company_name": "Google", "position": "Senior Software Engineer", "finetune_level": "good", "ats_score": 87, "resume_data": { "basics": { "...": "..." }, "sections": { "...": "..." } }, "pdf_url": "https://api.finetuneresume.app/files/res_a1b2c3d4e5f6.pdf", "created_at": "2026-02-21T10:30:00Z" } ``` --- ### POST /resumes/:id/pdf Generate (or regenerate) a PDF for an existing resume. **Request body:** None required. **Response (200):** ```json { "object": "resume.pdf", "id": "res_a1b2c3d4e5f6", "pdf_url": "https://api.finetuneresume.app/files/res_a1b2c3d4e5f6.pdf", "generated_at": "2026-02-21T10:31:00Z" } ``` --- ### DELETE /resumes/:id Delete a generated resume. **Response (200):** ```json { "object": "resume.deleted", "id": "res_a1b2c3d4e5f6", "deleted": true } ``` --- ### GET /usage Get your current usage statistics and credit balance. **Response (200):** ```json { "object": "usage", "credits_total": 100, "credits_used": 58, "credits_remaining": 42, "resumes_generated_this_week": 12, "plan": "pro", "rate_limit": { "requests_per_minute": 60, "remaining": 58 }, "billing_cycle_end": "2026-03-01T00:00:00Z" } ``` --- ### GET /api-keys List your API keys (secrets are masked). **Response (200):** ```json { "object": "list", "data": [ { "object": "api_key", "id": "key_abc123", "name": "Production", "prefix": "ft_live_abc1....", "created_at": "2026-01-15T08:00:00Z", "last_used_at": "2026-02-21T10:30:00Z" } ], "has_more": false } ``` --- ## Finetune Levels The `finetune_level` parameter controls how aggressively the AI tailors your resume: | Level | Credits | What it does | |-------|---------|-------------| | `basic` | 1 | Keyword optimization. Adjusts skills section and adds relevant keywords to summary. Fastest, lightest touch. | | `good` | 2 | Balanced rewrite. Rewrites summary, tweaks experience bullet points, optimizes skills. Best for most applications. | | `super` | 3 | Deep rewrite. Reframes experience narratives for the target role and industry. Rewrites summary, bullets, and skills from scratch. Best for career pivots or competitive roles. | --- ## Error Handling All errors return a consistent JSON shape: ```json { "object": "error", "name": "validation_error", "message": "job_description must be at least 50 characters", "status": 400 } ``` **Error codes:** | Name | Status | Description | |------|--------|-------------| | `validation_error` | 400 | Invalid or missing request parameters. Check the `message` field for details. | | `missing_api_key` | 401 | No `X-API-Key` header provided. | | `invalid_api_key` | 403 | API key is invalid, revoked, or expired. | | `not_found` | 404 | The requested resource does not exist or does not belong to your account. | | `rate_limit_exceeded` | 429 | Too many requests. Wait and retry after the `ratelimit-reset` time. | | `credit_limit_exceeded` | 429 | Not enough credits for this operation. Upgrade your plan or wait for the next billing cycle. | | `internal_server_error` | 500 | Something went wrong on our end. Retry with an idempotency key. | --- ## Rate Limiting Every response includes IETF rate limit headers: ``` ratelimit-limit: 60 ratelimit-remaining: 58 ratelimit-reset: 1740130260 ``` | Header | Description | |--------|-------------| | `ratelimit-limit` | Max requests allowed per minute for your plan. | | `ratelimit-remaining` | Requests remaining in the current window. | | `ratelimit-reset` | Unix timestamp (seconds) when the window resets. | **Limits by plan:** - Free: 10 requests/minute - Pro: 60 requests/minute When rate limited, you receive a `429` response. Wait until `ratelimit-reset` before retrying. --- ## Pagination List endpoints use cursor-based pagination. **Parameters:** - `limit` — Number of items to return (1-100, default 20). - `after` — Return items after this cursor (the `id` of the last item on the previous page). - `before` — Return items before this cursor (the `id` of the first item on the next page). **Response shape:** ```json { "object": "list", "data": [ ... ], "has_more": true } ``` When `has_more` is `true`, pass the last item's `id` as the `after` parameter to fetch the next page. --- ## Test Mode API keys prefixed with `ft_test_` operate in test mode: - No LLM calls are made. Responses return mock tailored data. - No credits are deducted. - Resumes are stored and visible in your dashboard (marked as test). - Rate limits still apply. - Use test keys for development, integration tests, and CI pipelines. --- ## Code Examples ### curl ```bash # Generate a tailored resume curl -X POST https://api.finetuneresume.app/api/v1/resumes/generate \ -H "X-API-Key: ft_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "job_description": "Senior Software Engineer at Google. Build distributed systems for Google Cloud...", "company_name": "Google", "position": "Senior Software Engineer", "finetune_level": "good", "include_pdf": true }' # List resumes curl https://api.finetuneresume.app/api/v1/resumes \ -H "X-API-Key: ft_live_xxx" # Get a specific resume curl https://api.finetuneresume.app/api/v1/resumes/res_a1b2c3d4e5f6 \ -H "X-API-Key: ft_live_xxx" # Check usage curl https://api.finetuneresume.app/api/v1/usage \ -H "X-API-Key: ft_live_xxx" ``` ### Python ```python import requests API_KEY = "ft_live_xxx" BASE = "https://api.finetuneresume.app/api/v1" resp = requests.post(f"{BASE}/resumes/generate", headers={ "X-API-Key": API_KEY, "Content-Type": "application/json", }, json={ "job_description": "Senior Software Engineer at Google...", "company_name": "Google", "position": "Senior Software Engineer", "finetune_level": "good", "include_pdf": True, }) data = resp.json() print(data["id"], data.get("pdf_url")) ``` ### Node.js (TypeScript) ```typescript const API_KEY = "ft_live_xxx"; const BASE = "https://api.finetuneresume.app/api/v1"; const resp = await fetch(`${BASE}/resumes/generate`, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ job_description: "Senior Software Engineer at Google...", company_name: "Google", position: "Senior Software Engineer", finetune_level: "good", include_pdf: true, }), }); const data = await resp.json(); console.log(data.id, data.pdf_url); ``` --- ## Links - Dashboard: https://finetuneresume.app/developers - API keys: https://finetuneresume.app/developers/keys - OpenAPI spec: https://finetuneresume.app/openapi.json - Status page: https://status.finetuneresume.app - Support: support@finetuneresume.app