Documents to Markdown

SpiderMail can turn a document into markdown an AI agent can actually read. Send a PDF, Word document, spreadsheet, slide deck, CSV or image and get structured markdown back — including text pulled out of scanned pages by OCR.

This runs on the same worker that sends your mail, which is the only one in the fleet carrying the full document toolchain.

Two different conversions. This page is about files, and it is asynchronous — you get a job back and poll it. To convert a string between markdown and HTML, that is a separate, instant call: see Markdown ↔ HTML.

What converts

::table
Format | Notes
PDF | Text layer extracted; scanned pages fall back to OCR
DOCX | Word documents
XLSX | Spreadsheets
PPTX | Slide decks
CSV | Tabular data
Images | PNG / JPG etc. — OCR only, since there is no text layer

Archives, audio and video are refused rather than attempted. Extract an archive yourself and convert its documents individually.

Convert a document

Submit the file, then poll for the result.

# 1. Submit — returns a job_id, NOT the markdown
curl -X POST "https://spideriq.ai/api/v1/jobs/spiderConvert/submit" \
  -H "Authorization: Bearer $SPIDERIQ_PAT" \
  -H "Content-Type: application/json" \
  -d '{"payload": {"content_base64": "'"$(base64 -w0 report.pdf)"'", "filename": "report.pdf"}}'

# 2. Poll until status is completed
curl "https://spideriq.ai/api/v1/jobs/JOB_ID/results?format=yaml" \
  -H "Authorization: Bearer $SPIDERIQ_PAT"

From the CLI it is one command, which polls for you:

spideriq convert ./report.pdf              # prints the markdown
spideriq convert ./report.pdf --preview    # cheaper: first 1500 characters
spideriq convert ./big.pdf --no-wait       # prints the job_id instead

Getting the file to us

::table
Size | How | Why
Up to 10 MB | content_base64 inline | One call, no upload step
Over 10 MB | Upload to SpiderMedia first, pass media_id | The jobs endpoint sits behind a 10 MB limit; the media route allows 100 MB

Previews are the default, on purpose

full_text defaults to false, so you get a 1500-character preview. That is usually enough to answer "is this an invoice?" or "which vendor is this from?" without moving an entire document across the wire.

Ask for full_text: true only when you genuinely need the whole body.

The file type is detected, not trusted

SpiderMail decides what a document is from its magic bytes, not its filename. filename and mime_type are advisory:

  • A mislabelled file still converts correctly.

  • A .pdf that is really a PNG is reported as an image.

Read source_format on the result to learn what the document actually was.

Reading the result

::table
Field | Meaning
markdown | The converted body
preview | First 1500 characters
pages | Page count
source_format | What the magic bytes said it was
size_original / size_markdown | Byte sizes before and after
truncated | The body is incomplete — see below
security | Whether the extracted text looks like instructions

truncated: true has two different causes

Either you asked for a preview (full_text was false), or the extraction was large enough to spill to storage. Check whether storage_key is set to tell them apart — do not assume the document was cut off mid-sentence.

Check security before feeding text to a model

If security.safe is false, the extracted text contains instruction-like content. A PDF can carry "ignore all previous instructions" in its text layer just as easily as an email body can.

The text is returned unmodified — SpiderMail tells you rather than silently editing your document. If you are about to paste it into a model prompt, that flag is the entire reason it exists.

OCR control

::table
ocr | Behaviour
auto (default) | OCR scanned pages when there is no text layer
never | Skip OCR entirely. Much cheaper on a large scanned corpus, but errors on images, which have no text layer at all
force | Accepted, but behaves as auto — the extractor decides internally

Known limit

HTML and plain-text input currently come back as their raw source, labelled as markdown — they are passed through rather than converted. If you have an HTML string, use Markdown ↔ HTML instead, which is built for exactly that.