Working with serverless indexes

Serverless indexes scale automatically: you pay for storage and queries, and Pinecone handles capacity, replication, and availability.

Create a serverless index

create() takes the index’s fields as a schema and its cloud and region as a deployment:

from pinecone import Pinecone

pc = Pinecone(api_key="your-api-key")

pc.indexes.create(
    name="product-search",
    schema={"fields": {"your_vector": {"type": "dense_vector", "dimension": 1536, "metric": "cosine"}}},
    deployment={"deployment_type": "managed", "cloud": "aws", "region": "us-east-1"},
)

"deployment_type": "managed" is what makes an index serverless. Omit deployment and the server creates a managed index on AWS us-east-1.

Only fields that get searched go in the schema: dense_vector, sparse_vector, or string with a full_text_search, embed, or sparse_embed config (the last two enable integrated embedding). Leave out metadata you only filter on: put it on the records you upsert and the server indexes it for filtering automatically. The server rejects a schema that declares a metadata-only field. The schema can’t change after the index exists, so a hybrid index has to declare its sparse_vector field at create time.

create polls until the index is ready, with no upper time bound. Pass a positive timeout to bound the wait (it raises PineconeTimeoutError if the index still isn’t ready), or timeout=-1 to return as soon as the server accepts the request.

dimension=, metric=, vector_type=, and spec= are deprecated keyword arguments that the SDK translates into schema= and deployment=. Combining one with the argument it translates to raises PineconeValueError. See the v10 migration guide to move code off them.

Clouds and regions

CloudProvider and the per-cloud region enums AwsRegion, GcpRegion, and AzureRegion give you tab-completion and typo safety:

from pinecone import AwsRegion, CloudProvider, Pinecone

pc = Pinecone(api_key="your-api-key")

pc.indexes.create(
    name="product-search",
    schema={"fields": {"your_vector": {"type": "dense_vector", "dimension": 1536, "metric": "cosine"}}},
    deployment={
        "deployment_type": "managed",
        "cloud": CloudProvider.AWS,
        "region": AwsRegion.US_EAST_1,
    },
)

The SDK sends cloud and region as plain strings without checking them, so a region Pinecone adds before an enum member names it works as a string literal, and a typo comes back as a server error.

Enable deletion protection

Pass deletion_protection="enabled" to keep the index from being deleted by accident:

pc.indexes.create(
    name="product-search",
    schema={"fields": {"your_vector": {"type": "dense_vector", "dimension": 1536, "metric": "cosine"}}},
    deployment={"deployment_type": "managed", "cloud": "aws", "region": "us-east-1"},
    deletion_protection="enabled",
)

Choose read capacity

read_capacity decides how the index serves reads and how they are billed. Omit it and the index uses on-demand capacity, billed per operation. Dedicated capacity provisions read nodes that you size yourself:

pc.indexes.create(
    name="product-search",
    schema={"fields": {"your_vector": {"type": "dense_vector", "dimension": 1536, "metric": "cosine"}}},
    deployment={"deployment_type": "managed", "cloud": "aws", "region": "us-east-1"},
    read_capacity={
        "mode": "Dedicated",
        "dedicated": {
            "node_type": "t1",
            "scaling": "Manual",
            "manual": {"shards": 2, "replicas": 2},
        },
    },
)

node_type is "b1" or "t1", and manual sets the shards and replicas to provision. Setting replicas to 0 disables the index, which cuts cost while a workload is paused without dropping the data.

configure() takes the same argument to change an existing index. The change applies asynchronously, so configure returns while it is still in flight:

pc.indexes.configure("product-search", read_capacity={"mode": "OnDemand"})

describe(...).read_capacity is a ReadCapacityOnDemandResponse or a ReadCapacityDedicatedResponse, or None when the response carries none. Neither class has a mode attribute; branch on the type:

from pinecone import ReadCapacityDedicatedResponse

rc = pc.indexes.describe("product-search").read_capacity
if rc is not None:
    print(rc.status.state)  # 'Ready'
    if isinstance(rc, ReadCapacityDedicatedResponse):
        print(rc.dedicated.node_type, rc.dedicated.manual.shards)

status.state shows an asynchronous change in progress: "Scaling" after a replica or shard change, "Migrating" while moving to another node type, "Ready" once settled, and "Error" with the reason in status.error_message. status.current_shards and status.current_replicas report what is actually running.

Pod-based indexes don’t take read_capacity; they size reads with pod type, replicas, and shards (see pod-based indexes).

Check index status

describe() returns an IndexModel. If you passed timeout=-1 to create, poll status.ready until the index can serve requests:

import time

while not pc.indexes.describe("product-search").status.ready:
    time.sleep(5)

List indexes

list() returns a Paginator that yields every index in the project:

names = [idx.name for idx in pc.indexes.list()]

Describe an index

The dimension and metric live on each vector field in schema.fields, not on the index:

idx = pc.indexes.describe("product-search")
print(idx.host, idx.deployment.cloud, idx.deployment.region)
vector_field = idx.schema.fields["your_vector"]
print(vector_field.dimension, vector_field.metric)

idx.dimension and idx.metric are deprecated. They resolve only when the schema has a single dense vector field, and raise AttributeError naming the fields when it has several.

Delete an index

pc.indexes.delete("product-search")

delete() polls until the index is gone, with no upper time bound. Pass a positive timeout to bound the wait, or timeout=-1 to return while the server is still tearing the index down.

The server refuses to delete an index with deletion protection enabled, raising ForbiddenError. Disable it first:

pc.indexes.configure("product-search", deletion_protection="disabled")
pc.indexes.delete("product-search")

See also