PDF to Word
API Documentation
Convert PDF documents into editable Microsoft Word (.docx) files with optional OCR for scanned and image-based pages โ synchronously or through async jobs โ over a simple REST API. Built for developers.
Overview
The PDF to Word API converts PDF documents into editable Word (.docx) files while preserving text, tables, and layout. For scanned or image-based PDFs, enable OCR to extract text automatically. Synchronous conversion is ideal for small files; the async endpoints are designed for larger documents or batch workflows. Multiprocessing is used on the server to keep conversions fast.
Base URL & Authentication
Base URL
Base URL
https://api.chemhub.com/api/v1
Authentication
Include your API credentials in every request header.
Headers
X-API-Key: your_api_key
X-API-Secret: your_secret
Content-Type: application/json
Get API Keys
Follow these steps to obtain your API credentials:
Access Your Profile
Once you log in, click on your profile picture in the top-right corner, then click on "Your profile" from the dropdown menu.
Create API Key
From your profile page, proceed to the "API Keys" section and create a key by filling in a name you prefer.
"PDF converter bot" or "My Document Pipeline"
Save Your Credentials
Click on "Create API Key" to generate your credentials, then immediately copy and save both the API Key and Secret.
Your secret key will only be displayed once! Copy it immediately and store it securely. You won't be able to see it again.
Never share your API keys publicly or commit them to version control. Treat them like passwords โ regenerate immediately if compromised.
15 requests per minute per API key. Check your dashboard for current usage and limits.
Endpoints
GET /pdf2word/healthPOST /pdf2word/convertPOST /pdf2word/convert/asyncGET /pdf2word/jobs/{job_id}GET /pdf2word/jobs/{job_id}/downloadHealth Check
Health check endpoint to verify the PDF to Word service status, available backends, and supported formats.
Response
Success Response
{
"success": true,
"status": "healthy",
"pdf2w_backends": {
"pdfplumber": true,
"tesseract": true,
"ftfy": true,
"docx": true
},
"supported_formats": [".pdf"],
"max_file_size_mb": 20,
"database_available": true,
"timestamp": "2026-08-07T12:00:00"
}
Code Examples
Python
import requests
url = "https://api.chemhub.com/api/v1/pdf2word/health"
headers = {
"X-API-Key": "your_api_key",
"X-API-Secret": "your_secret"
}
res = requests.get(url, headers=headers)
print(res.json())
cURL
curl -X GET https://api.chemhub.com/api/v1/pdf2word/health \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret"
JavaScript
fetch('https://api.chemhub.com/api/v1/pdf2word/health', {
method: 'GET',
headers: {
'X-API-Key': 'your_api_key',
'X-API-Secret': 'your_secret'
}
})
.then(res => res.json())
.then(console.log);
Convert PDF to Word
Convert a PDF file to a Word document synchronously. Upload the file as multipart/form-data.
Form Parameters
| Field | Type | Default | Description |
|---|---|---|---|
file | File | โ | PDF file (required) โ up to 20MB |
use_ocr | bool | false | Set to true to run OCR on scanned / image-based pages |
On success the endpoint returns the converted Word document bytes directly with Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document.
cURL Example
cURL
curl -X POST "https://api.chemhub.com/api/v1/pdf2word/convert" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret" \
-F "file=@document.pdf" \
-F "use_ocr=false" \
--output document.docx
Code Examples
Python
import requests
url = "https://api.chemhub.com/api/v1/pdf2word/convert"
headers = {
"X-API-Key": "your_api_key",
"X-API-Secret": "your_secret"
}
with open("document.pdf", "rb") as f:
files = {"file": f}
data = {"use_ocr": "true"}
response = requests.post(url, headers=headers, files=files, data=data)
if response.status_code == 200:
with open("document.docx", "wb") as out:
out.write(response.content)
print("Conversion successful!")
else:
print(response.json())
JavaScript
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('use_ocr', 'true');
fetch('https://api.chemhub.com/api/v1/pdf2word/convert', {
method: 'POST',
headers: {
'X-API-Key': 'your_api_key',
'X-API-Secret': 'your_secret'
},
body: form
})
.then(async res => {
if (res.ok) {
const blob = await res.blob();
const url = URL.createObjectURL(blob);
console.log('Word document URL:', url);
} else {
console.log(await res.json());
}
});
Convert PDF to Word (Async)
Submit a conversion job and receive a job_id immediately. Use the job endpoints to poll for progress and download the result. Ideal for large files or batch workflows.
Form Parameters
Same parameters as the synchronous endpoint.
| Field | Type | Default | Description |
|---|---|---|---|
file | File | โ | PDF file (required) โ up to 20MB |
use_ocr | bool | false | Set to true to run OCR on scanned / image-based pages |
Response
Success Response
{
"success": true,
"job_id": "8f3a6b2c9d1e4a5f",
"status": "queued",
"message": "Document conversion started. Use the job ID to check status."
}
Code Examples
Python
import requests
url = "https://api.chemhub.com/api/v1/pdf2word/convert/async"
headers = {
"X-API-Key": "your_api_key",
"X-API-Secret": "your_secret"
}
with open("document.pdf", "rb") as f:
response = requests.post(url, headers=headers, files={"file": f}, data={"use_ocr": "false"})
result = response.json()
job_id = result.get("job_id")
print("Job ID:", job_id)
cURL
curl -X POST "https://api.chemhub.com/api/v1/pdf2word/convert/async" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret" \
-F "file=@document.pdf" \
-F "use_ocr=false"
Get Job Status
Poll the status of an asynchronous conversion job. Job statuses are queued, processing, done, and failed.
Path Parameters
| Field | Type | Description |
|---|---|---|
job_id | string | Job ID returned by the async submit endpoint (required) |
Response
Success Response
{
"success": true,
"job_id": "8f3a6b2c9d1e4a5f",
"status": "done",
"filename": "document.pdf",
"created_at": "2026-08-07 12:00:01",
"updated_at": "2026-08-07 12:00:05",
"result_url": "/api/v1/pdf2word/jobs/8f3a6b2c9d1e4a5f/download"
}
Jobs are bound to the API key that created them. Accessing another key's job returns 403 Access denied.
Code Examples
Python
import requests
job_id = "8f3a6b2c9d1e4a5f"
url = f"https://api.chemhub.com/api/v1/pdf2word/jobs/{job_id}"
headers = {
"X-API-Key": "your_api_key",
"X-API-Secret": "your_secret"
}
res = requests.get(url, headers=headers)
print(res.json())
cURL
curl -X GET "https://api.chemhub.com/api/v1/pdf2word/jobs/8f3a6b2c9d1e4a5f" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret"
Download Conversion Result
Download the Word document produced by a completed async job.
Path Parameters
| Field | Type | Description |
|---|---|---|
job_id | string | Job ID of a completed (done) job (required) |
On success the endpoint returns the converted Word document as an attachment. If the job has not finished, it returns 400 Job not completed.
cURL Example
cURL
curl -X GET "https://api.chemhub.com/api/v1/pdf2word/jobs/8f3a6b2c9d1e4a5f/download" \
-H "X-API-Key: your_api_key" \
-H "X-API-Secret: your_secret" \
--output converted.docx
Python SDK
Complete Python SDK covering health check, synchronous conversion, async job submission, polling, and download.
Complete SDK Class
import os
import requests
BASE_URL = "https://api.chemhub.com/api/v1"
# Your api key here
API_KEY = "your_api_key"
API_SECRET = "your_api_secret"
headers = {
"X-API-Key": API_KEY,
"X-API-Secret": API_SECRET
}
# --- Health Check ---
def check_health():
return requests.get(f"{BASE_URL}/pdf2word/health", headers=headers).json()
# --- Synchronous conversion ---
def convert_sync(pdf_path, use_ocr=False):
with open(pdf_path, "rb") as f:
files = {"file": f}
data = {"use_ocr": "true" if use_ocr else "false"}
response = requests.post(
f"{BASE_URL}/pdf2word/convert",
headers=headers,
files=files,
data=data
)
if response.status_code == 200:
output_path = pdf_path.rsplit(".", 1)[0] + ".docx"
with open(output_path, "wb") as out:
out.write(response.content)
print(f"Saved: {output_path}")
return output_path
print(f"Error: {response.status_code} - {response.text}")
return None
# --- Async conversion ---
def convert_async(pdf_path, use_ocr=False):
with open(pdf_path, "rb") as f:
files = {"file": f}
data = {"use_ocr": "true" if use_ocr else "false"}
response = requests.post(
f"{BASE_URL}/pdf2word/convert/async",
headers=headers,
files=files,
data=data
)
return response.json()
# --- Poll job status ---
def get_job_status(job_id):
return requests.get(
f"{BASE_URL}/pdf2word/jobs/{job_id}",
headers=headers
).json()
# --- Download completed job ---
def download_job(job_id, output_path="converted.docx"):
response = requests.get(
f"{BASE_URL}/pdf2word/jobs/{job_id}/download",
headers=headers
)
if response.status_code == 200:
with open(output_path, "wb") as out:
out.write(response.content)
print(f"Downloaded: {output_path}")
else:
print(f"Error: {response.status_code} - {response.text}")
# --- Test all endpoints ---
if __name__ == "__main__":
print("=== PDF TO WORD API TEST ===")
health = check_health()
print(f"Health: {health.get('status', 'unknown')}")
# Replace with your own paths
document_path = r"C:\path\to\your\document.pdf"
output_path = r"C:\path\to\your\output\converted.docx"
print("\nSubmitting PDF conversion job...")
job = convert_async(document_path, use_ocr=False)
job_id = job.get("job_id")
if not job_id:
print(f"Failed to submit job: {job}")
else:
print(f"Submitted job: {job_id}")
print("\nWaiting for conversion to complete...")
while True:
status_response = get_job_status(job_id)
status = status_response.get("status")
print(f"Job status: {status}")
if status == "done":
print("\nConversion completed. Downloading Word document...")
download_job(job_id, output_path)
break
if status == "failed":
print(f"\nConversion failed: {status_response}")
break
Errors & Status Codes
Error Response Format
Error Response
{
"success": false,
"error": "Invalid file",
"message": "File too large. Max size is 20MB"
}
Status Codes
| Code | Meaning |
|---|---|
200 | Success |
400 | Bad request / invalid parameters / unsupported file format |
401 | Invalid or missing API key/secret |
403 | Access denied (job belongs to another API key) |
404 | Job not found or result unavailable |
429 | Rate limit exceeded |
500 | Internal server error / conversion failed |