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.
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.
# 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"import os
import time
from openai import OpenAI
client = OpenAI(base_url="https://api.vultrinference.com/v1", api_key=os.environ["VULTR_INFERENCE_API_KEY"])
# 1. Upload the document; it is converted to text once, in the background
uploaded = client.files.create(file=("policy.md", b"# Leave policy\n\nNew hires get 20 vacation days a year.\n"), purpose="user_data")
# 2. Wait until it is processed (or error, with the reason in status_details)
while (file := client.files.retrieve(uploaded.id)).status == "processing":
time.sleep(1)
if file.status != "processed":
raise SystemExit(f"{file.id}: {file.status_details}")
# 3. Ask about it by its id, in as many requests as you like
reply = client.chat.completions.create(
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}},
],
}],
)
print(reply.choices[0].message.content)
# 4. Delete it when you are done (otherwise it expires after 30 days)
client.files.delete(file.id)- 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_idworks in aninput_filepart on/v1/responsesand adocumentpart 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.
- Make a collection with a
name. Itsid, which every other call takes, is the name lowercased, cut to 14 letters, digits and underscores. - Add text as items, or upload whole files and let the API cut them into passages.
- Search with an
input, for the 10 nearest passages, nearest first. - Or ask the RAG route, which searches with the first user message and gives the model the four nearest passages before it answers.
| Step | Route |
|---|---|
| Make a collection | POST /v1/vector_store |
| Add text | POST /v1/vector_store/{id}/items |
| Add a file | POST /v1/vector_store/{id}/files |
| Search | POST /v1/vector_store/{id}/search |
| Ask | POST /v1/chat/completions/RAG |
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.
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.
Endpoints
/v1/files
Upload File
GET/v1/files/{id}
Get File
DEL/v1/files/{id}
Delete File
POST/v1/vector_store
Create Collection
POST/v1/vector_store/{id}/items
Add Collection Item
POST/v1/vector_store/{id}/files
Add Collection File
POST/v1/vector_store/{id}/search
Search Collection
POST/v1/chat/completions/RAG
RAG Chat Completion