Independent guide — not affiliated with IBM or the Docling project. Every command verified on Docling 2.143.1.
  1. Home
  2. docling-serve API
docling-serve & MCP

Run Docling as a service — docling-serve, the API and MCP

docling-serve wraps Docling in a FastAPI server so any language can convert documents over HTTP — with an optional web UI, async jobs, S3/Azure/GCS sources and API keys. We read every endpoint and default from the 1.38.2 source.

Verified on Docling 2.143.1Updated 27 Oct 202611 min read
In short
  • pip install "docling-serve[ui]" then docling-serve run --enable-ui → API on port 5001, docs at /docs, UI at /ui.
  • Small jobs: POST /v1/convert/source (URLs) or /v1/convert/file (uploads). Sync calls wait up to 120 s.
  • Big jobs: the /async variants return a task_id; poll /v1/status/poll/{id}, then fetch /v1/result/{id} once.
  • Protect it with DOCLING_SERVE_API_KEY (clients send X-Api-Key). Agents can use the same server through docling-mcp.

Start a server in one minute#

bashPython
pip install "docling-serve[ui]"
docling-serve run --enable-ui          # API: http://127.0.0.1:5001 · docs: /docs · UI: /ui
bashContainer (CPU image)
docker run -p 5001:5001 -e DOCLING_SERVE_ENABLE_UI=1 \
  quay.io/docling-project/docling-serve-cpu

docling-serve run listens on all interfaces (host 0.0.0.0); docling-serve dev binds to 127.0.0.1 and auto-reloads for development. Image choice and GPU variants are on the Docker page.

How a request flows#

Your appcurl · Python · n8nX-Api-Key (optional)POST /v1/convert/sourcesync: waits up to 120 s→ document JSONPOST …/source/asyncreturns a task_id at once→ task_idGET /v1/status/poll/{id}or WebSocket /v1/status/wsGET /v1/result/{id}fetch once; kept 300 sWorker pool (local engine, 2 workers by default)or RQ + Redis / Ray for scale-out
Endpoints and defaults read from docling-serve 1.38.2 (app.py and settings.py).

Endpoints#

Method & pathPurpose
POST /v1/convert/sourceConvert from URLs or cloud sources, wait for the result
POST /v1/convert/fileConvert uploaded files (multipart), wait for the result
POST /v1/convert/source/async, /v1/convert/file/asyncStart a job, return a task_id immediately
POST /v1/convert/source/batchSubmit several sources as one batch
POST /v1/chunk/{hybrid|hierarchical}/{source|file}[/async]Convert and chunk in one call
GET /v1/status/poll/{task_id}Job status (pending, started, success, failure…)
WS /v1/status/ws/{task_id}Push status updates over a WebSocket
GET /v1/result/{task_id}Fetch the finished result
GET /health, /ready, /version, /v1/capabilitiesHealth, readiness, versions, enabled features
GET /docs, /scalar, /uiSwagger docs, Scalar docs, playground UI

Paths read from docling_serve/app.py in 1.38.2. Management endpoints (/v1/clear/*, /v1/memory/*) exist but are off unless DOCLING_SERVE_ENABLE_MANAGEMENT_ENDPOINTS is set.

Convert a URL#

bash
curl -X POST http://localhost:5001/v1/convert/source \
  -H 'Content-Type: application/json' \
  -d '{
    "sources": [{"kind": "http", "url": "https://arxiv.org/pdf/2408.09869"}],
    "options": {"to_formats": ["md", "json"], "do_ocr": false}
  }'

The response is a ConvertDocumentResponse: document (with md_content, json_content, html_content, text_content, doctags_content for the formats you asked for), status, errors, processing_time, timings and confidence.

Source kinds accepted in 2.143.1's request model: http, file (base64), s3, azure_blob, google_cloud_storage and google_drive.

Upload a file#

bash
curl -X POST http://localhost:5001/v1/convert/file \
  -F 'files=@report.pdf' \
  -F 'to_formats=md' \
  -F 'do_ocr=false' \
  -F 'table_mode=fast'

Options become form fields with the same names as in the JSON body; repeat a field for lists (-F to_formats=md -F to_formats=json). The config builder writes these requests for you.

Long documents: async jobs#

pythonasync_convert.py
import time, requests

BASE = "http://localhost:5001"
job = requests.post(f"{BASE}/v1/convert/source/async", json={
    "sources": [{"kind": "http", "url": "https://arxiv.org/pdf/2408.09869"}],
    "options": {"to_formats": ["md"]},
}).json()

task_id = job["task_id"]
while True:
    status = requests.get(f"{BASE}/v1/status/poll/{task_id}").json()
    if status["task_status"] in ("success", "partial_success", "failure"):
        break
    time.sleep(2)

result = requests.get(f"{BASE}/v1/result/{task_id}").json()
print(result["document"]["md_content"][:300])

Options you will actually use#

The JSON options object is ConvertDocumentsOptions. The most common fields and their defaults in 2.143.1:

FieldDefaultNote
to_formats["md"]md, json, html, text, doctags, …
image_export_modeplaceholderUnlike the CLI, which defaults to embedded
do_ocr / force_ocrtrue / false
ocr_preset / ocr_langauto / nonePreset IDs are the CLI engine names; ocr_engine still works but is deprecated. A pip-installed server has no onnxruntime, so the rapidocr preset fails — auto works. With tesseract, always set ocr_lang (eng or iso:en worked; plain en did not).
pipelinestandardvlm with vlm_pipeline_preset
table_modeaccuratefast is quicker
page_rangeall pages[1, 10]
document_timeoutnoneSeconds per document
images_scale2.0Server caps it at DOCLING_SERVE_MAX_IMAGES_SCALE (2.0)
do_formula_enrichment, do_code_enrichment, do_picture_description, do_chart_extractionfalseEnrichment models

Server settings#

All settings are environment variables with the DOCLING_SERVE_ prefix (or a YAML/JSON file given by DOCLING_SERVE_CONFIG_FILE). Uvicorn settings use UVICORN_.

VariableDefaultWhat it does
DOCLING_SERVE_ENABLE_UIfalseServe the playground at /ui
DOCLING_SERVE_API_KEYempty (no auth)Require X-Api-Key
DOCLING_SERVE_MAX_SYNC_WAIT120Seconds a sync request waits
DOCLING_SERVE_ENG_KINDlocallocal, rq (Redis queue) or ray
DOCLING_SERVE_ENG_LOC_NUM_WORKERS2Parallel jobs in the local engine
DOCLING_SERVE_MAX_NUM_PAGES, …_MAX_FILE_SIZEunlimitedReject oversized documents
DOCLING_SERVE_MAX_SOURCES_PER_REQUEST3Sources per call
DOCLING_SERVE_LOAD_MODELS_AT_BOOTtrueWarm models at start-up
DOCLING_SERVE_ARTIFACTS_PATHnonePre-downloaded models (offline)
DOCLING_SERVE_ENABLE_REMOTE_SERVICESfalseAllow API-based models
DOCLING_SERVE_SINGLE_USE_RESULTS, …_RESULT_REMOVAL_DELAYtrue, 300Delete results after fetching
DOCLING_SERVE_CORS_ORIGINS["*"]Tighten for browser clients

Defaults read from docling_serve/settings.py (1.38.2).

Python clients#

Docling ships its own client. It mirrors DocumentConverter, so switching between local and remote conversion is a one-line change.

python
from docling.datamodel.service.options import ConvertDocumentsOptions
from docling.service_client import DoclingServiceClient

with DoclingServiceClient(url="http://localhost:5001") as client:   # api_key="..." if set
    result = client.convert(
        "https://arxiv.org/pdf/2408.09869",
        options=ConvertDocumentsOptions(do_ocr=False, to_formats=["md"]),
    )
    print(result.document.export_to_markdown()[:300])

Install it without PyTorch via pip install "docling-slim[service-client]"; the same extra adds the docling convert-remote command (see the CLI reference).

MCP: Docling for AI agents#

docling-mcp exposes conversion and document tools to MCP clients such as Claude Desktop or LM Studio. In version 3.x the default conversion mode is remote, so point it at a docling-serve instance — or install the local extra.

jsonMCP client config — remote mode
{
  "mcpServers": {
    "docling": {
      "command": "uvx",
      "args": ["--from=docling-mcp", "docling-mcp-server"],
      "env": {
        "DOCLING_MCP_CONVERSION_MODE": "remote",
        "DOCLING_MCP_SERVICE_URL": "http://localhost:5001"
      }
    }
  }
}
bashlocal mode, no server
uvx --from "docling-mcp[local]" docling-mcp-server --transport stdio
# with DOCLING_MCP_CONVERSION_MODE=local in the environment

Transports: stdio (desktop clients), sse and streamable-http (containers). docling-mcp 3.x requires MCP SDK 2.x; pin docling-mcp<3.0.0 for clients that have not migrated.

Production checklist#

  • Set DOCLING_SERVE_API_KEY and restrict DOCLING_SERVE_CORS_ORIGINS.
  • Use the async endpoints for anything over a few pages; sync calls are capped at 120 s.
  • Cap input with DOCLING_SERVE_MAX_NUM_PAGES and DOCLING_SERVE_MAX_FILE_SIZE.
  • Pre-download models into the image (DOCLING_SERVE_ARTIFACTS_PATH) so pods start without internet.
  • Scale out with DOCLING_SERVE_ENG_KIND=rq and Redis workers when one machine is not enough.

Frequently asked questions

What port does docling-serve use?

5001 by default. The API is at http://127.0.0.1:5001, interactive API docs at /docs and, when enabled with --enable-ui or DOCLING_SERVE_ENABLE_UI=1, a playground UI at /ui.

How do I add authentication to docling-serve?

Set DOCLING_SERVE_API_KEY to a secret. Clients must then send it in the X-Api-Key header.

Why does my synchronous docling-serve request time out?

Synchronous endpoints wait up to DOCLING_SERVE_MAX_SYNC_WAIT seconds (120 by default). For long documents use the /async endpoints and poll for the result, or raise the limit.

Can I fetch a docling-serve result twice?

Not by default. DOCLING_SERVE_SINGLE_USE_RESULTS is true, so a result is deleted after it is fetched (after a 300-second delay). Set it to false if clients may retry.

How do I connect Docling to Claude Desktop or another MCP client?

Use docling-mcp: add a server entry that runs uvx --from docling-mcp docling-mcp-server. Since docling-mcp 3.x the default mode is remote, so set DOCLING_MCP_SERVICE_URL to your docling-serve, or install docling-mcp[local] and set DOCLING_MCP_CONVERSION_MODE=local.

Sources