Pinecone Managed Vector Database: Setup, Operations, and Integrations
Managed vector DB for production RAG and search.
Written by Neura Market from the official Hermes Agent documentation for Pinecone. Commands, paths, and version numbers are reproduced from the source unchanged.
Read the official documentationPinecone is a fully managed, serverless vector database built for production AI workloads. You reach for it when you need low-latency similarity search at scale without running your own infrastructure. This guide covers installation, core operations, hybrid search, and integrations with LangChain and LlamaIndex.
What it does
Pinecone stores and retrieves vector embeddings alongside metadata. It handles auto-scaling, replication, and indexing so you can focus on building retrieval-augmented generation (RAG) pipelines, recommendation systems, or semantic search. The service guarantees p95 latency under 100ms and a 99.9% uptime SLA. You interact with it through a Python SDK that supports upsert, query, metadata filtering, and namespace partitioning.
Before you start
You need a Pinecone account and an API key from the Pinecone console. The SDK runs on Linux, macOS, and Windows. Install the current package (v5+; the old pinecone-client is deprecated):
pip install pinecone
Note: the old
pinecone-clientpackage is deprecated. Installpinecone(v5+; current 9.x). The import staysfrom pinecone import Pinecone.
Quick start
Initialize the client, create a serverless index, upsert a few vectors, and run a query.
from pinecone import Pinecone, ServerlessSpec
# Initialize
pc = Pinecone(api_key="your-api-key")
# Create index
pc.create_index(
name="my-index",
dimension=1536, # Must match embedding dimension
metric="cosine", # or "euclidean", "dotproduct"
spec=ServerlessSpec(cloud="aws", region="us-east-1")
)
# Connect to index
index = pc.Index("my-index")
# Upsert vectors
index.upsert(vectors=[
{"id": "vec1", "values": [0.1, 0.2, ...], "metadata": {"category": "A"}},
{"id": "vec2", "values": [0.3, 0.4, ...], "metadata": {"category": "B"}}
])
# Query
results = index.query(
vector=[0.1, 0.2, ...],
top_k=5,
include_metadata=True
)
print(results["matches"])
Core operations
Create index
You can create a serverless index (recommended for most use cases) or a pod-based index for consistent performance under predictable workloads.
# Serverless (recommended)
pc.create_index(
name="my-index",
dimension=1536,
metric="cosine",
spec=ServerlessSpec(
cloud="aws", # or "gcp", "azure"
region="us-east-1"
)
)
# Pod-based (for consistent performance)
from pinecone import PodSpec
pc.create_index(
name="my-index",
dimension=1536,
metric="cosine",
spec=PodSpec(
environment="us-east1-gcp",
pod_type="p1.x1"
)
)
Upsert vectors
Upsert adds or updates vectors. Batch upserts are more efficient than single ones.
# Single upsert
index.upsert(vectors=[
{
"id": "doc1",
"values": [0.1, 0.2, ...], # 1536 dimensions
"metadata": {
"text": "Document content",
"category": "tutorial",
"timestamp": "2025-01-01"
}
}
])
# Batch upsert (recommended)
vectors = [
{"id": f"vec{i}", "values": embedding, "metadata": metadata}
for i, (embedding, metadata) in enumerate(zip(embeddings, metadatas))
]
index.upsert(vectors=vectors, batch_size=100)
Query vectors
Query returns the nearest neighbors. You can filter by metadata, scope to a namespace, and control which fields are returned.
# Basic query
results = index.query(
vector=[0.1, 0.2, ...],
top_k=10,
include_metadata=True,
include_values=False
)
# With metadata filtering
results = index.query(
vector=[0.1, 0.2, ...],
top_k=5,
filter={"category": {"$eq": "tutorial"}}
)
# Namespace query
results = index.query(
vector=[0.1, 0.2, ...],
top_k=5,
namespace="production"
)
# Access results
for match in results["matches"]:
print(f"ID: {match['id']}")
print(f"Score: {match['score']}")
print(f"Metadata: {match['metadata']}")
Metadata filtering
Filters narrow results before similarity scoring. Supported operators include exact match, comparison, logical, and $in.
# Exact match
filter = {"category": "tutorial"}
# Comparison
filter = {"price": {"$gte": 100}} # $gt, $gte, $lt, $lte, $ne
# Logical operators
filter = {
"$and": [
{"category": "tutorial"},
{"difficulty": {"$lte": 3}}
]
} # Also: $or
# In operator
filter = {"tags": {"$in": ["python", "ml"]}}
Namespaces
Namespaces let you partition data within a single index, useful for multi-tenant setups or separating environments.
# Partition data by namespace
index.upsert(
vectors=[{"id": "vec1", "values": [...]}],
namespace="user-123"
)
# Query specific namespace
results = index.query(
vector=[...],
namespace="user-123",
top_k=5
)
# List namespaces
stats = index.describe_index_stats()
print(stats['namespaces'])
Hybrid search (dense + sparse)
Hybrid search combines dense embeddings with sparse vectors (e.g., TF-IDF) for better retrieval quality. Note that index.query() does not accept an alpha parameter; you must scale the query vectors yourself before sending them.
# Upsert with sparse vectors
index.upsert(vectors=[
{
"id": "doc1",
"values": [0.1, 0.2, ...], # Dense vector
"sparse_values": {
"indices": [10, 45, 123], # Token IDs
"values": [0.5, 0.3, 0.8] # TF-IDF scores
},
"metadata": {"text": "..."}
}
])
# Hybrid query
# NOTE: index.query() does NOT accept an `alpha` kwarg. Pinecone stores a
# single sparse-dense vector, so weighting must be applied by pre-scaling the
# query vectors before sending them. Use the hybrid_score_norm helper below
# (alpha * dense + (1 - alpha) * sparse; alpha=1 → pure dense, 0 → pure sparse).
def hybrid_score_norm(dense, sparse, alpha: float):
"""Scale dense/sparse query vectors for weighted hybrid search."""
if not 0 <= alpha <= 1:
raise ValueError("alpha must be between 0 and 1")
scaled_sparse = {
"indices": sparse["indices"],
"values": [v * (1 - alpha) for v in sparse["values"]],
}
return [v * alpha for v in dense], scaled_sparse
hdense, hsparse = hybrid_score_norm(
dense=[0.1, 0.2, ...],
sparse={"indices": [10, 45], "values": [0.5, 0.3]},
alpha=0.5, # 0=sparse, 1=dense, 0.5=balanced
)
results = index.query(
vector=hdense,
sparse_vector=hsparse,
top_k=5,
)
LangChain integration
Pinecone works as a vector store in LangChain, supporting document ingestion, similarity search, and retriever creation.
from langchain_pinecone import PineconeVectorStore
from langchain_openai import OpenAIEmbeddings
# Create vector store
vectorstore = PineconeVectorStore.from_documents(
documents=docs,
embedding=OpenAIEmbeddings(),
index_name="my-index"
)
# Query
results = vectorstore.similarity_search("query", k=5)
# With metadata filter
results = vectorstore.similarity_search(
"query",
k=5,
filter={"category": "tutorial"}
)
# As retriever
retriever = vectorstore.as_retriever(search_kwargs={"k": 10})
LlamaIndex integration
Connect Pinecone to LlamaIndex for document indexing and querying.
from llama_index.vector_stores.pinecone import PineconeVectorStore
# Connect to Pinecone
pc = Pinecone(api_key="your-key")
pinecone_index = pc.Index("my-index")
# Create vector store
vector_store = PineconeVectorStore(pinecone_index=pinecone_index)
# Use in LlamaIndex
from llama_index.core import StorageContext, VectorStoreIndex
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
Index management
List, describe, and delete indices. Use describe_index_stats to monitor vector counts and namespaces.
# List indices
indexes = pc.list_indexes()
# Describe index
index_info = pc.describe_index("my-index")
print(index_info)
# Get index stats
stats = index.describe_index_stats()
print(f"Total vectors: {stats['total_vector_count']}")
print(f"Namespaces: {stats['namespaces']}")
# Delete index
pc.delete_index("my-index")
Delete vectors
Remove vectors by ID, filter, namespace, or wipe the entire index.
# Delete by ID
index.delete(ids=["vec1", "vec2"])
# Delete by filter
index.delete(filter={"category": "old"})
# Delete all in namespace
index.delete(delete_all=True, namespace="test")
# Delete entire index
index.delete(delete_all=True)
Best practices
- Use serverless - Auto-scaling, cost-effective
- Batch upserts - More efficient (100-200 per batch)
- Add metadata - Enable filtering
- Use namespaces - Isolate data by user/tenant
- Monitor usage - Check Pinecone dashboard
- Optimize filters - Index frequently filtered fields
- Test with free tier - 1 index, 100K vectors free
- Use hybrid search - Better quality
- Set appropriate dimensions - Match embedding model
- Regular backups - Export important data
Performance
| Operation | Latency | Notes |
|---|---|---|
| Upsert | ~50-100ms | Per batch |
| Query (p50) | ~50ms | Depends on index size |
| Query (p95) | ~100ms | SLA target |
| Metadata filter | ~+10-20ms | Additional overhead |
Pricing (as of 2025)
Serverless:
- $0.096 per million read units
- $0.06 per million write units
- $0.06 per GB storage/month
Free tier:
- 1 serverless index
- 100K vectors (1536 dimensions)
- Great for prototyping
When not to use it
If you need a self-hosted, open-source solution, consider Chroma or Weaviate. For offline, pure similarity search without a database, FAISS is a better fit. Pinecone is a managed SaaS service, so you trade infrastructure control for operational simplicity.
Limits and gotchas
- The
index.query()method does not accept analphaparameter for hybrid search; you must pre-scale vectors using thehybrid_score_normhelper shown above. - The old
pinecone-clientpackage is deprecated. Usepinecone(v5+). - Index dimension must match your embedding model's output size.
- Metadata filtering adds ~10-20ms overhead per query.
Resources
- Website: https://www.pinecone.io
- Docs: https://docs.pinecone.io
- Console: https://app.pinecone.io
- Pricing: https://www.pinecone.io/pricing