Document conversion
Convert PDF, DOCX, XLSX, PPTX, and image files to markdown with automatic OCR. Batch processing and real-time streaming progress.
POST /v1/documents/convertQuick Start
import httpx
API_KEY = "sk-mel-<YOUR_API_KEY>"
async def convert_document():
async with httpx.AsyncClient() as client:
with open("report.pdf", "rb") as f:
response = await client.post(
"https://api.melious.ai/v1/documents/convert",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"files": ("report.pdf", f, "application/pdf")}
)
result = response.json()
print(result["results"][0]["pages"][0]["content"])import fs from 'fs';
import FormData from 'form-data';
const API_KEY = 'sk-mel-<YOUR_API_KEY>';
async function convertDocument() {
const form = new FormData();
form.append('files', fs.createReadStream('report.pdf'));
const response = await fetch('https://api.melious.ai/v1/documents/convert', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
},
body: form
});
const result = await response.json();
console.log(result.results[0].pages[0].content);
}curl https://api.melious.ai/v1/documents/convert \
-H "Authorization: Bearer sk-mel-<YOUR_API_KEY>" \
-F "files=@report.pdf"Authentication
The Document Conversion API supports two authentication methods:
Session Authentication (Free)
When logged in via the web interface, document conversion is free but rate-limited based on your plan tier:
| Plan Tier | Daily Page Limit |
|---|---|
| Free, Anonymous | 500 pages/day |
| Plus, Explorer, Pro, Edu | 2,000 pages/day |
| Max, Business, Business Plus | 5,000 pages/day |
API Key Authentication (Paid)
API key users are charged per page with no daily limits:
- Rate: €0.50 per 1,000 pages (€0.0005 per page)
- Scope Required:
kit.documents
Create an API key with kit.documents scope at melious.ai/account/api/keys.
Request Format
Multipart Form Data
Upload files using multipart/form-data encoding:
POST /v1/documents/convert
Content-Type: multipart/form-data
Authorization: Bearer sk-mel-<YOUR_API_KEY>Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
stream | boolean | false | Enable SSE streaming for real-time progress |
force_ocr | boolean | false | Force OCR even for text-extractable documents |
Supported File Formats
| Category | Formats | Notes |
|---|---|---|
.pdf | Text extraction + OCR for scanned pages | |
| Microsoft Office | .docx, .doc, .xlsx, .xls, .pptx, .ppt | Full formatting preserved |
| OpenDocument | .odt, .ods, .odp | LibreOffice/OpenOffice formats |
| Spreadsheets | .csv | Comma-separated values |
| Rich Text | .rtf | Rich text format |
| Images | .png, .jpg, .gif, .webp, .bmp, .tiff, .heic, .avif | OCR applied automatically |
| Text | .txt, .md, .html, .xml | Converted to markdown |
Response Format
Non-Streaming Response
{
"results": [
{
"filename": "report.pdf",
"pages": [
{
"page_number": 1,
"content": "# Introduction\n\nThis is the first page content...",
"metadata": {
"word_count": 250,
"table_count": 0,
"image_count": 1
}
},
{
"page_number": 2,
"content": "## Chapter 1\n\nDetailed analysis...",
"metadata": {
"word_count": 500,
"table_count": 2
}
}
],
"total_pages": 2,
"file_type": "application/pdf",
"status": "success",
"processing_time_ms": 1234
}
],
"total": 1,
"successful": 1,
"failed": 0,
"processing_time_ms": 1234
}Response Fields
| Field | Type | Description |
|---|---|---|
results | array | Array of document results |
results[].filename | string | Original filename |
results[].pages | array | Array of page objects |
results[].pages[].page_number | integer | Page number (1-indexed) |
results[].pages[].content | string | Markdown content |
results[].pages[].metadata | object | Optional page metadata |
results[].total_pages | integer | Total pages in document |
results[].file_type | string | Detected MIME type |
results[].status | string | success or error |
results[].error | string | Error message (if status is error) |
results[].processing_time_ms | integer | Processing time in milliseconds |
total | integer | Total documents submitted |
successful | integer | Number of successful conversions |
failed | integer | Number of failed conversions |
processing_time_ms | integer | Total processing time |
Streaming Response
Enable streaming for real-time progress updates on large documents or batches:
POST /v1/documents/convert?stream=trueSSE Event Types
The streaming response sends Server-Sent Events (SSE) with the following event types:
queued - Document queued for processing
{
"type": "queued",
"request_id": "abc123",
"filename": "report.pdf",
"position": 0,
"estimated_wait_ms": 0
}processing - Document processing started
{
"type": "processing",
"request_id": "abc123",
"filename": "report.pdf",
"is_ocr": true
}complete - Document conversion completed
{
"type": "complete",
"request_id": "abc123",
"result": {
"filename": "report.pdf",
"pages": [...],
"total_pages": 5,
"file_type": "application/pdf",
"status": "success",
"processing_time_ms": 2500
}
}error - Document conversion failed
{
"type": "error",
"request_id": "abc123",
"filename": "corrupted.pdf",
"error": "Unable to parse PDF structure",
"code": "KIT_6202"
}stream_end - All documents processed
{
"type": "stream_end",
"request_id": "abc123",
"total": 3,
"successful": 2,
"failed": 1,
"processing_time_ms": 5000
}The stream ends with:
data: [DONE]Streaming Example
import httpx
import json
API_KEY = "sk-mel-<YOUR_API_KEY>"
async def convert_with_streaming():
async with httpx.AsyncClient() as client:
with open("large_report.pdf", "rb") as f:
async with client.stream(
"POST",
"https://api.melious.ai/v1/documents/convert?stream=true",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"files": ("large_report.pdf", f, "application/pdf")},
timeout=300.0
) as response:
async for line in response.aiter_lines():
if line.startswith("data: "):
data = line[6:]
if data == "[DONE]":
print("Stream complete!")
break
event = json.loads(data)
print(f"Event: {event['type']}")
if event["type"] == "complete":
print(f"Converted: {event['result']['filename']}")import fs from 'fs';
import FormData from 'form-data';
const API_KEY = 'sk-mel-<YOUR_API_KEY>';
async function convertWithStreaming() {
const form = new FormData();
form.append('files', fs.createReadStream('large_report.pdf'));
const response = await fetch(
'https://api.melious.ai/v1/documents/convert?stream=true',
{
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` },
body: form
}
);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const lines = decoder.decode(value).split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') {
console.log('Stream complete!');
return;
}
const event = JSON.parse(data);
console.log(`Event: ${event.type}`);
}
}
}
}curl -N https://api.melious.ai/v1/documents/convert?stream=true \
-H "Authorization: Bearer sk-mel-<YOUR_API_KEY>" \
-F "files=@large_report.pdf"Multiple Files
Convert multiple documents in a single request:
import httpx
API_KEY = "sk-mel-<YOUR_API_KEY>"
async def convert_multiple():
async with httpx.AsyncClient() as client:
files = [
("files", ("report.pdf", open("report.pdf", "rb"), "application/pdf")),
("files", ("data.xlsx", open("data.xlsx", "rb"), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")),
("files", ("scan.png", open("scan.png", "rb"), "image/png")),
]
response = await client.post(
"https://api.melious.ai/v1/documents/convert",
headers={"Authorization": f"Bearer {API_KEY}"},
files=files
)
result = response.json()
print(f"Converted {result['successful']} of {result['total']} documents")import fs from 'fs';
import FormData from 'form-data';
const API_KEY = 'sk-mel-<YOUR_API_KEY>';
async function convertMultiple() {
const form = new FormData();
form.append('files', fs.createReadStream('report.pdf'));
form.append('files', fs.createReadStream('data.xlsx'));
form.append('files', fs.createReadStream('scan.png'));
const response = await fetch('https://api.melious.ai/v1/documents/convert', {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` },
body: form
});
const result = await response.json();
console.log(`Converted ${result.successful} of ${result.total} documents`);
}curl https://api.melious.ai/v1/documents/convert \
-H "Authorization: Bearer sk-mel-<YOUR_API_KEY>" \
-F "files=@report.pdf" \
-F "files=@data.xlsx" \
-F "files=@scan.png"Force OCR Mode
Force OCR processing even for documents with extractable text (useful for scanned documents saved as text-PDFs):
curl https://api.melious.ai/v1/documents/convert?force_ocr=true \
-H "Authorization: Bearer sk-mel-<YOUR_API_KEY>" \
-F "files=@scanned_document.pdf"Force OCR mode may increase processing time but can improve quality for documents with poor text extraction.
Error Handling
Error Response
{
"error": {
"code": "VALIDATION_REQUIRED_FIELD",
"message": "At least one file is required"
}
}Error Codes
| Code | Description |
|---|---|
VALIDATION_REQUIRED_FIELD | No files provided |
AUTH_SESSION_REQUIRED | No valid authentication |
AUTH_INSUFFICIENT_SCOPE | API key missing kit.documents scope |
KIT_DOCUMENT_RATE_LIMITED | Daily page limit exceeded (session auth) |
BILLING_INSUFFICIENT_ENERGY | Insufficient balance (API key auth) |
KIT_6202 | Document parsing failed |
SYSTEM_SERVICE_UNAVAILABLE | Document service temporarily unavailable |
Rate Limit Response
When daily limits are exceeded (session auth only):
{
"error": {
"code": "KIT_DOCUMENT_RATE_LIMITED",
"message": "Daily document limit reached (500/500 pages). Resets at midnight UTC.",
"details": {
"used_pages": 500,
"daily_limit": 500,
"remaining_pages": 0,
"reset_at": "2026-01-29T00:00:00Z"
}
}
}Best Practices
- Use streaming for large files - Get real-time progress updates instead of waiting for the entire batch
- Batch related documents - Process multiple files in a single request to reduce overhead
- Check file types - Ensure files are in supported formats before uploading
- Handle partial failures - When processing multiple files, some may succeed while others fail
- Use force_ocr wisely - Only enable when text extraction quality is poor
See Also
- Tools API - Other available tools
- Chat Completions - Use converted documents with AI