Sync vs Async Clients

The SDK ships two client pairs:

Sync

Async

Control plane

Pinecone

AsyncPinecone

Data plane

Index

AsyncIndex

Transport

httpx (sync)

httpx (async)

Context manager

with Pinecone() as pc:

async with AsyncPinecone() as pc:

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)

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 Pinecone and Index each manage a synchronous httpx.Client.

  • AsyncPinecone and AsyncIndex each manage an httpx.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