Extract a document
Returns the markdown text of one or more sources without indexing them. Same input shape as POST /v2/documents/process — multipart files + JSON URL arrays — but the response is the extracted content, not a Qdrant-indexed document_id.
Use this when you want the raw markdown for your own pipeline: feeding it to a different LLM, displaying it to your users, or running an offline transform. Sources also land in your api_documents table (with chunk_count = 0) so you can audit what was extracted.
Request
Headers
| Header | Required | Notes |
|---|---|---|
X-API-Key | yes | Your test or production key |
X-External-User-Id | yes (or X-User-Id) | Identifies the learner |
Content-Type | yes | multipart/form-data; boundary=… |
Form fields
files file[] Up to 10 files. Supported: PDF, DOCX, PPTX, TXT, MD, HTML, PNG, JPG, WEBP. 50 MB max each. |
youtube_urls string JSON-encoded array of YouTube video URLs. Returns transcripts. |
web_urls string JSON-encoded array of web page URLs. Returns readable markdown via Jina Reader. |
Pricing
Every extraction is metered. Each successful entry comes back with the dollars it consumed:
| Source type | Cost |
|---|---|
| PDFs and images (OCR) | $0.002 per page |
| DOCX, PPTX, HTML, web URLs | $0.001 per file or URL |
| YouTube transcripts | $0.0005 per URL |
Failures don’t bill — if a file errors out (status: "error"), billed_amount_usd stays at 0. The total_billed_usd field on the response is the sum of every successful entry.
Looking for indexed documents you can query later with RAG? Use POST /v2/documents/process instead — that ingests, chunks, embeds, and writes into your Qdrant collection. Extract-only is for when you just want the markdown back.
Example
curl -X POST "https://api.nuton.app/v2/documents/extract" \
-H "X-API-Key: $NUTON_KEY" \
-H "X-External-User-Id: alice@example.com" \
-F "files=@whitepaper.pdf" \
-F "youtube_urls=[\"https://youtu.be/kwSVtQ7dziU\"]" \
-F "web_urls=[\"https://en.wikipedia.org/wiki/Photosynthesis\"]"
Returns
{
"documents": [
{
"id": "d_01HW0XYZ",
"filename": "whitepaper.pdf",
"source_type": "file",
"source_url": "https://cdn.nuton.app/sources/.../whitepaper.pdf",
"mime_type": "application/pdf",
"pages": 12,
"char_count": 38421,
"markdown": "# Whitepaper title\n\nIn this paper we show that…",
"billed_amount_usd": 0.024,
"status": "ready",
"error_message": null,
"created_at": "2026-05-22T14:00:00Z"
},
{
"id": "d_01HW0XZA",
"filename": "https://youtu.be/kwSVtQ7dziU",
"source_type": "youtube",
"source_url": "https://youtu.be/kwSVtQ7dziU",
"mime_type": "text/plain",
"pages": null,
"char_count": 18204,
"markdown": "Welcome back to the channel. Today we…",
"billed_amount_usd": 0.0005,
"status": "ready",
"error_message": null,
"created_at": "2026-05-22T14:00:01Z"
}
],
"count": 2,
"ready": 2,
"errored": 0,
"total_billed_usd": 0.0245
}
Each source is processed in parallel; one failing source does not abort the batch — it comes back with status: "error" and an error_message.
Errors
- 400 — No sources provided (need at least one of
files,youtube_urls,web_urls). - 413 — Combined upload exceeds the per-file 50 MB or the 10-file batch limit.
- 415 — Unsupported file format. Check the supported-extensions list above.
- 422 — Malformed
youtube_urls/web_urlsJSON. - 429 — Rate limit exceeded.