Playground ↗ Postman ↗ Dashboard ↗
POST /v2/documents/extract

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

HeaderRequiredNotes
X-API-KeyyesYour test or production key
X-External-User-Idyes (or X-User-Id)Identifies the learner
Content-Typeyesmultipart/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 typeCost
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_urls JSON.
  • 429 — Rate limit exceeded.