Melious
Tools

Document Conversion

Convert documents to markdown with OCR support

Document Conversion

Convert PDF, DOCX, XLSX, PPTX, and image files to markdown format with automatic OCR support. The API supports both batch processing and real-time streaming progress updates.

POST /v1/documents/convert

Quick Start

import httpx

API_KEY = "sk-mel-your-api-key-here"

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-here';

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-here" \
  -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 TierDaily Page Limit
Free, Anonymous500 pages/day
Plus, Explorer, Pro, Edu2,000 pages/day
Max, Business, Business Plus5,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-here

Query Parameters

ParameterTypeDefaultDescription
streambooleanfalseEnable SSE streaming for real-time progress
force_ocrbooleanfalseForce OCR even for text-extractable documents

Supported File Formats

CategoryFormatsNotes
PDF.pdfText extraction + OCR for scanned pages
Microsoft Office.docx, .doc, .xlsx, .xls, .pptx, .pptFull formatting preserved
OpenDocument.odt, .ods, .odpLibreOffice/OpenOffice formats
Spreadsheets.csvComma-separated values
Rich Text.rtfRich text format
Images.png, .jpg, .gif, .webp, .bmp, .tiff, .heic, .avifOCR applied automatically
Text.txt, .md, .html, .xmlConverted 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

FieldTypeDescription
resultsarrayArray of document results
results[].filenamestringOriginal filename
results[].pagesarrayArray of page objects
results[].pages[].page_numberintegerPage number (1-indexed)
results[].pages[].contentstringMarkdown content
results[].pages[].metadataobjectOptional page metadata
results[].total_pagesintegerTotal pages in document
results[].file_typestringDetected MIME type
results[].statusstringsuccess or error
results[].errorstringError message (if status is error)
results[].processing_time_msintegerProcessing time in milliseconds
totalintegerTotal documents submitted
successfulintegerNumber of successful conversions
failedintegerNumber of failed conversions
processing_time_msintegerTotal processing time

Streaming Response

Enable streaming for real-time progress updates on large documents or batches:

POST /v1/documents/convert?stream=true

SSE 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-here"

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-here';

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-here" \
  -F "files=@large_report.pdf"

Multiple Files

Convert multiple documents in a single request:

import httpx

API_KEY = "sk-mel-your-api-key-here"

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-here';

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-here" \
  -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-here" \
  -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

CodeDescription
VALIDATION_REQUIRED_FIELDNo files provided
AUTH_SESSION_REQUIREDNo valid authentication
AUTH_INSUFFICIENT_SCOPEAPI key missing kit.documents scope
KIT_DOCUMENT_RATE_LIMITEDDaily page limit exceeded (session auth)
BILLING_INSUFFICIENT_ENERGYInsufficient balance (API key auth)
KIT_6202Document parsing failed
SYSTEM_SERVICE_UNAVAILABLEDocument 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

  1. Use streaming for large files - Get real-time progress updates instead of waiting for the entire batch
  2. Batch related documents - Process multiple files in a single request to reduce overhead
  3. Check file types - Ensure files are in supported formats before uploading
  4. Handle partial failures - When processing multiple files, some may succeed while others fail
  5. Use force_ocr wisely - Only enable when text extraction quality is poor

See Also

On this page