Sync vs Async Clients¶
The SDK ships two client pairs:
Sync |
Async |
|
|---|---|---|
Control plane |
|
|
Data plane |
|
|
Transport |
httpx (sync) |
httpx (async) |
Context manager |
|
|
Use the sync client for scripts, CLI tools, and simple integrations. Use the async client inside async frameworks such as FastAPI, Starlette, or aiohttp, or any code that drives many concurrent operations.
Sync Client¶
Pinecone and Index are blocking. Each method call completes before returning.
from pinecone import Pinecone
pc = Pinecone() # reads PINECONE_API_KEY from environment
desc = pc.indexes.describe("product-search")
index = pc.index(host=desc.host)
index.upsert(vectors=[("product-42", [0.1, 0.2, ...])])
results = index.query(vector=[0.1, 0.2, ...], top_k=5)
Pinecone supports the context manager protocol to ensure the underlying HTTP client
is closed when you are finished:
with Pinecone() as pc:
results = pc.indexes.list()
Async Client¶
AsyncPinecone and AsyncIndex are non-blocking. Every method is a coroutine — you
must await it inside an async function.
AsyncPinecone is an async context manager. Always use it with async with so the
HTTP client is closed properly:
import asyncio
from pinecone import AsyncPinecone
async def main() -> None:
async with AsyncPinecone() as pc:
desc = await pc.indexes.describe("product-search")
index = await pc.index(host=desc.host)
await index.upsert(vectors=[("product-42", [0.1, 0.2, ...])])
results = await index.query(vector=[0.1, 0.2, ...], top_k=5)
asyncio.run(main())
pc.index() on AsyncPinecone is itself a coroutine — await it to get back an
AsyncIndex. The returned AsyncIndex manages its own HTTP session; close it
with async with index: or await index.close().
async with AsyncPinecone() as pc:
# Preferred: pass the host directly to skip a describe call
index = await pc.index(host="product-search-abc123.svc.pinecone.io")
async with index:
results = await index.query(vector=[0.1, 0.2, ...], top_k=5)
Same Operation — Two Styles¶
The example below shows the same upsert-and-query flow in both styles.
from pinecone import Pinecone
pc = Pinecone()
with pc.index(name="movie-recommendations") as index:
index.upsert(vectors=[
("movie-42", [0.012, -0.087, 0.153, ...]),
("movie-99", [0.045, 0.021, -0.064, ...]),
])
results = index.query(
vector=[0.012, -0.087, 0.153, ...],
top_k=5,
filter={"genre": "comedy"},
)
for match in results.matches:
print(match.id, match.score)
import asyncio
from pinecone import AsyncPinecone
async def main() -> None:
async with AsyncPinecone() as pc:
# Resolve the host first, then create the index client
desc = await pc.indexes.describe("movie-recommendations")
index = await pc.index(host=desc.host)
async with index:
await index.upsert(vectors=[
("movie-42", [0.012, -0.087, 0.153, ...]),
("movie-99", [0.045, 0.021, -0.064, ...]),
])
results = await index.query(
vector=[0.012, -0.087, 0.153, ...],
top_k=5,
filter={"genre": "comedy"},
)
for match in results.matches:
print(match.id, match.score)
asyncio.run(main())
When to Use gRPC¶
Pass grpc=True to pc.index() to use a GrpcIndex instead of the default HTTP
Index. gRPC uses HTTP/2 multiplexing and binary serialization, which can improve
throughput significantly for bulk upsert workloads.
from pinecone import Pinecone
pc = Pinecone()
index = pc.index(name="product-search", grpc=True)
index.upsert(vectors=[("product-42", [0.1, 0.2, ...])])
GrpcIndex is only available on the sync client. For high-throughput async workloads,
use AsyncIndex with concurrent tasks instead:
import asyncio
from pinecone import AsyncPinecone
async def upsert_batch(pc: AsyncPinecone, batch: list[tuple[str, list[float]]]) -> None:
index = await pc.index(host="product-search-abc123.svc.pinecone.io")
async with index:
await index.upsert(vectors=batch)
async def main() -> None:
batches = [...] # split your vectors into chunks
async with AsyncPinecone() as pc:
await asyncio.gather(*[upsert_batch(pc, b) for b in batches])
asyncio.run(main())
Connection Management¶
Both clients use httpx under the hood:
The sync
PineconeandIndexeach manage a synchronoushttpx.Client.AsyncPineconeandAsyncIndexeach manage anhttpx.AsyncClient.
Close the client when you are done to release connections. The context manager protocol handles this automatically:
# Explicit close
pc = Pinecone()
# ... use pc ...
pc.close()
# Preferred: context manager
with Pinecone() as pc:
# ... use pc ...
pass # close() called on exit
The AsyncIndex manages its own HTTP session independently of AsyncPinecone. Closing
AsyncPinecone does not close any AsyncIndex objects you created from it. Close
each AsyncIndex separately:
async with AsyncPinecone() as pc:
index = await pc.index(host="...")
async with index:
await index.query(...)
# index is closed here; pc is closed at the outer block exit