Creating and Organizing Vector Stores

Ma Mahalakshmi V Updated 16 Sep 2026
7 min read ·Lesson 77 of 224

What a Vector Store Actually Is

A vector store is the hosted container that holds your documents in a form the file_search tool can search: chunked text, embeddings for each chunk, and any metadata you attach. Conceptually it plays the same role as an index in a search engine — you build it once (or update it incrementally), and then queries run against the index rather than against the raw documents.

It helps to be precise about what "vector" means here. Each chunk of text is converted into a list of floating-point numbers — an embedding — by an embedding model such as text-embedding-4. Two chunks whose embeddings are numerically close (by cosine similarity) are semantically close in meaning, even if they don't share many words. The vector store's job is to store these embeddings efficiently and answer "which stored vectors are closest to this query vector" quickly, even across millions of chunks.

You interact with a vector store through its ID (a string starting with vs_), and you attach one or more vector store IDs to a file_search tool call, as you saw in Lesson 1.

Creating a Vector Store

from openai import OpenAI

client = OpenAI()

vector_store = client.vector_stores.create(
    name="product-docs-v1",
)

print(vector_store.id)
print(vector_store.status)

Note: The exact resource name (client.vector_stores), method signatures, and default field values are specific to the SDK version in use. Confirm the current vector store API surface against official OpenAI documentation before relying on exact field names in production code.

What this does: it creates an empty vector store and returns an object describing it, including a unique id you'll use everywhere else — attaching files to it, referencing it in file_search tool calls, checking its processing status, and eventually deleting it. The name field is not used by retrieval logic at all; it exists purely so that you, as the developer, can recognize the store in logs, dashboards, or your own database. Giving it a meaningful, versioned name (like product-docs-v1 rather than store1) pays off the moment you have more than two or three stores, which happens quickly in any real project.

Immediately after creation, a vector store is empty and has no files. Adding files, discussed in depth in Lesson 3, is a separate step, and it is asynchronous — the files go through a processing pipeline (chunking and embedding) before they become searchable.

Why Organize Documents Into Separate Stores at All

You could, in principle, put every document your application will ever need into one giant vector store. In practice, this is almost always a mistake once you have more than one distinct topic, audience, or access boundary. There are three main reasons to split documents across multiple vector stores rather than using a single one:

  1. Relevance precision. A file_search call retrieves the top-K most similar chunks within the stores it's given. If a "billing FAQ" vector store also contains your engineering runbooks, an ambiguous query might surface an irrelevant engineering chunk purely because it's the least-bad match among a bad candidate set. Narrowing the searchable universe to only relevant documents improves precision more reliably than any downstream filtering.
  2. Access control and data boundaries. If different users, teams, or customers should only see certain documents, separate vector stores give you a clean, structural way to enforce that: a support agent's assistant is wired to the internal-only stores; a customer-facing assistant is wired only to public documentation stores. This is a stronger and simpler boundary than trying to filter a shared store by metadata for security purposes (metadata filtering, covered in Lesson 5, is best treated as a relevance tool, not a security control).
  3. Lifecycle management. Documents that update on different schedules (a rarely-changing legal policy versus a weekly-updated product changelog) are easier to refresh independently when they live in separate stores — you can rebuild or prune one without touching the other.

A Practical Organizing Pattern

A pattern that works well for most applications is to organize vector stores by domain and audience, not by document type or file format:

stores_to_create = [
    "support-kb-public",      # customer-facing help center content
    "support-kb-internal",    # internal-only troubleshooting docs
    "legal-policies",         # terms, privacy policy, compliance docs
    "product-release-notes",  # changelog-style, frequently updated
]

created = {}
for name in stores_to_create:
    store = client.vector_stores.create(name=name)
    created[name] = store.id

for name, store_id in created.items():
    print(f"{name}: {store_id}")

This example loops over a list of logical store names and creates one vector store per name, keeping a dictionary that maps the human-readable name to the store's actual ID. In a real application, you would persist this mapping — in a database, a config file, or environment variables — rather than recreating the stores on every run, because vector_stores.create always creates a new store; it does not look up an existing one by name. A common early mistake is calling this kind of setup code on every application startup, silently accumulating dozens of duplicate, mostly-empty vector stores over time.

Naming Conventions and Versioning

Because vector store names are just labels, adopting a consistent convention early prevents confusion later. A useful pattern is:

{domain}-{purpose}-v{version}

For example: support-kb-public-v3. The version suffix matters more than it looks: when you substantially change how a document set is chunked, cleaned, or curated (see Lesson 7), you often want to build a new vector store rather than mutating the old one in place, so you can compare retrieval quality side by side before cutting your application over. Keeping the old version around during that comparison, then deleting it once you've confirmed the new one performs better, is far safer than editing a live store your production traffic depends on.

Checking Vector Store Status

store = client.vector_stores.retrieve("vs_68f2a1c9e4b8...")

print(store.status)              # e.g. "completed", "in_progress"
print(store.file_counts.total)
print(store.file_counts.completed)
print(store.file_counts.failed)

Note: Field names such as file_counts and the exact set of status values are subject to change between API versions — verify them against current documentation.

This code retrieves the current state of a vector store, which matters because file ingestion is asynchronous. A store can exist and have an ID immediately, but its files may still be processing in the background. file_counts breaks down how many files have finished processing successfully versus failed, which is essential for catching silent ingestion failures — a malformed PDF that fails to parse, for instance, will show up in failed rather than raising an exception at upload time. Checking this after every batch upload, rather than assuming success, is the difference between finding out about a bad document during development versus finding out when a user asks about content that was never actually indexed.

Common Mistakes

Creating one vector store per file instead of grouping related files into a shared store, which happens when developers treat vector stores like individual document uploads — this defeats the purpose of file_search, since a file_search call is scoped to entire stores, and searching across dozens of single-file stores requires attaching all of them and loses any benefit of a curated, topic-scoped corpus.

Never checking file_counts.failed, which causes documents to silently never be searchable, because ingestion happens asynchronously and a failed parse does not throw an exception in your calling code — always poll or inspect status after uploading a batch.

Reusing one vector store across unrelated features indefinitely, which happens because it's the path of least resistance early in a project, but degrades retrieval precision over time as unrelated content accumulates and starts competing in similarity search.

Best Practices

Name every vector store with a domain, purpose, and version, so that six months into a project you can tell what a store contains without opening it and inspecting files one by one.

Treat vector store creation as infrastructure, not a runtime operation. Create stores through a setup script or migration, not inline in request-handling code, and persist their IDs in configuration rather than recreating them on every deploy.

Keep the number of stores attached to a single file_search call small and purposeful. Attaching every store you have "just in case" reduces the relevance of any individual retrieval; attach only the stores that are actually relevant to that assistant's scope.

0 Comments

Reviewed before they appear

No comments yet.

OpenAI SDK
Introduction to the OpenAI SDK Setting Up Python Creating an API Key Your First Call — client.responses.create() and response.output_text Understanding Billing, Credits, and What a Request Costs Why Responses Replaced Chat Completions Anatomy of a Request: model, input, and instructions Anatomy of a Response: The Typed output Array, Not Just Text Roles: User, Assistant, and Developer/System Choosing a Model, and Reading the Models Page Instead of Memorizing Names Instructions vs. Input Writing Prompts That Get Consistent Results Few-Shot Examples Reasoning Models and the reasoning Parameter Debugging a Prompt That Misbehaves Why Streaming Matters for User Experience stream=True and Iterating Over Events Handling the Event Types You Actually Care About Background Mode for Long-Running Jobs Project — Add Live Streaming to Your Chatbot The Problem With Parsing Free Text JSON Schema and Strict Mode Pydantic Models With the SDK's Parse Helpers Handling Refusals and Validation Failures Project — A Resume-to-JSON Extractor Working With input_image input_file, PDFs, and the Files API Image Generation Speech-to-Text and Text-to-Speech Project: A PDF Question-Answering Script What Function Calling Is Defining a Tool Schema The Full Loop Multiple Tools Errors, Timeouts, and Untrusted Arguments Project: A Weather Assistant Web Search File Search and Vector Stores Code Interpreter Remote MCP Servers and Connectors Project: A Research Assistant What an Embedding Is, Without the Maths Generating and Storing Embeddings Similarity Search From Scratch Hosted Vector Stores vs. Rolling Your Own A Small RAG App Over a Folder of Notes Agents vs. a Single API Call — When You Need One pip install openai Giving Agents Tools Handoffs and Multi-Agent Triage Guardrails and Approvals Tracing and Observing What Your Agent Did A Multi-Agent Support Desk Error Codes and What Each One Means Retries, Timeouts, and Backoff Rate Limits and Spend Limits Prompt Caching and Cost Optimisation The Batch API for Bulk Work Async Clients and Concurrency Moderation and Safety Best Practices Designing the App Backend With FastAPI Streaming to a Simple Frontend Deploying and a Cost/Safety Checklist Why Web Search Is Useful for Current Information Using the Web Search Tool with the Responses API Configuring Search Behavior for Application Use Cases Understanding Citations and Source Attribution Where to Go Next Building a Research Assistant with Web Search Combining Web Search with Structured Outputs Handling Conflicting or Low-Quality Web Sources Reducing Unsupported Claims with Grounded Generation Testing Freshness-Sensitive AI Answers Production Considerations for Web-Grounded Applications Understanding File Search and Retrieval-Augmented Generation Creating and Organizing Vector Stores Uploading Documents for Retrieval Connecting Vector Stores to Responses API Requests Designing Document Metadata and Filtering Strategies Building a PDF Question-Answering Application Improving Retrieval Quality With Better Document Preparation Handling Missing Evidence and Retrieval Failures Combining File Search With Web Search Building a Production Knowledge-Base Assistant What the Code Interpreter Tool Is Designed For Running Python-Based Analysis Through the OpenAI SDK Uploading Datasets for Analysis Analyzing CSV and Spreadsheet Data Generating Charts and Data Summaries Handling Generated Files and Downloadable Artifacts Building a Data-Analysis Assistant Combining Code Execution with Structured Outputs Validating Generated Calculations and Results Security and Sandbox Considerations for Code Execution Understanding Multimodal Input with the OpenAI SDK Sending Images to a Model Image Analysis from URLs and Uploaded Files Extracting Text and Information from Screenshots Building an Image-Question-Answering Application Combining Image Input with Structured Output Analyzing Multiple Images in One Request Handling Image Quality and Input Limitations Designing Multimodal Prompts for Reliable Results Building a Practical Vision-Powered Python Application Understanding Speech-to-Text and Text-to-Speech Workflows Transcribing Audio with the OpenAI SDK Working with Uploaded Audio Files Handling Timestamps and Transcription Metadata Building a Meeting Transcription Workflow Generating Spoken Responses from Text Handling Long Audio and Processing Failures Combining Audio with Text and Tool Calling Building an End-to-End Python Voice Application What Embeddings Are and When to Use Them Generating Embeddings With the OpenAI API Preparing Text for Embedding Comparing Vectors With Cosine Similarity Building a Simple Semantic Search Engine in Python Storing Embeddings in a Database Metadata Filtering for Semantic Search Chunking Strategies for Better Retrieval Evaluating Semantic Search Quality Building a Document Similarity Application When Batch Processing Makes Sense Designing Large-Volume AI Processing Pipelines Using Asynchronous Python with the OpenAI SDK Running Concurrent Requests Safely Controlling Concurrency and Avoiding Rate Limits Tracking Batch Job Progress Handling Partial Failures in Bulk Workloads Retrying Failed Items Without Duplicating Successful Work Designing Resumable AI Processing Jobs Building a Production Batch-Processing Pipeline Batch Processing Makes Sense Large-Scale AI Processing Pipelines Async Python with OpenAI SDK Safe Concurrent Requests Concurrency & Rate Limits Batch Progress Tracking Partial Failure Handling Safe Retry Handling Resumable AI Jobs Production Batch Pipeline System–User Data Separation Reusable App Instructions Prompt Templates & Variables Extraction & Classification Prompts Summarization & Transformation Prompts Explicit Output Requirements Prompt Version Management Prompt Testing & Evaluation Reusable Python Prompt Library API Key Security Secure API Key Storage Secure Secret Management Prompt Injection Prevention Trusted vs. Untrusted Content Tool Argument Validation Sensitive Data Handling Secure Logging AI Action Authorization Production AI Security Checklist Why AI Applications Need Evaluation Beyond Unit Tests Unit Testing OpenAI SDK Integration Code Mocking API Responses in Python Tests Testing Structured Outputs Against Schemas Testing Tool-Calling Workflows Building a Small Evaluation Dataset Measuring Accuracy, Consistency, and Failure Rates Regression Testing Prompts and Model Changes Human Evaluation Versus Automated Evaluation Creating a Repeatable Evaluation Pipeline AI Request Monitoring Token Cost Management Usage Metrics Design Reducing Model Calls Prompt & Context Optimization Model Selection & Optimization AI Caching Strategies Interactive Latency Optimization Usage Dashboards & Budget Alerts Performance & Cost Checklist Every API Call Starts Fresh Fixing API Statelessness Server-Side Conversation Memory Limits of Response Chaining What We're Building Conversation Memory Challenges Preparing an OpenAI SDK Application for Deployment Environment-Specific Configuration for Development and Production Deploying a Python AI Service with Docker Container Health Checks and Startup Configuration Managing Secrets in Cloud Deployments Background Workers for Long-Running AI Tasks Queues and Asynchronous Job Architectures Scaling AI Workloads Horizontally Monitoring Production Incidents and Failures Production Deployment Checklist for OpenAI SDK Applications Reusable OpenAI Service Classes AI Client Dependency Injection Typed AI Responses Python Configuration Management AI Request Decorators Centralized AI Error Handling Clean SDK Abstractions Reusable OpenAI Utilities Internal AI Python Libraries SDK Integration Maintenance Production AI Chatbot Document Q&A System Web Research Assistant Customer Support Agent AI Data Analysis Assistant Image Analysis App Meeting Transcription & Summary Semantic Document Search Multi-Tool AI Agent Production OpenAI SDK App Why "It Looked Fine When I Tested It" Isn't Enough Timing Note Status Note Pre-Decision Status Note Current Availability Note
Ask about this post
AI Ask about this post

Ask questions about Creating and Organizing Vector Stores and get answers drawn from it.

Signed-in readers only.