RCRAG Converter

The API

Turn your documents, images, audio and video into a corpus your application can retrieve from - one endpoint, one file out.

What it does

You POST files. You get back JSONL: one JSON object per line, each carrying a passage of text, its embedding and the metadata needed to trace it back to the file and the moment it came from. Nearly every vector database imports newline-delimited JSON, which makes it the working standard for moving a corpus between systems.

It is deliberately one endpoint. Conversion is the expensive step and the one you run least often - on upload, on a schedule, or when a source system signals a change. Retrieval is what sits in your request path, and that runs against your own vector store, not against us.

One call

curl -X POST https://api.ragconverter.com/v1/convert \
  -H "Authorization: Bearer $RAGCONVERTER_KEY" \
  -F "file=@handbook.pdf" \
  -F "file=@keynote.mp4" \
  -o chunks.jsonl

The manifest - what each file produced, and how its text was obtained - comes back in the x-ragconverter-manifest header. Keys are minted from your account page after signing in.

Reading order, and where a passage came from

Lines come back in reading order, and say so rather than leaving you to assume it. Sort by metadata.index to recover the whole pack, or by metadata.seq within one document. Both are dense and ascending from zero, and the manifest's ordering block states that as a promise - a vector database is under no obligation to hand rows back in the order you inserted them, and most do not.

{
  "id": "chunk-...",
  "text": "Invoices are issued monthly and due in 30 days.",
  "embedding": [...],
  "metadata": {
    "source": "handbook.docx",
    "index": 41, "seq": 12, "page": 3,
    "kind": "text",
    "heading_path": ["Billing", "Invoices"],
    "section_title": "Invoices",
    "char_start": 8214, "char_end": 8492,
    "sensitivity": 0
  }
}

kind is text for prose and list, heading, table, image or equation for everything else. char_start and char_end are CHARACTER offsets into the document - what you index with in Python or Go - and both are zero when a chunk is not a span of the source, as a figure's caption is not.

Chunks, or one row per file

The default is one row per passage. Send format=documents instead and you get one row per FILE, holding the whole document as ordered markdown with its headings and lists intact - the shape a knowledge base that stores a row per document wants. format=markdown returns the same content as one readable file.

curl -X POST https://api.ragconverter.com/v1/convert \
  -H "Authorization: Bearer $RAGCONVERTER_KEY" \
  -F "format=documents" \
  -F "file=@handbook.docx" \
  -o documents.jsonl

Document rows carry an empty embedding on purpose: a whole document does not fit a sentence-transformer's 512 positions, so a vector here would silently encode the first few paragraphs and call that the document. You embed it, or your knowledge base does.

A flag on what you might not want to index

Every passage is scanned for email addresses, telephone numbers, card numbers that pass a Luhn check, credentials with a vendor's own prefix, and the phrases an author writes when they mean it - confidential, do not distribute. What was found travels in metadata.sensitivity (0-100) and metadata.sensitive, and the manifest says how many of each file's chunks matched.

It flags and never drops. A false positive that silently removed a passage would leave the document quietly incomplete and the answer built from it confidently wrong, which is worse than the noise it would have saved. Zero means nothing matched, not that a passage is safe to publish.

Errors

Every failure is a JSON body of the shape {"error": "..."} with a status that says what to do about it: 401 when we do not know who you are, 403 when we do and the key lacks the capability, 400 for a request we cannot read, 402 when the balance is spent, and 429 when retrying later is the right move. A 500 is logged in full our side and returned in outline.

Three engines on a video, and paying for fewer

A video carries three separate things worth indexing, and they are read by three different models: Carmenta takes the text on the frames, Argus describes what those frames show, and Mercury transcribes the audio track. Each becomes its own passage, timestamped, so a query lands on the minute that answers it.

You do not have to buy all three. Name the ones you want and the rest are never run - not filtered out afterwards, never started. Asking for the speech alone out of a long recording is one pass instead of several hundred, and the bill follows the work.

# just the transcript
    curl -X POST https://api.ragconverter.com/v1/convert   -H "Authorization: Bearer $RAGCONVERTER_KEY"   -F "engines=asr"   -F "file=@keynote.mp4"   -o transcript.jsonl

# slides and speech, but no descriptions
      -F "engines=ocr,asr"

Omit the field and you get all three. An unrecognised name is an error rather than a silent fallback to everything - a typo that quietly widened the request would be billed as three engines and would never show up as a mistake.

About Argus

Argus is the one that makes a picture searchable when there is no text to read. A photograph, a diagram, a frame of a screen recording - OCR returns nothing from any of them, and the file is either described or it is not in your index at all. That is what it is for, and it is the reason a video is worth more than its transcript.

It is still the expensive one - a description is a full forward pass, which is why video samples it ten times more sparsely than it reads text - but it is no longer the expensive one it was. The current build encodes an image as a single tile and 64 image tokens rather than seventeen tiles and 1088: 37.6 seconds against 544 on the same photograph, measured by the engine's own authors.

What that drops is fine print and small objects, not the subject. Which is the right trade here precisely because Carmenta has already read the text off the same frames a moment earlier - paying nine minutes for a second, worse reading of words we already have properly would be spending the whole budget on the one thing this engine is not for.

What it reads

Text and Markdown, PDF, Word, PowerPoint and Excel are parsed directly. Images and scanned PDFs go through OCR at full resolution - no browser memory ceiling, which is the main reason this tier exists. Audio is transcribed. Video is taken apart into three tracks: the text on screen, what the frames show, and what is being said, each timestamped so they line up.

The exact list, because a client that validates before uploading should not have to guess it: .txt .md .markdown .json .csv .tsv .html .htm .xml .pdf .doc .docx .xls .xlsx .pptx .jpg .jpeg .png .gif .webp .bmp .tif .tiff .wav .flac .mp3 .m4a .aac .ogg .opus .mp4 .mov .m4v .avi .mkv .webm. Anything else is a 400 naming the file, and it is refused BEFORE the upload is measured or billed - so an unreadable type costs you nothing. The check is on the extension, so a file whose bytes do not match its name gets past this gate and is refused by the parser instead, which says what the bytes actually looked like.

The pre-2007 Office formats

.doc and .xls are read, and they are a genuinely different format from .docx and .xlsx rather than an older version of one - OLE2 compound files holding binary records, not zipped XML. They produce the SAME shape of output as their modern equivalents, deliberately: one passage per paragraph for a document, one table passage per sheet for a workbook. Nothing downstream has to know which of the two a corpus came from.

Two details worth knowing if you are diffing output against another tool. Field results are kept and field CODES are dropped, so a hyperlink contributes the text a reader sees rather than the instruction HYPERLINK "http://..." that produced it. And the 0x80-0x9F byte range is decoded as Windows-1252 rather than Latin-1, which is why curly quotes, em dashes and ellipses come through as themselves instead of replacement characters.

.ppt is not read. It is refused by name, not silently half-parsed.

TIFF

.tif and .tiff are treated as images: full-resolution OCR, the same path as a JPEG or a PNG. Multi-sample and high-bit-depth scans are narrowed to eight bits rather than refused, so a 16-bit archival scan converts instead of failing. This is a format the browser tier cannot do at all, so the API is the only place it works.

Each passage records HOW its text was obtained - parsed, read by OCR, heard by a speech model, or described by a vision model. That distinction matters when your application cites a source: a transcription is a record of words that were really there, and a description is inference that can be confidently wrong. An application that presents both as quotations will eventually quote something nobody said.

What people build with it

Support that answers from your docs

A support assistant that has read your manuals, release notes and past tickets answers the eighty percent of questions that are already documented, and cites the page it used. The value is not the deflection rate on its own - it is that the answer is traceable, so a wrong one can be found and fixed at the source.

Search that understands the question

Keyword search fails when the user's words are not the document's words. Someone asking "can I get my money back" never matches a page headed "Refunds and Returns" on keywords, and matches it immediately on meaning. Retrieval over embeddings is the difference.

Onboarding and internal knowledge

New staff ask the questions already answered in a wiki nobody can navigate. A retrieval layer over the same material turns it from a filing system into something answerable, without anyone rewriting it.

Contracts, policies and compliance review

Finding every clause across hundreds of agreements that touches liability, notice periods or data residency is a retrieval problem before it is a reasoning one. Conversion is what makes those clauses findable - including the ones inside scanned PDFs, which is where they usually hide.

Recorded meetings and calls

An hour of audio is unsearchable until it is a transcript with timings. Once it is, "what did we agree about the renewal" becomes a query rather than an afternoon of scrubbing.

Video, training material and screen recordings

A recorded walkthrough carries three separate things worth indexing: the text on screen, what the frames show, and what the presenter said. Read apart and timestamped, a question lands on the minute that answers it.

What it is worth

The value of retrieval is not that the model sounds better. It is that the answer is grounded in a passage you can point at. A model without retrieval answers your customer's question about your refund policy from its training data, fluently and wrongly, and neither you nor the customer can tell. With retrieval the answer comes from your document, and when it is wrong, the document is wrong - which is a problem you can actually fix.

The other half is reach. Most of an organisation's useful material is not in a database: it is in PDFs, decks, scans, recordings and video. Conversion is what makes that material addressable at all, and it is usually the difference between a retrieval feature that covers a quarter of what people ask about and one that covers most of it.

What is not built yet

Conversion is stateless: you send files and receive a corpus, and we keep nothing. A stored corpus and a hosted query endpoint are planned and are not built, so retrieval today happens in your own vector store. PowerPoint's pre-2007 binary format (.ppt) is not read - the record tree is a much larger job than the Word and Excel equivalents, and it is refused by name rather than half-read.

This list is not marketing modesty. GET /health reports the same status for every capability on this page, from the running instance, so you can check rather than trust.

Scripts, pipelines, and agents

The HTTP API is the surface everything else calls. A curl one-liner, a Python script, a nightly CI job - all POST the same multipart body and receive the same JSONL the browser download produces. GET /health needs no key and reports what this Fly instance can do right now (OCR, speech, semantic embeddings).

Agents in Cursor or Claude Desktop can use the optional MCP server (npm install -g ragconverter-mcp): four tools that wrap /health, balance, usage, and /v1/convert. MCP does not sign in or pay - a human tops up prepaid credit and puts an API key in the server env once. For the full curl reference and video engine options, read the API guide.

Free, and private, in the browser

If your material cannot leave the machine it is on - identity documents, medical records, anything under an NDA or a data residency clause - the browser converter does the same job on your own hardware and uploads nothing. Same output format, no account. The API is for volume and for the larger models, not for the sensitive cases.