File search

Retrieval over documents your customer uploaded, not the public corpora — a store, a few files, and a tool the model calls when a question needs them.

Three pieces. A vector store is a named bucket you create. Files you upload into it are chunked and embedded automatically — you never touch an embedding yourself. And file_search is a built-in tool: name your store on the request, and the model decides mid-answer when a question needs to search it.

What this costs
Metered two ways: storage, per GB-month, for what a store holds, and a fee per query each time the model actually calls file_search. Pricing is set by your platform operator and may not be published yet — check Pricing.

Create a store

POST/v1/vector_stores
ParameterTypeDescription
name
Required
stringA name for the store — yours to choose, shown back on every read.
owner
Optional
stringA label for your own bookkeeping. Stored and returned as-is; nothing here enforces access with it.
Response
{
  "id": "vs_9a2f1e7c3b0d5a41e6f2",
  "name": "vendor-contracts",
  "object": "vector_store"
}

Upload files into it

POST/v1/files

Sent as multipart/form-data, not JSON — two fields, file and vector_store_id.

Text is extracted, not merely stored: PDF (its text layer only — a scanned page with nothing selectable extracts nothing), Word .docx, and anything else is read as plain UTF-8 text, which is exactly right for .txt, .md, .csv, .json and source code, and exactly wrong for a legacy .doc, .xlsx or .pptx — those are binary formats that will decode into noise, not an error, and quietly pollute the store. Convert those to PDF or plain text first.
Response
{
  "id": "file_2c88a1f0d94b7e315a02",
  "filename": "contract.pdf",
  "bytes": 84213,
  "chunks": 41,
  "vector_store_id": "vs_9a2f1e7c3b0d5a41e6f2"
}

Each file is split into roughly 900-character passages with 150 characters of overlap at every boundary, cut on paragraph breaks where it can — so a sentence that starts in one chunk and finishes in the next still retrieves for either half.

Create and upload, together

# Create the store.
STORE=$(curl https://api.data.larsima.com/v1/vector_stores \
  -H "Authorization: Bearer $DATA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "vendor-contracts"}')
echo "$STORE"
STORE_ID=$(echo "$STORE" | python3 -c 'import json, sys; print(json.load(sys.stdin)["id"])')

# Upload a file into it. Multipart, not JSON.
curl https://api.data.larsima.com/v1/files \
  -H "Authorization: Bearer $DATA_API_KEY" \
  -F "vector_store_id=$STORE_ID" \
  -F "file=@contract.pdf"

Search it from a chat completion

tools
[{ "type": "file_search", "vector_store_ids": ["vs_9a2f1e7c3b0d5a41e6f2"] }]
The store ids you put on the request always win — even if the model tries to pass different ones as arguments, yours overwrite them before the search runs, so it can never search a store you did not authorize. data-auto and data-general execute file_search (the law models do not); data-auto is the model every example on this page uses.
curl https://api.data.larsima.com/v1/chat/completions \
  -H "Authorization: Bearer $DATA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "data-auto",
    "messages": [{"role": "user", "content":
      "What is the termination notice period in the vendor contract? Name which file it came from."}],
    "tools": [{"type": "file_search",
      "vector_store_ids": ["vs_9a2f1e7c3b0d5a41e6f2"]}]
  }'

Search it from the Responses API

POST /v1/responses takes the same tool, written the OpenAI Responses way, and answers with a proper response object: a file_search_call item recording the query, then the answer as an output_text message whose annotations carry a file_citation (file_id and filename) for each file the search returned. response.output_text in the OpenAI SDK works as usual. Add "include": ["file_search_call.results"] to also get the matched passages back on the file_search_call. stream: true is supported; the answer is produced first and then delivered as the standard event sequence, so expect it to arrive after the search and the model have finished, not token by token.

Every id in vector_store_ids must be a store your organization owns. An unknown id returns 404 with code vector_store_not_found, and a file_search with no vector_store_ids returns 400 — the request is refused rather than answered from the model's memory.
curl https://api.data.larsima.com/v1/responses \
  -H "Authorization: Bearer $DATA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "data-auto",
    "input": "What is the termination notice period in the vendor contract? Name which file it came from.",
    "tools": [{"type": "file_search",
      "vector_store_ids": ["vs_9a2f1e7c3b0d5a41e6f2"]}]
  }'

Citing what comes back

Each hit the model sees carries a filename, a similarity score, and the matched passage — nothing tracks page or paragraph numbers, because extraction does not keep them. Cite by filename: ask the model, in your system prompt, to name the source file for every fact it pulls from a search.

FieldTypeMeaning
filenamestringThe uploaded file the passage came from.
scorenumberCosine similarity to the query, 0 to 1 — higher is closer.
textstringThe matched passage itself, up to roughly 900 characters.

List and delete a store

GET/v1/vector_stores
DELETE/v1/vector_stores/{id}

Deleting a store deletes its files and their chunks with it — there is no separate step.

curl https://api.data.larsima.com/v1/vector_stores \
  -H "Authorization: Bearer $DATA_API_KEY"

curl -X DELETE https://api.data.larsima.com/v1/vector_stores/vs_9a2f1e7c3b0d5a41e6f2 \
  -H "Authorization: Bearer $DATA_API_KEY"
↑↓ Navigate ↵ Open esc Close