Skip to main content
Use Files to upload documents once. Use Vector Stores to index those files for semantic search and retrieval. This is the standard pattern for document-backed assistants, retrieval workflows, and grounded responses.

Upload a file

Upload the file with multipart/form-data. The live OpenAPI spec requires file and purpose. Note: multipart file parts must include a nonblank filename. The lowercase WHATWG placeholder filename "blob" is treated as missing.
The response returns a file object with an ID such as file-abc123.

Create a vector store

Create a vector store and attach one or more uploaded file IDs.
This response returns a vector store ID such as vs_abc123.

Create a graph store

Set retrieval_mode to "graph" to get graph-aware retrieval instead of plain vector similarity. On a graph store, entities and relations are extracted from every chunk at ingest to build a knowledge graph, and search traverses that graph to collect connected evidence rather than returning the nearest chunks alone. The gains show up on questions that require linking facts across several documents. Two options apply only to graph stores:
  • extraction_model — the model used for entity and relation extraction. It follows the same contract as embedding_model: optional, defaults to auto, and resolved to a concrete model at creation.
  • max_hops — how far to expand through the graph on each query, from 1 to 4. The default is 2.
The response echoes retrieval_mode, extraction_model, and max_hops back, with extraction_model resolved to the concrete model rather than auto. You search a graph store with the same search call as any other vector store — the mode is a property of the store, not of the request.

What changes on a graph store

  • The mode is frozen at creation. There is no way to convert a store between vector and graph afterward; the update endpoint does not accept retrieval_mode. Switching means creating a new store and re-attaching the files.
  • Extraction is metered against your usage. Entities and relations are extracted from every chunk at ingest, and each query also runs an extraction pass to identify the entities to expand from. Both are billed as normal model usage, so a graph store costs more to fill and more to query than a vector store over the same files.
  • Graph search returns at most 20 results. max_num_results accepts up to 50, but graph queries clamp to 20. A higher value is truncated rather than rejected.
  • Attribute filters can under-fill. As noted under Filter search by file attributes, filtering on a graph store is partly applied after retrieval, so a filtered search can return fewer than max_num_results matches.
  • extraction_model and max_hops are graph-only. Sending either without retrieval_mode: "graph" returns 400.

Add more files later

You can add more files to an existing vector store without recreating it.
The vector store file can return status: "in_progress" while indexing runs. A file is not searchable until its status reaches "completed" — searching before then succeeds but omits the file, so poll for "completed" rather than assuming a fixed wait. Indexing usually finishes in seconds, but latency varies with file size and load. Check the Files and Vector Stores endpoints in the API Reference for the polling endpoint.

List files with pagination

File listings are sorted by created_at (newest first by default) and return up to limit items per page. When has_more is true, pass the last item’s id as after to fetch the next page.
To page backwards, pass an item’s id as before instead: the response is the page immediately preceding that item in the display order. Files attached in the same batch can share a creation timestamp; the cursor accounts for this, so a full walk returns every file exactly once. If a cursor’s file no longer exists — for example, it was removed from the vector store while you were paging — the API returns 400 Invalid pagination cursor. Restart the walk from the first page.

Search the vector store

Use semantic search to retrieve the most relevant chunks for a user question.
The response returns ranked matches with file_id, filename, score data, chunk content, and the file’s current attributes.

Filter search by file attributes

Attributes are file-level key-value metadata — string, number, or boolean values — set when you attach a file (see Add more files later). You can change them after ingest with the update-file endpoint (POST /vector_stores/{vector_store_id}/files/{file_id}); search always evaluates the current values. Pass filters in the search request to restrict results to files whose attributes match. A filter is either a comparison — eq, ne, gt, gte, lt, lte, in, nin — or an and/or compound of nested filters.
Filter evaluation follows these rules:
  • A file that does not have the filter’s key never matches — including for ne and nin. A filter only opts a file in on evidence, never by absence.
  • eq and ne are strict equality with no type coercion: the string "2" does not equal the number 2.
  • gt, gte, lt, and lte compare numbers only; a non-numeric attribute or filter value fails the comparison.
  • in and nin take an array value and test whether the file’s attribute is (or is not) one of its elements.
  • and and or compounds nest to any depth.
  • On graph stores, filtering is applied after retrieval, so a filtered search can return fewer than max_num_results matches.

Typical workflow

Use this sequence for most retrieval setups:
  1. Upload the source file.
  2. Create a vector store with file_ids, or attach the file later.
  3. Wait for file processing to complete.
  4. Search the vector store when you need relevant context.
You can then feed the returned text into your own application logic or a Responses request.