Migrating from v8.x to v9.x

v9 is a ground-up rewrite focused on simplicity, performance, and type safety. This guide covers the breaking changes and shows you the v9 equivalent for each v8 pattern.

Key changes

1. Namespace pattern for control-plane operations

In v8, control-plane methods lived directly on the Pinecone client:

# v8
pc.create_index(name="my-index", dimension=1536, metric="cosine", spec=...)
indexes = pc.list_indexes()
pc.delete_index("my-index")

In v9, they are grouped under namespace properties:

# v9
pc.indexes.create(name="my-index", dimension=1536, metric="cosine", spec=...)
indexes = pc.indexes.list()
pc.indexes.delete("my-index")

The same pattern applies to collections, backups, and inference:

pc.collections.create(...)
pc.backups.list()
pc.inference.embed(...)

2. Async client rename

PineconeAsyncio is renamed to AsyncPinecone. The old name still works but is deprecated and will be removed in a future release.

# v8
from pinecone import PineconeAsyncio
async with PineconeAsyncio(api_key="...") as pc:
    ...

# v9
from pinecone import AsyncPinecone
async with AsyncPinecone(api_key="...") as pc:
    ...

3. Response models

v8 returned a mix of plain dicts, dataclass models, and bespoke objects. v9 returns msgspec.Struct instances. Field access is straightforward—idx.name, idx.dimension—but the objects are immutable. dict() no longer works; use msgspec.structs.asdict(idx) if you need a dict.

# v9 — field access is unchanged
idx = pc.indexes.describe("my-index")
print(idx.name)        # works
print(idx.dimension)   # works
print(dict(idx))       # TypeError — structs are not dict-convertible

4. HTTP transport: httpx replaces urllib3

The SDK uses httpx instead of urllib3. Retry behavior is now configured with RetryConfig passed at client construction:

# v8 — retry parameters were keyword args on the client
pc = Pinecone(api_key="...", retries=3)

# v9
from pinecone import Pinecone, RetryConfig
pc = Pinecone(
    api_key="...",
    retry_config=RetryConfig(max_retries=3, backoff_factor=1.5),
)

5. gRPC: Rust extension replaces grpcio

GrpcIndex is now backed by a compiled Rust extension instead of the Python grpcio package. You do not need to install grpcio or grpcio-tools. The interface—upsert, query, fetch, delete—is unchanged.

# v9 — interface is the same; no grpcio dependency required
index = pc.index("my-index", grpc=True)
index.upsert(vectors=[...])

6. Import paths

Most public classes are still importable directly from pinecone:

from pinecone import Pinecone, AsyncPinecone, Index, GrpcIndex
from pinecone import ServerlessSpec, PodSpec
from pinecone import ConflictError, NotFoundError, ForbiddenError

Deep imports (from pinecone.core.client.api...) are no longer supported. Use the top-level package instead.

7. Python version requirement

Python 3.9 support is dropped. The minimum supported version is Python 3.10.

8. Removal of the pinecone_plugins.assistant import path

In v8, the assistant SDK shipped as a separate plugin package (pinecone-plugin-assistant) installed alongside pinecone. Code imported model classes from pinecone_plugins.assistant.*:

# v8
from pinecone_plugins.assistant.models import (
    AssistantModel, ContextOptions, Message, FileModel,
)
from pinecone_plugins.assistant.models.chat import ChatResponse

In v9 the assistant API is built into the main pinecone package and the pinecone_plugins import tree has been removed. All classes are now reachable from pinecone.models.assistant under either the canonical name or a legacy alias.

Replace each legacy import with the canonical path:

v8 import path

v9 import path

from pinecone_plugins.assistant.models import AssistantModel

from pinecone.models.assistant import AssistantModel

from pinecone_plugins.assistant.models import ContextOptions

from pinecone.models.assistant import ContextOptions

from pinecone_plugins.assistant.models import Message

from pinecone.models.assistant import Message

from pinecone_plugins.assistant.models import FileModel

from pinecone.models.assistant import FileModel (deprecated alias for AssistantFileModel)

from pinecone_plugins.assistant.models.chat import ChatResponse

from pinecone.models.assistant import ChatResponse

from pinecone_plugins.assistant.models.chat import Citation, Reference, Highlight

from pinecone.models.assistant import Citation, Reference, Highlight (deprecated aliases for ChatCitation etc.)

from pinecone_plugins.assistant.models.chat import StreamChatResponseMessageStart, StreamChatResponseContentDelta, StreamChatResponseCitation, StreamChatResponseMessageEnd

from pinecone.models.assistant import StreamChatResponseMessageStart, StreamChatResponseContentDelta, StreamChatResponseCitation, StreamChatResponseMessageEnd (deprecated aliases for StreamMessageStart etc.)

from pinecone_plugins.assistant.models.chat_completion import ChatCompletionResponse, StreamingChatCompletionChunk

from pinecone.models.assistant import ChatCompletionResponse, StreamingChatCompletionChunk

from pinecone_plugins.assistant.models.context_responses import ContextResponse, TextSnippet, MultimodalSnippet

from pinecone.models.assistant import ContextResponse, TextSnippet, MultimodalSnippet

from pinecone_plugins.assistant.models.context_responses import TextBlock, ImageBlock, Image

from pinecone.models.assistant import TextBlock, ImageBlock, Image (deprecated aliases for ContextTextBlock, ContextImageBlock, ContextImageData)

from pinecone_plugins.assistant.models.context_responses import PdfReference, TextReference, JsonReference, MarkdownReference, DocxReference

from pinecone.models.assistant import PdfReference, TextReference, JsonReference, MarkdownReference, DocxReference (all five alias the consolidated FileReference)

from pinecone_plugins.assistant.models.evaluation_responses import AlignmentResponse, Metrics, EvaluatedFact

from pinecone.models.assistant import AlignmentResponse, Metrics, EvaluatedFact (deprecated aliases for AlignmentResult, AlignmentScores, EntailmentResult)

from pinecone_plugins.assistant.models.list_files_response import ListFilesResponse

from pinecone.models.assistant import ListFilesResponse

from pinecone_plugins.assistant.models.list_assistants_response import ListAssistantsResponse

from pinecone.models.assistant import ListAssistantsResponse

from pinecone_plugins.assistant.models.shared import Message, Usage, TokenCounts

from pinecone.models.assistant import Message, Usage, TokenCounts (Usage and TokenCounts are deprecated aliases for ChatUsage)

from pinecone_plugins.assistant.assistant.assistant import Assistant

No replacement — see note below.

What does not change. Method-call backcompat is preserved:

# Both v8 and v9
pc = Pinecone(api_key="...")
pc.assistant.create_assistant("my-assistant")        # works in v9
pc.assistant.list_assistants()                       # works in v9
assistant = pc.assistant.describe_assistant("my-assistant")
assistant.upload_file(file_path="report.pdf")        # works in v9
assistant.chat(messages=[...])                       # works in v9

The pc.assistant namespace is preserved and singular/plural forms (pc.assistant and pc.assistants) are interchangeable. Legacy method names like create_assistant, delete_assistant, list_assistants_paginated, etc. continue to work alongside the canonical pc.assistants.create, .delete, .list_page.

The legacy plugin class is removed. Code that manually instantiated the plugin (Assistant(config, client_builder) from pinecone_plugins.assistant.assistant.assistant) has no v9 equivalent — the plugin discovery system was retired and pc.assistant is now a property on the Pinecone client. Such code must be rewritten to use pc.assistants directly.

Environment variables PINECONE_PLUGIN_ASSISTANT_CONTROL_HOST and PINECONE_PLUGIN_ASSISTANT_DATA_HOST are no longer consulted. To target a non-prod control plane, pass host= to the Pinecone constructor or set PINECONE_HOST. The data-plane host is discovered automatically from the host field of the describe_assistant response.

9. Partial-success contract for batched upsert

index.upsert(...) gains batch_size, max_concurrency, and show_progress kwargs that match v8’s signature on the surface — but the failure behaviour changed.

v8 behaviour. idx.upsert(vectors=[...], batch_size=N) raised on the first batch failure. Successful batches were lost; subsequent batches were not attempted.

v9 behaviour. Batches are submitted concurrently (max_concurrency=4 default; range 1–64). Per-batch HTTP retries happen automatically via RetryConfig. Failures that exceed the retry budget are captured on the returned UpsertResponse rather than raised. The response now carries total_item_count, failed_item_count, total_batch_count, successful_batch_count, failed_batch_count, errors: list[BatchError], plus convenience properties has_errors, error_count, success_count, and failed_items.

Code that wraps a batched upsert in try/except will silently undercount in v9 unless updated:

# v8 — relied on exception to roll back
try:
    idx.upsert(vectors=batch, batch_size=100)
except Exception:
    rollback()

# v9 — inspect response.has_errors instead
response = idx.upsert(vectors=batch, batch_size=100)
if response.has_errors:
    # Optionally retry only the failures:
    idx.upsert(vectors=response.failed_items, batch_size=100)
    # …or roll back if any failure is unacceptable

Single-request upsert (batch_size=None, the default) keeps its v8 raise-on-failure semantics. The contract change applies only when batch_size is set.

The same partial-success contract applies to AsyncIndex.upsert(...) and GrpcIndex.upsert(...).


v8 → v9 migration table

Operation

v8

v9

Create index

pc.create_index(name=..., dimension=..., spec=...)

pc.indexes.create(name=..., dimension=..., spec=...)

List indexes

pc.list_indexes()

pc.indexes.list()

Describe index

pc.describe_index("name")

pc.indexes.describe("name")

Delete index

pc.delete_index("name")

pc.indexes.delete("name")

Configure index

pc.configure_index("name", ...)

pc.indexes.configure("name", ...)

Check index exists

pc.describe_index("name") + try/except

pc.indexes.exists("name")

Get data-plane index

pc.Index("name")

pc.Index("name") (unchanged)

Get gRPC index

Pinecone(...).GrpcIndex("name")

pc.index("name", grpc=True)

Create collection

pc.create_collection(name=..., source=...)

pc.collections.create(name=..., source=...)

List collections

pc.list_collections()

pc.collections.list()

Delete collection

pc.delete_collection("name")

pc.collections.delete("name")

Upsert vectors

index.upsert(vectors=[...])

index.upsert(vectors=[...]) (unchanged for single-request; batched form gains max_concurrency + partial-success contract — see §9)

Query vectors

index.query(vector=[...], top_k=10)

index.query(vector=[...], top_k=10) (unchanged)

Fetch vectors

index.fetch(ids=[...])

index.fetch(ids=[...]) (unchanged)

Delete vectors

index.delete(ids=[...])

index.delete(ids=[...]) (unchanged)

Async client

PineconeAsyncio(api_key=...)

AsyncPinecone(api_key=...)

Retry config

Pinecone(retries=3)

Pinecone(retry_config=RetryConfig(max_retries=3))

Convert response to dict

dict(idx) or idx.to_dict()

msgspec.structs.asdict(idx)

Embed text

pc.inference.embed(...)

pc.inference.embed(...) (unchanged)


Legacy aliases

The following aliases remain importable from pinecone but are deprecated:

Deprecated name

Canonical name

PineconeAsyncio

AsyncPinecone

ForbiddenException

ForbiddenError (ForbiddenException still works as a deprecated alias)

NotFoundException

NotFoundError (NotFoundException still works as a deprecated alias)

pinecone_plugins.assistant.* (any submodule)

pinecone.models.assistant, pinecone.client.assistants.Assistants (via pc.assistant / pc.assistants)

These aliases will be removed in a future major release. Update your code to use the canonical names.