← Blog

Self-host the MinerU API on RunPod

Evaluate a move from the MinerU cloud API to RunPod Serverless with a compatibility client, documented gaps, and an explicit infrastructure data path.

If you're calling the official MinerU API (mineru.net/api/v4) in production, you may hit its daily quota of 1,000 high-priority pages, its 200 MB / 200-page file limits, or a regional data requirement. You can deploy the open-source MinerU runtime on RunPod Serverless, choose your own bucket, and keep a compatible create-task / poll / download loop. It is not a drop-in copy: the hosted service and your endpoint can run different versions, models, and configuration.

The cloud API is the right way to try MinerU. Once you're parsing real volume, self-hosting changes the economics and the data path. Here's why, what it costs, and how to move your code over.

Why self-host the MinerU API instead of using mineru.net?

Three reasons: cost at volume, regional control, and runtime control. The cloud API meters a daily high-priority page quota and then deprioritizes you; your RunPod endpoint has no MinerU SaaS quota. You choose the MinerU version, GPU, concurrency, RunPod region, and object-storage provider.

The official MinerU API caps each file at 200 MB and gives each account a daily quota of 1,000 high-priority pages (see the published limits) before jobs drop to lower priority. That ceiling is fine for evaluation and light use. It becomes a planning problem the moment you're ingesting thousands of pages a day on a deadline.

The data path matters too. On the SaaS, each document goes through mineru.net. With this template, the document goes through infrastructure in your RunPod account and, when configured, your chosen object-storage provider. That removes the MinerU SaaS from the parsing path; it does not remove third-party infrastructure or the need to evaluate its region, subprocessors, and data-processing terms.

The trade-off is honest: you run the infrastructure. In a four-request test, the fast FlashBoot path returned in ~7-8 s after a host had already paid the ~110 s model boot, while another host repeated the full initialization. RunPod does not document that scheduling or restore scope as a guarantee. The model is baked into the image, so nothing is downloaded at request time. If your traffic is a handful of pages a month, the cloud API's free tier is simpler; steady volume is where self-hosting becomes worth measuring.

What does self-hosting MinerU cost vs the cloud API?

Roughly $0.001 per page warm on a 24 GB RTX 4090, plus a ~$0.03 fresh-host tax in the measured ~110 s vLLM boot. The observed fast FlashBoot path reduced a later same-context request to ~7-8 s. RunPod bills GPU time by the second and can scale workers to zero. The cloud API's published quota controls high-priority processing; self-hosting replaces that MinerU SaaS ceiling with GPU, storage, and network costs you operate.

The real number depends on how many pages share a warm window and how often a request lands on a fresh host. Benchmark your document mix and use the measured workload-shape math instead of treating one per-page number as universal.

How do you deploy your own MinerU endpoint on RunPod?

Deploy the open-source mineru-runpod template from the RunPod Hub, or fork it and point RunPod's GitHub build at your fork. Create a Serverless Endpoint on a 24 GB GPU (ADA_24 / RTX 4090) with FlashBoot enabled. For full_zip_url parity with the cloud API, also set four BUCKET_* env vars pointing at an S3-compatible bucket.

The fastest path is the Hub listing: one click, fill in the deploy-time form, done. The full walkthrough (fork-and-build and bring-your-own-image included) is in the deploy guide.

The one piece worth getting right up front is object storage. The compat client returns results as a full_zip_url, which means the worker uploads the output archive to a bucket and hands back a presigned URL, exactly like the SaaS. That path needs four env vars on the endpoint:

BUCKET_ENDPOINT_URL=https://<account>.r2.cloudflarestorage.com
BUCKET_NAME=mineru-outputs
BUCKET_ACCESS_KEY_ID=<key>
BUCKET_SECRET_ACCESS_KEY=<secret>

Cloudflare R2 is a common pairing because direct R2 egress is free; any compatible store works, including Backblaze B2, MinIO, and AWS S3. To get started you need a RunPod account; this signup link is a referral link. At the measured rates, $5 is roughly 160 full cold starts or 5,000 warm pages before storage and other charges.

How do you migrate your code off the MinerU API?

Install the mineru-client package and swap the supported requests calls for MineruApiClient. It maps create_task / get_task into compatible response dicts, so the polling structure can remain. Under the hood it requests archive_format="zip", so full_zip_url points to a .zip as it does in the SaaS response.

Install it (no version pin, so it tracks the repo):

pip install "mineru-client @ git+https://github.com/sergeyshmakov/mineru-runpod"

Here's the before and after. The official API, polling by hand:

import requests, time

H = {"Authorization": f"Bearer {MINERU_TOKEN}"}
task_id = requests.post(
    "https://mineru.net/api/v4/extract/task",
    headers=H, json={"url": pdf_url, "model_version": "vlm"},
).json()["data"]["task_id"]

while True:
    data = requests.get(
        f"https://mineru.net/api/v4/extract/task/{task_id}", headers=H
    ).json()["data"]
    if data["state"] in ("done", "failed"):
        break
    time.sleep(2)
zip_url = data["full_zip_url"]   # then download + unzip yourself

Self-hosted, against your own endpoint:

from mineru_client import MineruApiClient

client = MineruApiClient(endpoint_id="<your-endpoint-id>", api_key="<runpod-key>")

task_id = client.create_task(pdf_url, model_version="vlm")["data"]["task_id"]
done = client.wait_for_task(task_id)        # polls to a terminal state
client.download_results(done, "./out")      # full_zip_url is a real .zip; unpacked for you

Same lifecycle, same {"code": 0, "data": {...}} response shape. The parameter names map across cleanly too: model_version to the worker's backend, language to lang, enable_formula / enable_table straight through. The full field-by-field mapping is in Migrate from the MinerU API, and the Clients page covers the native client if you'd rather use the worker's own (richer) request shape once you've moved.

One auth note: the self-hosted endpoint authenticates with your RunPod API key, also via Authorization: Bearer, so even that part of your code barely changes.

What doesn't carry over from the cloud API?

The compat client rejects callback because RunPod's webhook shape differs from MinerU's signed callback. It does not support extra_formats (docx/html/latex), the MinerU-HTML model, or multi-range page_ranges such as "2,4-6". It accepts seed for call compatibility but does not use it without callback support. There is no batch endpoint, and full_zip_url requires the BUCKET_* setup above.

The full list, so there are no surprises:

Cloud API featureSelf-hosted status
create_task / get_task, state machine, full_zip_urlSupported (with object storage configured)
model_version: pipeline / vlmSupported (maps to the worker backends)
model_version: MinerU-HTMLNot supported, raises
extra_formats (docx / html / latex)Not produced by this worker, raises
page_ranges multi-range ("2,4-6")One contiguous range per job, raises otherwise
callbackRejected; poll with get_task / wait_for_task instead
seedAccepted but unused because callback signing is not implemented
Batch (/extract/task/batch)Not offered; submit tasks individually and raise workers_max — RunPod's queue parallelizes them across workers

Where it falls down: if your pipeline leans on webhook callbacks or exports to DOCX/LaTeX, the compat client isn't a clean swap today. There's no batch endpoint either, but you rarely need one: submit tasks individually and let RunPod fan them across workers_max workers (the queue parallelizes them). For everything else, the create / poll / download path behaves like the SaaS.

One more practical detail. The compat client is URL-only, mirroring the cloud API's POST /extract/task, which doesn't accept file uploads. For small local files you don't need to host anything: use the native MineruClient with file_b64 instead. RunPod's 20 MB /runsync JSON limit leaves roughly 15 MB for raw bytes after base64 expansion. I parsed a 522 KB scanned Russian invoice that way and got clean Cyrillic Markdown back, no bucket round-trip involved.

FAQ

Is the MinerU API free?

The official MinerU cloud API has a free daily quota of 1,000 high-priority pages, after which jobs run at lower priority. There's no per-call charge inside the quota. A RunPod deployment has no MinerU SaaS page quota; it replaces that limit with per-second GPU billing and storage or network costs.

What are the MinerU API's limits?

The precision cloud API currently caps a file at 200 MB and 200 pages, with 1,000 high-priority pages per account per day (see the published API docs). This worker rejects decoded file_b64 data over 20 MB, while RunPod's JSON limit makes the practical raw-file ceiling lower; file_url is capped at 200 MB, and volume_path has no explicit application cap. GPU memory, temporary disk, output size, and endpoint timeout still apply.

Can I self-host MinerU without RunPod?

Yes. MinerU is open source under its MinerU Open Source License and supports several deployment paths. This template targets RunPod Serverless because it scales to zero; on a fixed GPU host you would run MinerU directly and skip this wrapper.

Does the self-hosted output match the cloud API's full_zip_url?

Yes, when the endpoint has object storage configured. The compat client requests archive_format="zip", so the worker uploads a .zip to your bucket and returns a presigned full_zip_url, the same container and field the SaaS returns. download_results fetches and unpacks it for you, and autodetects .tar.gz too if you change the format.

Is self-hosting actually cheaper than the MinerU API?

At steady volume, usually yes, because you stop being throttled by the daily quota and pay only for GPU seconds used. The crossover depends on cold-start amortization: dense traffic lands near $0.001/page, sparse one-doc-per-cold-start traffic closer to $0.005–$0.01. Below a few hundred pages a month, the cloud API's free tier is the cheaper and simpler option.

Do my documents stay private when self-hosting?

The MinerU SaaS is no longer in the data path. Files are processed in your RunPod account and may be read from or written to your configured storage provider. Whether that satisfies a privacy or residency requirement depends on those vendors, selected regions, access controls, retention, logging, and your agreements with them.


Self-hosting is about control over quotas, versions, infrastructure, and billing, not guaranteed output parity. Compare both paths on representative documents before switching. If the trade fits, deploy the template from the RunPod Hub or fork it for tighter version control.

Last updated August 7, 2026