DEVELOPER API · V1

PaddleOCR API Docs — Document Parsing API for Markdown & JSON

Upload a document, poll the task and download Markdown, JSON and images in one ZIP. Covers auth, endpoints and limits. API access requires Pro membership.

Getting started

Overview

Create one task per file or URL, poll its status, then download the ZIP. Requests are asynchronous. Pro Monthly and Pro Annual support the API; Free does not.

API base URL
>
https://YOUR_API_HOST/api/open/v1

Replace YOUR_API_HOST with your API domain. All paths below use this base URL. The examples use placeholder IDs and signed URLs.

Authentication

Send Authorization: Bearer followed by your Pro API key on every API request. Get or reset the key using the API key button. Keys are case-sensitive: sk- plus 24 random letters and digits. User login JWTs do not authenticate these endpoints.

FieldTypeRequiredDescription
AuthorizationstringYesBearer followed by the Pro API key; required on every endpoint.
Request headers
>
Authorization: Bearer sk-YOUR_API_KEY

Keep the key on your server. Do not expose it in public frontend code, repositories or download requests. Downloads use their own signed URL. Resetting the key immediately invalidates the old key; API access also requires an active Pro membership.

Quick start

Replace the API host, set MINERU_API_KEY, and provide paper.pdf. These examples upload once, poll every 3 seconds for up to 30 minutes, then stream the ZIP to disk. The polling timeout is a client limit and does not cancel the server task. Use the task ID to continue querying. Python needs requests; Node.js needs version 20+; cURL uses Bash and jq.

Python
>
# pip install requests
import os
import time
import requests

API = "https://YOUR_API_HOST/api/open/v1"
headers = {"Authorization": "Bearer " + os.environ["MINERU_API_KEY"]}
with open("paper.pdf", "rb") as source:
    response = requests.post(API + "/tasks", headers=headers, files={"file": ("paper.pdf", source, "application/pdf")},
        data={"force_ocr": "true", "extra_formats": '["docx","html"]'}, timeout=120)
response.raise_for_status()
body = response.json()
if body["code"] != 0:
    raise RuntimeError(body["code"] + ": " + body["msg"])
task_id = body["data"]["task_id"]

deadline = time.monotonic() + 1800
while time.monotonic() < deadline:
    response = requests.get(API + "/tasks/" + task_id, headers=headers, timeout=30)
    response.raise_for_status()
    body = response.json()
    if body["code"] != 0:
        raise RuntimeError(body["code"] + ": " + body["msg"])
    data = body["data"]
    task = data["task"]
    if task["status"] == "completed":
        url = data["results"].get("full_zip_url")
        if not url:
            raise RuntimeError("ZIP unavailable; check expires_at")
        # Download separately: do not send the API key to the download host.
        with requests.get(url, stream=True, timeout=120) as download:
            download.raise_for_status()
            with open("result.zip", "wb") as output:
                for chunk in download.iter_content(1024 * 1024):
                    output.write(chunk)
        print("Saved result.zip")
        break
    if task["status"] in {"failed", "expired"}:
        raise RuntimeError(task["status"] + ": " + task["error"])
    time.sleep(3)
else:
    raise TimeoutError("Polling timed out; task ID: " + task_id)

API endpoints

Create a task

POST/api/open/v1/tasks

One endpoint: POST /api/open/v1/tasks. Choose one input method: upload a file using multipart/form-data, or send source_url using JSON. Both return data.task_id for the same task query flow. Each accepted submission creates a new task.

Chemical structures accept PDF and images only. force_ocr and extra_formats are unavailable.

Option 1: upload a file

Upload exactly one file directly to the API server. Let your HTTP client set the multipart boundary; do not manually set Content-Type. Send only file and optional force_ocr and extra_formats. The server checks the received bytes and units before storing and queueing. No separate upload-complete request is needed.

Body parameters · multipart/form-data
FieldTypeRequiredDescription
filebinaryYesOne file; filename must include a supported extension.
parse_typestring (0 | 1)No0: general documents (default); 1: chemical structures.Default: 0
force_ocrstring (true | false)Notrue / false; For PDF only. On: ignore the existing text layer and use OCR. Off: detect automatically. Other formats are unaffected.Default: false
extra_formatsstring (JSON)NoJSON-encoded array string, e.g. ["docx","html"]. Values: html, docx, latex.Default: []
Request example · cURL
>
curl --fail-with-body "https://YOUR_API_HOST/api/open/v1/tasks" \
  -H "Authorization: Bearer $API_KEY" \
  -F '[email protected]' \
  -F 'force_ocr=true' \
  -F 'extra_formats=["docx","html"]'
Response fields · 200 OK

HTTP 200 means the task was created. code is 0, msg is ok, and data contains only task_id. Query the task to get its status and results.

FieldTypeDescription
codeinteger0 on successful task creation.
data.task_idstring (UUID)Task UUID returned as data.task_id when created.
msgstringok on successful task creation.
Response example
>
{
  "code": 0,
  "data": {
    "task_id": "a90e6ab6-44f3-4554-b459-b62fe4c6b436"
  },
  "msg": "ok"
}

Option 2: submit a URL

Send JSON instead of multipart. source_url must be a public HTTP/HTTPS document, image or HTML URL. The source must expose a positive Content-Length and a supported format from headers or filename; missing, conflicting or changed metadata fails the task. Private addresses, URLs containing credentials, compressed or chunked responses are rejected. At most 5 redirects. Creation returns queued; fetch failures appear later in task.error.

Body parameters · application/json
FieldTypeRequiredDescription
source_urlstringYesPublic HTTP/HTTPS source URL, subject to metadata and size validation.
parse_typeinteger (0 | 1)No0: general documents (default); 1: chemical structures.Default: 0
force_ocrbooleanNotrue / false; For PDF only. On: ignore the existing text layer and use OCR. Off: detect automatically. Other formats are unaffected.Default: false
extra_formatsstring[]NoArray of html, docx and/or latex, not a JSON string. Markdown and JSON are already included.Default: []
Request example · cURL
>
curl --fail-with-body "https://YOUR_API_HOST/api/open/v1/tasks" \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"source_url":"https://example.com/paper.pdf","force_ocr":true,"extra_formats":["docx","html"]}'
Response fields · 200 OK

HTTP 200 means the task was created. code is 0, msg is ok, and data contains only task_id. Query the task to get its status and results.

FieldTypeDescription
codeinteger0 on successful task creation.
data.task_idstring (UUID)Task UUID returned as data.task_id when created.
msgstringok on successful task creation.
Response example
>
{
  "code": 0,
  "data": {
    "task_id": "a90e6ab6-44f3-4554-b459-b62fe4c6b436"
  },
  "msg": "ok"
}
Chemical task · cURL
>
curl --fail-with-body "https://YOUR_API_HOST/api/open/v1/tasks" \
  -H "Authorization: Bearer $API_KEY" \
  -F '[email protected]' \
  -F 'parse_type=1'

List tasks

GET/api/open/v1/tasks

Lists your web and API tasks together, newest first. Items contain only task_id, status, credits, created_at and file name, size, mime_type and source_type. Query details for file metadata, failure reason, expiry and result URLs. page_size accepts 1–100; invalid values default to 20. page below 1 defaults to 1.

Query parameters

FieldTypeRequiredDescription
pageintegerNo1-based page number; invalid or below 1 uses 1.Default: 1
page_sizeintegerNoItems per page, 1–100; invalid values use 20.Default: 20
searchstringNoFilter by file name across all your tasks (up to 255 characters).
statusstringNoOptional status filter: queued, running, completed, failed or expired. Separate multiple statuses with commas.
Request example · cURL
>
curl --fail-with-body "https://YOUR_API_HOST/api/open/v1/tasks?page=1&page_size=20" \
  -H "Authorization: Bearer $API_KEY"

Response fields · 200 OK

FieldTypeDescription
data.itemsTaskSummary[]Task summaries containing only fields in the TaskSummary table.
data.has_morebooleanWhether another page is available.
data.pageinteger1-based page number; invalid or below 1 uses 1.
data.page_sizeintegerItems per page, 1–100; invalid values use 20.
Response example
>
{
  "code": 0,
  "msg": "ok",
  "data": {
    "items": [
      {
        "task_id": "a90e6ab6-44f3-4554-b459-b62fe4c6b436",
        "parse_type": 0,
        "status": "completed",
        "credits": 2,
        "created_at": "2026-10-12T07:04:05Z",
        "file": {
          "name": "paper.pdf",
          "size": 12345,
          "mime_type": "application/pdf",
          "source_type": "upload"
        }
      }
    ],
    "has_more": false,
    "page": 1,
    "page_size": 20
  }
}

TaskSummary

FieldTypeDescription
task_idstring (UUID)Task UUID returned as data.task_id when created.
parse_typeinteger (0 | 1)0: general documents (default); 1: chemical structures.
statusstringOverall task state; see Task states.
creditsintegerBillable count: PDF pages, PPT slides or Excel worksheets; Word, HTML and images count as 1. It is 0 until URL file inspection finishes.
created_atstring (RFC3339)Record creation time in RFC3339.
file.namestringStored input filename; URL tasks may initially show the source host.
file.sizeinteger (int64)Actual stored bytes; 0 before URL download.
file.mime_typestringStored MIME type; may be empty while fetching.
file.source_typestringupload or url.

Get task results

GET/api/open/v1/tasks/{id}

Read data.task.status. results is always a dictionary: empty for queued, running, failed or expired, and contains document URLs when completed. The ZIP URL is results.full_zip_url. Failed tasks return HTTP 200 with code task_failed and the reason in msg; expired results use code result_expired.

Path parameters

FieldTypeRequiredDescription
idstring (UUID)YesTask UUID returned as data.task_id when created.
Request example · cURL
>
curl --fail-with-body "https://YOUR_API_HOST/api/open/v1/tasks/$TASK_ID" \
  -H "Authorization: Bearer $API_KEY"

Response fields · 200 OK

FieldTypeDescription
data.taskTaskTask object, always returned on successful detail lookup.
data.fileFileInput File object; URL metadata may not be ready yet.
data.resultsobjectA dictionary of file and image URLs. Unfinished, failed or expired tasks return an empty dictionary.
Processing response · running
>
{
  "code": 0,
  "msg": "ok",
  "data": {
    "task": {
      "task_id": "a90e6ab6-44f3-4554-b459-b62fe4c6b436",
      "parse_type": 0,
      "status": "running",
      "credits": 2,
      "force_ocr": true,
      "expires_at": "2026-10-19T07:04:05Z",
      "error": "",
      "created_at": "2026-10-12T07:04:05Z"
    },
    "file": {
      "name": "paper.pdf",
      "size": 12345,
      "mime_type": "application/pdf",
      "suffix": "pdf",
      "source_type": "upload",
      "source_url": ""
    },
    "results": {}
  }
}
Response example · completed
>
{
  "code": 0,
  "msg": "ok",
  "data": {
    "task": {
      "task_id": "a90e6ab6-44f3-4554-b459-b62fe4c6b436",
      "parse_type": 0,
      "status": "completed",
      "credits": 2,
      "force_ocr": true,
      "expires_at": "2026-10-19T07:04:05Z",
      "error": "",
      "created_at": "2026-10-12T07:04:05Z"
    },
    "file": {
      "name": "paper.pdf",
      "size": 12345,
      "mime_type": "application/pdf",
      "suffix": "pdf",
      "source_type": "upload",
      "source_url": ""
    },
    "results": {
      "original_url": "https://downloads.example.com/uploads/4001a517-cd93-47a6-a56a-e53d2a0828df/aa0e6ab6-44f3-4554-b459-b62fe4c6b437.pdf?token=SIGNED_TOKEN",
      "markdown_url": "https://downloads.example.com/results/a90e6ab6-44f3-4554-b459-b62fe4c6b436/preview/markdown.md?token=SIGNED_TOKEN",
      "middle_json_url": "https://downloads.example.com/results/a90e6ab6-44f3-4554-b459-b62fe4c6b436/preview/middle_json.json?token=SIGNED_TOKEN",
      "structured_json_url": "https://downloads.example.com/results/a90e6ab6-44f3-4554-b459-b62fe4c6b436/preview/structured_content.json?token=SIGNED_TOKEN",
      "images": {
        "images/001.jpg": "https://downloads.example.com/results/a90e6ab6-44f3-4554-b459-b62fe4c6b436/preview/images/001.jpg?token=SIGNED_TOKEN"
      },
      "full_zip_url": "https://downloads.example.com/results/a90e6ab6-44f3-4554-b459-b62fe4c6b436/exports/6b0e6ab6-44f3-4554-b459-b62fe4c6b439.zip?token=SIGNED_TOKEN"
    }
  }
}
Failed task response · HTTP 200
>
{
  "code": "task_failed",
  "msg": "MinerU job ended: failed",
  "data": {
    "task": {
      "task_id": "a90e6ab6-44f3-4554-b459-b62fe4c6b436",
      "parse_type": 0,
      "status": "failed",
      "credits": 2,
      "force_ocr": true,
      "expires_at": "2026-10-19T07:04:05Z",
      "error": "MinerU job ended: failed",
      "created_at": "2026-10-12T07:04:05Z"
    },
    "file": {
      "name": "paper.pdf",
      "size": 12345,
      "mime_type": "application/pdf",
      "suffix": "pdf",
      "source_type": "upload",
      "source_url": ""
    },
    "results": {}
  }
}
Chemical task · completed
>
{
  "code": 0,
  "msg": "ok",
  "data": {
    "task": {
      "task_id": "a90e6ab6-44f3-4554-b459-b62fe4c6b436",
      "parse_type": 1,
      "status": "completed",
      "credits": 2,
      "force_ocr": false,
      "expires_at": "2026-10-19T07:04:05Z",
      "error": "",
      "created_at": "2026-10-12T07:04:05Z"
    },
    "file": {
      "name": "paper.pdf",
      "size": 12345,
      "mime_type": "application/pdf",
      "suffix": "pdf",
      "source_type": "upload",
      "source_url": ""
    },
    "results": {
      "original_url": "https://downloads.example.com/uploads/4001a517-cd93-47a6-a56a-e53d2a0828df/aa0e6ab6-44f3-4554-b459-b62fe4c6b437.pdf?token=SIGNED_TOKEN",
      "json_url": "https://downloads.example.com/results/a90e6ab6-44f3-4554-b459-b62fe4c6b436/molecular_structures.json?token=SIGNED_TOKEN"
    }
  }
}

Task

FieldTypeDescription
task_idstring (UUID)Task UUID returned as data.task_id when created.
parse_typeinteger (0 | 1)0: general documents (default); 1: chemical structures.
statusstringOverall task state; see Task states.
creditsintegerBillable count: PDF pages, PPT slides or Excel worksheets; Word, HTML and images count as 1. It is 0 until URL file inspection finishes.
force_ocrbooleantrue / false; For PDF only. On: ignore the existing text layer and use OCR. Off: detect automatically. Other formats are unaffected.
expires_atstring | null (RFC3339)Task retention deadline; may be null before results/failure are recorded.
errorstringProcessing error or recovery message; empty when none.
created_atstring (RFC3339)Record creation time in RFC3339.

File

FieldTypeDescription
namestringStored input filename; URL tasks may initially show the source host.
sizeinteger (int64)Actual stored bytes; 0 before URL download.
mime_typestringStored MIME type; may be empty while fetching.
suffixstringLowercase extension without a dot; may be empty while fetching.
source_typestringupload or url.
source_urlstringUser-submitted source URL; empty for uploaded files.

results

FieldTypeDescription
original_urlstringSigned original file URL.
json_urlstringChemical JSON download URL; returned only for parse_type=1.
markdown_urlstringSigned native markdown.md URL for preview reading.
middle_json_urlstringSigned middle_json.json URL with pages, block structure and normalized bounding boxes.
structured_json_urlstringSigned structured_content.json URL; omitted if the resource is absent.
imagesRecord<string, string>Map of native relative paths to signed image URLs; empty object when there are no images.
full_zip_urlstringresults.full_zip_url is the signed final API ZIP URL, returned only when completed and retained.

Reference notes

Formats and limits

Pro limits: 200 MiB per document, 50 MiB per image, and 500 billable units. API and web share 5 daily plus 1,000 monthly units, using daily units first. Usage is reserved after validation and charged when the base parse result is saved. Confirmed parse failure or termination releases reservations. Publication/export failure does not refund successful parsing; failed or timed-out execution automatically releases uncharged reservations. Exports do not charge again.

FormatFilename extensionsBillable units
PDF.pdfPhysical PDF pages
Word.doc, .docx1 unit per file
PowerPoint.ppt, .pptxNumber of slides
Excel.xls, .xlsxNumber of worksheets
Images.png, .jpg, .jpeg, .webp, .gif, .bmp, .tif, .tiff1 unit per file
HTML.html, .htm1 unit per file

Upload format is determined by the filename extension, case-insensitively. PDF, PowerPoint and Excel must have readable page/unit metadata. MIME is not used to validate uploaded contents. Empty files, unsupported extensions, encrypted or unreadable PDFs and over-limit input are rejected. HTML parsing is static and does not run JavaScript. 1 MiB = 1,048,576 bytes.

Task states

Keep polling queued or running. Download on completed; read error and stop on failed. expired means retention has ended. status describes the whole task; API tasks complete only when the final ZIP is ready.

task.statusDescription
queuedAccepted; awaiting processing or fetching URL input.
runningProcessing, including parsing, saving results and API packaging.
completedThe final result is ready to query and download.
failedProcessing failed; read error. Do not automatically create a replacement task.
expiredThe result retention deadline in expires_at has passed; no download URLs.

ZIP results and downloads

The API produces one final ZIP. Markdown, Middle JSON, structured JSON and saved images are included by default. extra_formats supports html, docx and latex only; the requested files are added to that same ZIP. Do not create a separate web export job for an API task. No extras means results.full_zip_url points to base.zip; extras mean it points to exports/a ZIP file.

Chemical tasks return results.original_url and results.json_url, without a ZIP or extra exports.

Chemical results are a JSON array. Each molecule retains all native MolParser fields; no molecules returns [].

molecular_structures.json
>
source, input_index, page_index, bbox, confidence, raw_caption, caption, smi, esmi, cxsmiles, markush, sru, groups
ZIP
>
result.zip
├── markdown.md
├── middle_json.json
├── structured_content.json
├── images/
│   └── …
├── model_output.json       (optional)
├── result.html             (extra_formats: html)
├── result.docx             (extra_formats: docx)
└── result.tex              (extra_formats: latex)

Results are retained for 7 days. results.full_zip_url is signed for at most 15 minutes and never beyond retention. Download with a plain GET, without Authorization. If the signature expires while still retained, query details again for a new URL. Preview image links last at most 2 hours; other file links last at most 15 minutes.

ZIP documents keep native relative image references and include saved images, so those references do not depend on CDN signatures. Extract the whole archive and keep its folder structure. Remote images referenced by HTML input are not automatically archived and may still point to the source website.

Errors and recovery

code 0 means success. A nonzero code identifies the error category; msg gives the reason. Queries for failed or expired tasks return HTTP 200 with task_failed or result_expired and an empty results object.

HTTP 402
>
{
  "code": "quota_exceeded",
  "data": null,
  "msg": "not enough available pages/units"
}
HTTPcodeDescription
200task_failedParsing failed. See msg for the reason.
200result_expiredResults have expired.
400invalid_requestInvalid parameters or file. See msg for the reason.
400too_many_pagesDocument exceeds the page or billable-unit limit.
401unauthorizedAPI authentication failed.
402quota_exceededInsufficient available quota.
403user_disabledAccount is disabled.
403pro_requiredAn active Pro membership is required.
404not_foundTask not found or access denied.
413file_too_largeFile exceeds your plan’s size limit.
500internal_errorServer processing failed. See msg for the reason.