Parse Resumes with Python
Extract structured resume data in Python with a single API call
Parse PDF resumes with Python using the Resume Parser REST API. Setup with the requests library, rate-limit friendly batch processing, error handling, and structured JSON output.
Setup steps
- 1
Get your API key
Sign up at https://api.resumeparser.cc/signup with your email. You get a magic link, and you create an API key (rp_live_...) in the dashboard. Keep the key on your server and never expose it in client-side code.
- 2
Install the requests library
Run `pip install requests`. It handles the multipart/form-data encoding needed for file uploads.
- 3
Send a resume to the API
POST the PDF to https://api.resumeparser.cc/v1/parse as multipart field "file" with the header `Authorization: Bearer rp_live_...`. PDF only, up to 5 MB. Parsing is synchronous and can take up to 60 seconds, so set your HTTP client timeout to at least 60 seconds.
- 4
Use the JSON response
The response is `{ data, usage }`. data holds the parsed fields (firstName, lastName, emailAddress, phoneNumber, linkedInLink, workExperiences, educations, fluentLanguages, languages). usage holds billedAs, freeRemaining, and creditBalance.
Code examples
import os
import requests
API_URL = "https://api.resumeparser.cc/v1/parse"
API_KEY = os.environ["RESUME_PARSER_API_KEY"] # rp_live_...
MAX_BYTES = 5 * 1024 * 1024 # 5 MB
def parse_resume(file_path: str) -> dict:
if os.path.getsize(file_path) > MAX_BYTES:
raise ValueError("file exceeds 5 MB")
with open(file_path, "rb") as f:
response = requests.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": (os.path.basename(file_path), f, "application/pdf")},
timeout=60,
)
body = response.json()
if not response.ok:
error = body["error"]
raise RuntimeError(f"{response.status_code} {error['code']}: {error['message']}")
return body
result = parse_resume("resume.pdf")
data = result["data"]
# Every field is optional: it is left out when the CV does not have it
print(data.get("firstName"), data.get("lastName"))
print(data.get("emailAddress"))
for experience in data.get("workExperiences", []):
print(experience["title"], "at", experience["organization"])
print(experience["startMonth"], experience["startYear"], "ongoing:", experience["isOngoing"])
for education in data.get("educations", []):
print(education["degree"], "at", education["school"])
print(result["usage"]) # billedAs, freeRemaining, creditBalanceimport os
import time
import json
from pathlib import Path
import requests
API_URL = "https://api.resumeparser.cc/v1/parse"
API_KEY = os.environ["RESUME_PARSER_API_KEY"]
# 30 requests per minute per key: one request every 2 seconds, one at a time
SECONDS_BETWEEN_REQUESTS = 2
output_dir = Path("parsed")
output_dir.mkdir(exist_ok=True)
for pdf_path in sorted(Path("resumes").glob("*.pdf")):
with open(pdf_path, "rb") as f:
response = requests.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": (pdf_path.name, f, "application/pdf")},
timeout=60,
)
body = response.json()
if response.ok:
(output_dir / f"{pdf_path.stem}.json").write_text(json.dumps(body["data"], indent=2))
print(pdf_path.name, "ok, free remaining:", body["usage"]["freeRemaining"])
else:
print(pdf_path.name, "failed:", body["error"]["code"])
time.sleep(SECONDS_BETWEEN_REQUESTS)import os
import time
import requests
API_URL = "https://api.resumeparser.cc/v1/parse"
API_KEY = os.environ["RESUME_PARSER_API_KEY"]
# 429 rate_limited and 503 busy are worth retrying.
# 400 invalid_file, 401 invalid_api_key, 402 insufficient_credits and
# 413 file_too_large will fail again with the same request.
RETRYABLE_STATUSES = {429, 503}
def parse_resume_with_retry(file_path: str, max_attempts: int = 4) -> dict:
for attempt in range(1, max_attempts + 1):
with open(file_path, "rb") as f:
response = requests.post(
API_URL,
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": (os.path.basename(file_path), f, "application/pdf")},
timeout=60,
)
if response.ok:
return response.json()
error = response.json()["error"]
if response.status_code not in RETRYABLE_STATUSES or attempt == max_attempts:
raise RuntimeError(f"{response.status_code} {error['code']}: {error['message']}")
time.sleep(10 * attempt)
raise RuntimeError("unreachable")Integration benefits
Only the requests library
No SDK to install. Any Python code that can send a multipart POST with a header can call the API.
One fixed JSON schema
Every response uses the same documented fields. Every field is optional, so check for missing values.
Works in batch jobs
Each request is independent. Stay within 30 requests per minute per key and retry 429 and 503 responses.
Failed parses are not charged
50 free parses per month, then prepaid credits. A request that ends in an error does not use a parse.
Frequently asked questions
Any Python that can make HTTPS requests. The examples use Python 3 with the requests library.
Send one request every 2 seconds per key, or use a small token bucket. If you get 429 rate_limited, wait for the Retry-After header (60 seconds) before retrying. If you get 503 busy, the API is at capacity; wait a few seconds and retry.
Yes. Use httpx or aiohttp with the same request, and keep an asyncio.Semaphore or a rate limiter so you stay within 30 requests per minute per key.
The API returns 400 invalid_file, or 422 parse_failed if parsing fails. Failed parses are not charged. Scanned image-only PDFs do not work because there is no OCR; the PDF needs a text layer.
Every parse response includes usage (billedAs, freeRemaining, creditBalance). You can also call GET https://api.resumeparser.cc/v1/usage with the same Authorization header.
More code examples
Parse Resumes with JavaScript and Node.js
Parse PDF resumes from Node.js using the Resume Parser REST API. Built-in fetch and FormData, TypeScript types, rate-limit friendly batch processing, and an Express upload route.
FeaturesSkill Extraction API
Get each role's description, with the skills used, tied to the job where they were used. PDF to structured JSON with one API call.
FeaturesPDF Resume Parser
Parse single-column and multi-column PDF resumes into structured JSON with one API call. PDF up to 5 MB. Parsing is synchronous and can take up to 60 seconds.
Start parsing resumes with Python
Sign up with your email, create an API key, and use the 50 free parses per month.