Start hereQuick start · 6 you’ll reach for daily
Target versions · paceVersions
pinecone ≥ 5
@pinecone-database/pinecone ≥ 4
API 2025-04
The package renamed from pinecone-client →
pinecone in v5. The old global pinecone.init(...)
+ environment per index is gone — everything goes through the Pinecone instance.
Serverless is the default; pod-based is legacy + on its way out. Hybrid search needs
metric="dotproduct" and sparse vectors. This sheet pins to v5+.
Install · authSetup
# Python — v5+ client (post-rename from "pinecone-client")
pip install "pinecone>=5"
# Optional integrations
pip install pinecone[grpc] # high-throughput gRPC transport
pip install pinecone-text # sparse encoders for hybrid
# JS / TS
npm install @pinecone-database/pinecone
# API key — grab from the Pinecone console
export PINECONE_API_KEY=pcsk_...
# Smoke test
python -c "from pinecone import Pinecone; \
print(Pinecone(api_key='$PINECONE_API_KEY').list_indexes().names())"
Where things liveCommon imports
| from pinecone import Pinecone | Entry point — one instance per app. |
| from pinecone import ServerlessSpec, PodSpec | Index hosting specs. |
| from pinecone.grpc import PineconeGRPC as Pinecone | gRPC transport — ~2–5× throughput for big upserts. |
| from pinecone import Vector, SparseValues | Typed vector dicts (optional — raw dicts also accepted). |
| from pinecone_text.sparse import BM25Encoder, SpladeEncoder | Sparse encoders for hybrid search. |
| from pinecone import PineconeException | Catch-all client exception. |
Serverless · pod · lifecycleIndexes
| Pinecone(api_key=...) | Construct once. Reads PINECONE_API_KEY if omitted. |
| pc.list_indexes().names() | All index names in the project. |
| pc.describe_index("kb") | Spec + status + host URL. |
| pc.create_index(name, dimension, metric, spec=ServerlessSpec(...)) | Create serverless. Preferred for new projects. |
| ServerlessSpec(cloud="aws"|"gcp"|"azure", region=...) | Serverless host. |
| PodSpec(environment, pod_type="p1.x1"|"s1.x1", pods=N) | Legacy pod-based deploy. |
| metric="cosine" | "dotproduct" | "euclidean" | Distance. dotproduct required for hybrid. |
| dimension=1536 | Must match your embedder. Cannot be changed later. |
| deletion_protection="enabled" | Block delete_index until disabled. |
| pc.configure_index("kb", deletion_protection="disabled") | Toggle settings without recreating. |
| pc.delete_index("kb") | Destructive. |
| idx = pc.Index("kb") / pc.Index(host="https://...") | Open a handle. host avoids one lookup round-trip. |
from pinecone import Pinecone, ServerlessSpec, PodSpec
pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
# Serverless — pay-per-use, autoscales, default for new projects
pc.create_index(
name="kb",
dimension=1536, # match your embedder
metric="cosine", # cosine | dotproduct | euclidean
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
deletion_protection="enabled", # block accidental drops
)
# Pod-based — fixed capacity, predictable latency (legacy / large workloads)
pc.create_index(
name="kb-pod",
dimension=1536, metric="cosine",
spec=PodSpec(environment="us-west1-gcp", pod_type="p1.x1", pods=1),
)
# Wait until it's ready (status="Ready", host populated)
while not pc.describe_index("kb").status["ready"]:
time.sleep(1)
# Connect to the index
idx = pc.Index("kb")
idx.describe_index_stats()
# → {'dimension': 1536, 'index_fullness': 0.0,
# 'total_vector_count': 0, 'namespaces': {}}
upsert · update · delete · fetchUpsert & DML
| idx.upsert(vectors=[(id, values, metadata), ...]) | Tuple form. Quickest for plain dense vectors. |
| idx.upsert(vectors=[{"id","values","metadata","sparse_values"}]) | Dict form. Required for hybrid / sparse. |
| idx.upsert(..., namespace="prod") | Logical partition within an index. |
| idx.upsert(..., async_req=True) | Fire-and-forget — returns a future. |
| idx.update(id="d1", set_metadata={"reviewed": True}) | Patch metadata without re-uploading the vector. |
| idx.update(id="d1", values=[...]) | Replace the vector for one id. |
| idx.fetch(ids=["d1","d2"], namespace="prod") | Random access by id. |
| idx.delete(ids=["d1"], namespace="prod") | Delete by id. |
| idx.delete(filter={"src": "stale"}, namespace="prod") | Delete by metadata filter (serverless). |
| idx.delete(delete_all=True, namespace="prod") | Wipe a namespace. |
| Batch ≤ 100 vectors or 2 MB / call | Hard caps. Chunk before upload. |
k-NN · filtered · by idQuerying
| idx.query(vector=[...], top_k=5) | Plain k-NN. |
| idx.query(id="d1", top_k=5) | k-NN around an existing vector. |
| include_values=True, include_metadata=True | Return vectors / metadata alongside scores. |
| filter={"k": "v"} | Metadata pre-filter — runs before ANN. |
| namespace="prod" | Scope the search. Omit = default namespace. |
| vector=dense, sparse_vector={"indices","values"} | Hybrid query. Needs dotproduct index. |
| res["matches"][i]["score"] | Distance / similarity for each match. |
| idx.list_paginated(prefix="user-42-", limit=100) | Enumerate ids (serverless only). |
$eq · $in · $and · ...Metadata filters
| {"k": "v"} | Equality shorthand. |
| {"k": {"$eq": v}} / {"$ne": v} | Equality / inequality. |
| {"k": {"$gt": n, "$lte": m}} | Numeric range. |
| {"k": {"$in": [...]}} / {"$nin": [...]} | Set membership. |
| {"tags": {"$exists": true}} | Field present. |
| {"$and": [c1, c2]} / {"$or": [c1, c2]} | Boolean composition. Mix freely. |
| Allowed metadata types | str, number, bool, list of strings. No nested objects. |
| ~40 KB per vector metadata | Soft cap. Bigger payloads belong in a separate store keyed by id. |
Multi-tenant isolationNamespaces
| idx.upsert(..., namespace="tenant-a") | Logical partition. No cost to create — created on first write. |
| idx.query(..., namespace="tenant-a") | Search scoped to one namespace. |
| idx.describe_index_stats() | Per-namespace vector counts. |
| idx.delete(delete_all=True, namespace="tenant-a") | Drop one tenant’s data cleanly. |
Dense + sparseHybrid search
| metric="dotproduct" at index creation | Required. Cosine + euclidean don’t support sparse. |
| BM25Encoder().fit(corpus) | Train sparse statistics on your corpus. |
| bm25.encode_documents(text) | Encode for upsert — returns {"indices", "values"}. |
| bm25.encode_queries(text) | Encode for query — same shape. |
| SpladeEncoder() | Learned sparse model. Heavier but strong for technical text. |
| alpha · reweight dense and sparse | Multiply dense by α, sparse by (1−α) before query. Tune on a holdout. |
# Hybrid = dense semantic + sparse lexical (BM25-ish) in one query.
# Requires a dotproduct index (cosine breaks the math).
from pinecone import Pinecone
from pinecone_text.sparse import BM25Encoder
pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
idx = pc.Index("kb-hybrid") # created with metric="dotproduct"
# 1. Fit BM25 once on your corpus, then save its IDF table
bm25 = BM25Encoder()
bm25.fit(["text of doc 1", "text of doc 2", "..."])
# 2. Upsert dense + sparse for every chunk
docs = [{"id": "d1", "text": "..."}, {"id": "d2", "text": "..."}]
idx.upsert(vectors=[{
"id": d["id"],
"values": dense_embedder(d["text"]), # dense vector
"sparse_values": bm25.encode_documents(d["text"]), # {"indices", "values"}
"metadata": {"text": d["text"]}
} for d in docs])
# 3. Query — alpha trades off dense (1.0) ↔ sparse (0.0)
def weight(dense, sparse, alpha=0.5):
return ([v * alpha for v in dense],
{"indices": sparse["indices"],
"values": [v * (1 - alpha) for v in sparse["values"]]})
dv = dense_embedder("what is RAG?")
sv = bm25.encode_queries("what is RAG?")
hd, hs = weight(dv, sv, alpha=0.5)
idx.query(vector=hd, sparse_vector=hs, top_k=5, include_metadata=True)
Hosted embed · rerankPinecone Inference
| pc.inference.embed(model, inputs, parameters) | Hosted embeddings — multilingual-e5-large, llama-text-embed-v2, etc. |
| parameters={"input_type": "query" | "passage"} | Most embedders split query vs passage encoding. |
| pc.inference.rerank(model, query, documents, top_n) | Hosted cross-encoder rerank — bge-reranker-v2-m3, cohere-rerank-3.5. |
| integrated index with embed=... | Index that embeds text on the server — you upsert strings, not vectors. |
| idx.upsert_records(records=[{"id","text","..." }], namespace=...) | Integrated-inference upsert path. |
| idx.search(query={"inputs": {"text": "..."}, "top_k": 5}) | Integrated-inference query path. |
LangChain · LlamaIndex · HaystackFramework integrations
| from langchain_pinecone import PineconeVectorStore | Modern LangChain wrapper. Preferred. |
| PineconeVectorStore.from_documents(docs, embedding, index_name="kb") | Build store from LangChain Documents. |
| store.as_retriever(search_kwargs={"k": 4, "namespace": "prod"}) | Drop into an LCEL chain. |
| from langchain.vectorstores import Pinecone | Legacy import path — superseded. |
| from llama_index.vector_stores.pinecone import PineconeVectorStore | LlamaIndex adapter. |
| from haystack_integrations.document_stores.pinecone import PineconeDocumentStore | Haystack 2 document store. |
Create · upsert · queryEnd-to-end · Minimal RAG store
Serverless index + OpenAI embeddings + filtered query, namespaced. Drop in your own loader and you have a working store.
# Minimal Pinecone RAG store — create, upsert, filter, query
import os, time
from pinecone import Pinecone, ServerlessSpec
from openai import OpenAI
pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
oai = OpenAI()
NAME, DIM = "kb", 1536
if NAME not in [i.name for i in pc.list_indexes()]:
pc.create_index(
name=NAME, dimension=DIM, metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
)
while not pc.describe_index(NAME).status["ready"]:
time.sleep(1)
idx = pc.Index(NAME)
def embed(texts):
return [d.embedding for d in
oai.embeddings.create(model="text-embedding-3-small", input=texts).data]
docs = [
("d1", "Pinecone is a managed vector DB.", {"src": "blog"}),
("d2", "RAG joins retrieval with generation.", {"src": "docs"}),
("d3", "Namespaces isolate data per tenant.", {"src": "docs"}),
]
vecs = embed([t for _, t, _ in docs])
idx.upsert(vectors=[(i, v, {**m, "text": t})
for (i, t, m), v in zip(docs, vecs)],
namespace="prod")
q = embed(["what is RAG?"])[0]
res = idx.query(vector=q, top_k=2, namespace="prod",
filter={"src": {"$eq": "docs"}}, include_metadata=True)
for m in res["matches"]:
print(f"{m['score']:.3f} [{m['metadata']['src']}] {m['metadata']['text']}")
Best practiceGood to know
describe_index_stats().
from pinecone.grpc import PineconeGRPC drops you on a binary transport with multiplexed streams — usually 2–5× faster for bulk ingest.
top_k=5 can leave the ANN walker no candidates — bump top_k or pre-shard via namespaces.
Common trapsWatch out for
dotproduct indexes.
Setting metric="cosine" and then passing sparse_vector= gives an error or silently bad rankings — pick the metric before you start ingesting.
pinecone.init(...) is gone.
Any guide that calls pinecone.init(api_key=, environment=) is pre-v3. Use Pinecone(api_key=) and a Spec at index creation.