OpenAI Vector Stores (Retrieval API)
Status: DOCUMENTED · LIVE_VERIFIED (all 15 endpoints called 2026-09-18; file_batches/{id}/cancel on a completed batch → HTTP 500 = FAILED_VERIFICATION for that call only).
Sources: Retrieval guide · Vector stores reference · vector store files · file batches · File search tool guide · Pricing.
Last verified: 2026-09-18. Twins: endpoints fragment openai-files-vectorstores-batch-finetuning-evals.json, parameters openai-vector-stores.json, objects openai-platform-objects.json, lifecycles openai-lifecycles.json.
A vector store chunks, embeds and indexes Files. It backs (a) the standalone search endpoint documented here and (b) the hosted file_search tool of the Responses API (tools: [{type: "file_search", vector_store_ids: [...], max_num_results, filters, ranking_options}]) — the tool itself is owned by docs/tools/ (tools agent); this page covers the stores it consumes.
1. Endpoints
| Method / path | SDK (python) | Key params | 2026-09-18 |
|---|---|---|---|
POST /v1/vector_stores |
vector_stores.create |
name, description, file_ids[], chunking_strategy (only with file_ids), expires_after {anchor:"last_active_at", days 1–365}, metadata |
200, status: completed, usage_bytes 0 |
GET /v1/vector_stores |
.list |
limit 1–100, order, after, before |
200 |
GET /v1/vector_stores/{id} |
.retrieve |
200; 404 bogus id | |
POST /v1/vector_stores/{id} |
.update |
name, expires_after (nullable), metadata |
200 |
DELETE /v1/vector_stores/{id} |
.delete |
→ {object:"vector_store.deleted", deleted:true} |
200 |
POST /v1/vector_stores/{id}/search |
.search |
query (string or array), rewrite_query, max_num_results 1–50 (default 10), filters, ranking_options {ranker, score_threshold} |
200 |
POST /v1/vector_stores/{id}/files |
.files.create |
file_id, attributes, chunking_strategy |
200 → in_progress |
GET /v1/vector_stores/{id}/files |
.files.list |
filter in_progress/completed/failed/cancelled + cursors |
200 |
GET /v1/vector_stores/{id}/files/{file_id} |
.files.retrieve |
200 | |
POST /v1/vector_stores/{id}/files/{file_id} |
.files.update |
attributes (required; replaces the map) |
200 |
DELETE /v1/vector_stores/{id}/files/{file_id} |
.files.delete |
detaches (File itself remains) → vector_store.file.deleted |
200 |
GET /v1/vector_stores/{id}/files/{file_id}/content |
.files.content |
parsed chunks page | 200 |
POST /v1/vector_stores/{id}/file_batches |
.file_batches.create |
file_ids[] + global attributes/chunking_strategy or files[] {file_id, attributes?, chunking_strategy?} (mutually exclusive); ≤ 500 files |
200 → in_progress |
GET /v1/vector_stores/{id}/file_batches/{batch_id} |
.file_batches.retrieve |
200 | |
POST /v1/vector_stores/{id}/file_batches/{batch_id}/cancel |
.file_batches.cancel |
500 when already completed | |
GET /v1/vector_stores/{id}/file_batches/{batch_id}/files |
.file_batches.list_files |
filter + cursors |
200 |
Rate limits (documented): files + file_batches creation share 300 req/min per vector store; 2 000 attached files/min/org.
2. Chunking
| Strategy | Params | Constraints |
|---|---|---|
{"type":"auto"} (default) |
— | resolves to static 800 / 400 (observed: a file added with auto is returned with chunking_strategy.type = "static") |
{"type":"static","static":{"max_chunk_size_tokens","chunk_overlap_tokens"}} |
both required | max_chunk_size_tokens 100–4096 (default 800); chunk_overlap_tokens ≥ 0 and ≤ max/2 (default 400) |
response-only {"type":"other"} |
— | files indexed before chunking strategies existed |
Verified: static 100/20 accepted; a 200-byte file produced 1 chunk and usage_bytes 1203 (index overhead ≫ raw size — billing is on indexed bytes).
3. Attributes and filters
attributes = map of ≤ 16 keys, values string | number | boolean, key/string ≤ 256 chars. Update replaces the whole map (observed: {atlas, n, draft} → {atlas, n, lang}). Numbers come back as floats (1 → 1.0).
Comparison filter {"type": eq|ne|gt|gte|lt|lte|in|nin, "key": "<attr>", "value": string|number|boolean|array}; compound {"type": and|or, "filters": [...]} (nestable). The guide also shows "property": "filename" with in/nin (filter on filename) — UNVERIFIED. Live: and[eq atlas=platform, gte n=1] matched; eq atlas=nomatch → empty data.
4. Search response
{"object":"vector_store.search_results.page","search_query":["Woodchucks per passenger ratio"],
"data":[{"file_id":"file-…","filename":"….txt","score":0.977,"attributes":{"n":1.0,"atlas":"platform","draft":false},
"content":[{"type":"text","text":"…chunk…"}]}],"has_more":false,"next_page":null}search_query is always an array; with rewrite_query: true it holds the rewritten query (observed: question → keyword phrase). Array query (multi-query) is accepted and echoed. ranking_options.ranker ∈ none | auto | default-2024-11-15 (guide also mentions default-2024-08-21); score_threshold 0–1. The guide documents ranking_options.hybrid_search {embedding_weight, text_weight} (RRF weights) — not in the OpenAPI spec, UNVERIFIED.
5. Objects
- VectorStore
object:"vector_store":id(vs_…),name,description?,usage_bytes,file_counts {in_progress, completed, failed, cancelled, total},status(in_progress | completed | expired),expires_after,expires_at,last_active_at,metadata,created_at. - VectorStoreFile
object:"vector_store.file":id(= the File id),vector_store_id,usage_bytes,status(in_progress | completed | cancelled | failed),last_error {code: server_error|unsupported_file|invalid_file, message},chunking_strategy,attributes,created_at. - VectorStoreFileBatch: spec says
object:"vector_store.files_batch", live returns"vector_store.file_batch"(idprefixvsfb_ibj_…);status,file_counts,vector_store_id. - File content page
object:"vector_store.file_content.page":data[] {type:"text", text},has_more,next_page.
Lifecycles with observed timings (file: in_progress → completed in ~7.6 s; batch ~4 s) are in openai-lifecycles.json.
6. Expiration
expires_after {anchor: "last_active_at", days}: after days without activity the store becomes expired, its files are deleted and billing stops. Observed on create: expires_at = created_at + days·86400; last_active_at refreshed by search/file operations. Set expires_after: null on update to remove (nullable in spec).
7. Limits and pricing
| Item | Value |
|---|---|
| Max file size / tokens | 512 MB / 5 000 000 tokens per file |
| Files per batch request | 500 |
| Attributes | 16 keys × 256 chars |
| Search results | ≤ 50 per call |
| Storage price | first 1 GB free (across all stores), then $0.10 / GB / day (GB = 2^30 bytes) |
file_search tool calls (Responses only) |
$2.50 / 1 000 calls; direct /search calls are not separately priced on the pricing page (2026-09-18) |
8. Errors observed
| Call | HTTP | Message |
|---|---|---|
GET /vector_stores/vs_bogus |
404 | No vector store found with id 'vs_atlasbogus000'. (param: vector_store_id) |
POST …/file_batches/{completed}/cancel |
500 | The server had an error processing your request… |
9. Test recipe used (≈ $0, cleaned up)
create → files.create (static 100/20, attributes) → poll → list(filter) → content → search ×3 → files.update → update → file_batches.create(files[]) → poll → list_files → cancel(500) → delete files → delete store → leftover check (0 stores).