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.
https://YOUR_API_HOST/api/open/v1Replace 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.
| Field | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | Bearer followed by the Pro API key; required on every endpoint. |
Authorization: Bearer sk-YOUR_API_KEYKeep 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.
# 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
/api/open/v1/tasksOne 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
| Field | Type | Required | Description |
|---|---|---|---|
file | binary | Yes | One file; filename must include a supported extension. |
parse_type | string (0 | 1) | No | 0: general documents (default); 1: chemical structures.Default: 0 |
force_ocr | string (true | false) | No | true / false; For PDF only. On: ignore the existing text layer and use OCR.
Off: detect automatically. Other formats are unaffected.Default: false |
extra_formats | string (JSON) | No | JSON-encoded array string, e.g. ["docx","html"]. Values:
html, docx, latex.Default: [] |
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.
| Field | Type | Description |
|---|---|---|
code | integer | 0 on successful task creation. |
data.task_id | string (UUID) | Task UUID returned as data.task_id when created. |
msg | string | ok on successful task creation. |
{
"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
| Field | Type | Required | Description |
|---|---|---|---|
source_url | string | Yes | Public HTTP/HTTPS source URL, subject to metadata and size validation. |
parse_type | integer (0 | 1) | No | 0: general documents (default); 1: chemical structures.Default: 0 |
force_ocr | boolean | No | true / false; For PDF only. On: ignore the existing text layer and use OCR.
Off: detect automatically. Other formats are unaffected.Default: false |
extra_formats | string[] | No | Array of html, docx and/or latex, not a JSON string. Markdown and JSON are
already included.Default: [] |
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.
| Field | Type | Description |
|---|---|---|
code | integer | 0 on successful task creation. |
data.task_id | string (UUID) | Task UUID returned as data.task_id when created. |
msg | string | ok on successful task creation. |
{
"code": 0,
"data": {
"task_id": "a90e6ab6-44f3-4554-b459-b62fe4c6b436"
},
"msg": "ok"
}
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
/api/open/v1/tasksLists 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
| Field | Type | Required | Description |
|---|---|---|---|
page | integer | No | 1-based page number; invalid or below 1 uses 1.Default: 1 |
page_size | integer | No | Items per page, 1–100; invalid values use 20.Default: 20 |
search | string | No | Filter by file name across all your tasks (up to 255 characters). |
status | string | No | Optional status filter: queued, running, completed, failed or expired. Separate multiple statuses with commas. |
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
| Field | Type | Description |
|---|---|---|
data.items | TaskSummary[] | Task summaries containing only fields in the TaskSummary table. |
data.has_more | boolean | Whether another page is available. |
data.page | integer | 1-based page number; invalid or below 1 uses 1. |
data.page_size | integer | Items per page, 1–100; invalid values use 20. |
{
"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
| Field | Type | Description |
|---|---|---|
task_id | string (UUID) | Task UUID returned as data.task_id when created. |
parse_type | integer (0 | 1) | 0: general documents (default); 1: chemical structures. |
status | string | Overall task state; see Task states. |
credits | integer | Billable count: PDF pages, PPT slides or Excel worksheets; Word, HTML and images count as 1. It is 0 until URL file inspection finishes. |
created_at | string (RFC3339) | Record creation time in RFC3339. |
file.name | string | Stored input filename; URL tasks may initially show the source host. |
file.size | integer (int64) | Actual stored bytes; 0 before URL download. |
file.mime_type | string | Stored MIME type; may be empty while fetching. |
file.source_type | string | upload or url. |
Get task results
/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
| Field | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | Task UUID returned as data.task_id when created. |
curl --fail-with-body "https://YOUR_API_HOST/api/open/v1/tasks/$TASK_ID" \
-H "Authorization: Bearer $API_KEY"Response fields · 200 OK
| Field | Type | Description |
|---|---|---|
data.task | Task | Task object, always returned on successful detail lookup. |
data.file | File | Input File object; URL metadata may not be ready yet. |
data.results | object | A dictionary of file and image URLs. Unfinished, failed or expired tasks return an empty dictionary. |
{
"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": {}
}
}{
"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"
}
}
}{
"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": {}
}
}{
"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
| Field | Type | Description |
|---|---|---|
task_id | string (UUID) | Task UUID returned as data.task_id when created. |
parse_type | integer (0 | 1) | 0: general documents (default); 1: chemical structures. |
status | string | Overall task state; see Task states. |
credits | integer | Billable count: PDF pages, PPT slides or Excel worksheets; Word, HTML and images count as 1. It is 0 until URL file inspection finishes. |
force_ocr | boolean | true / false; For PDF only. On: ignore the existing text layer and use OCR. Off: detect automatically. Other formats are unaffected. |
expires_at | string | null (RFC3339) | Task retention deadline; may be null before results/failure are recorded. |
error | string | Processing error or recovery message; empty when none. |
created_at | string (RFC3339) | Record creation time in RFC3339. |
File
| Field | Type | Description |
|---|---|---|
name | string | Stored input filename; URL tasks may initially show the source host. |
size | integer (int64) | Actual stored bytes; 0 before URL download. |
mime_type | string | Stored MIME type; may be empty while fetching. |
suffix | string | Lowercase extension without a dot; may be empty while fetching. |
source_type | string | upload or url. |
source_url | string | User-submitted source URL; empty for uploaded files. |
results
| Field | Type | Description |
|---|---|---|
original_url | string | Signed original file URL. |
json_url | string | Chemical JSON download URL; returned only for parse_type=1. |
markdown_url | string | Signed native markdown.md URL for preview reading. |
middle_json_url | string | Signed middle_json.json URL with pages, block structure and normalized bounding boxes. |
structured_json_url | string | Signed structured_content.json URL; omitted if the resource is absent. |
images | Record<string, string> | Map of native relative paths to signed image URLs; empty object when there are no images. |
full_zip_url | string | results.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.
| Format | Filename extensions | Billable units |
|---|---|---|
.pdf | Physical PDF pages | |
| Word | .doc, .docx | 1 unit per file |
| PowerPoint | .ppt, .pptx | Number of slides |
| Excel | .xls, .xlsx | Number of worksheets |
| Images | .png, .jpg, .jpeg, .webp, .gif, .bmp, .tif, .tiff | 1 unit per file |
| HTML | .html, .htm | 1 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.status | Description |
|---|---|
queued | Accepted; awaiting processing or fetching URL input. |
running | Processing, including parsing, saving results and API packaging. |
completed | The final result is ready to query and download. |
failed | Processing failed; read error. Do not automatically create a replacement task. |
expired | The 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 [].
source, input_index, page_index, bbox, confidence, raw_caption, caption, smi, esmi, cxsmiles, markush, sru, groupsresult.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.
{
"code": "quota_exceeded",
"data": null,
"msg": "not enough available pages/units"
}| HTTP | code | Description |
|---|---|---|
| 200 | task_failed | Parsing failed. See msg for the reason. |
| 200 | result_expired | Results have expired. |
| 400 | invalid_request | Invalid parameters or file. See msg for the reason. |
| 400 | too_many_pages | Document exceeds the page or billable-unit limit. |
| 401 | unauthorized | API authentication failed. |
| 402 | quota_exceeded | Insufficient available quota. |
| 403 | user_disabled | Account is disabled. |
| 403 | pro_required | An active Pro membership is required. |
| 404 | not_found | Task not found or access denied. |
| 413 | file_too_large | File exceeds your plan’s size limit. |
| 500 | internal_error | Server processing failed. See msg for the reason. |