Docs
Get an API key
Workflows›Chat over your files

Chat over your files

Ask questions about your documents: upload them once and send them whole, or store them in a vector store and let the API find the passages that matter.

View .md

There are two ways, and the size of your documents decides between them.

Send the file Search a vector store
Fits A few documents that fit the model's context window More text than a context window holds
The model reads The whole document, every request Up to four passages that match the question
Billed The document's text as prompt tokens, every request The passages as prompt tokens

Send the file

Upload the document to POST /v1/files. It answers at once, and the text is read in the background:

Or error, with the reason in status_details. Once processed, send its file_id in a file part of any chat request, as often as you like; it is never read again.

Upload, wait, ask
# 1. Upload the document; it is converted to text once, in the background
printf '# Leave policy\n\nNew hires get 20 vacation days a year.\n' > policy.md
file_id=$(curl -s https://api.vultrinference.com/v1/files \
  -H "Authorization: Bearer $VULTR_INFERENCE_API_KEY" \
  -F file=@policy.md | jq -r .id)

# 2. Wait until it is processed (or error, with the reason in status_details)
while [ "$(curl -s https://api.vultrinference.com/v1/files/$file_id \
  -H "Authorization: Bearer $VULTR_INFERENCE_API_KEY" | jq -r .status)" = processing ]; do sleep 1; done

# 3. Ask about it by its id, in as many requests as you like
curl https://api.vultrinference.com/v1/chat/completions \
  -H "Authorization: Bearer $VULTR_INFERENCE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v4.1-flash",
    "messages": [{"role": "user", "content": [
      {"type": "text", "text": "How many vacation days do new hires get?"},
      {"type": "file", "file": {"file_id": "'"$file_id"'"}}
    ]}]
  }'

# 4. Delete it when you are done (otherwise it expires after 30 days)
curl -X DELETE https://api.vultrinference.com/v1/files/$file_id \
  -H "Authorization: Bearer $VULTR_INFERENCE_API_KEY"
  • A file may be up to 100 MB, a PDF up to 300 pages; a subscription holds up to 1,000 files.
  • A file expires 30 days after upload, and a request that names it after that is a 400 that says so. Delete it sooner with DELETE /v1/files/{id}.
  • The same file_id works in an input_file part on /v1/responses and a document part on /v1/messages (Documents and media).

Search a vector store

A vector store keeps your text as passages, each with an embedding, so the passages nearest a question can be found in a collection of any size.

  1. Make a collection with a name. Its id, which every other call takes, is the name lowercased, cut to 14 letters, digits and underscores.
  2. Add text as items, or upload whole files and let the API cut them into passages.
  3. Search with an input, for the 10 nearest passages, nearest first.
  4. Or ask the RAG route, which searches with the first user message and gives the model the four nearest passages before it answers.

An item holds up to 2,000 characters. Text longer than the embedding model reads at once is refused, unless you send auto_chunk: true to split it into overlapping passages. A file may be PDF, Word, Excel, PowerPoint, CSV, HTML, Markdown, AsciiDoc or plain text, and is enqueued, then processing, then complete or error.

Store, search, ask
import os
import secrets

import requests

API = "https://api.vultrinference.com/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['VULTR_INFERENCE_API_KEY']}"


def call(method: str, path: str, **kwargs) -> dict:
    response = session.request(method, API + path, timeout=300, **kwargs)
    response.raise_for_status()
    return response.json() if response.content else {}


# 1. A collection; its id is the name lowercased, letters, digits and _ only, 14 characters at most
collection = call("POST", "/vector_store", json={"name": "handbook-" + secrets.token_hex(2)})["collection"]["id"]

# 2. Add text (up to 2,000 characters an item), or upload whole files to /vector_store/{id}/files
for text in ["New hires get 20 vacation days a year.", "Expenses over $500 need a manager's approval."]:
    call("POST", f"/vector_store/{collection}/items", json={"content": text})

# 3. Search it yourself: the nearest items first, 10 at most
for result in call("POST", f"/vector_store/{collection}/search", json={"input": "time off"})["results"]:
    print("found:", result["content"])

# 4. Or let the chat route search it with the first user message and answer from what it finds
reply = call("POST", "/chat/completions/RAG", json={
    "model": "deepseek-v4.1-flash",
    "collection": collection,
    "messages": [{"role": "user", "content": "How many vacation days do new hires get?"}],
})
print(reply["choices"][0]["message"]["content"])

call("DELETE", f"/vector_store/{collection}")

The RAG route searches with the first user message, as plain text: send the question as a string, first. It does not read file parts or media; use the ordinary chat route for those.