Generating embeddings

Pinecone hosts embedding models, so you can turn text into vectors without running embedding infrastructure of your own. pc.inference.embed takes the text and hands the vectors back to you — to upsert, to query with, or to keep somewhere else entirely.

If you would rather never handle a vector, that is an index with integrated inference, where Pinecone embeds on both write and read. See Integrated records.

Basic usage

from pinecone import Pinecone

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

embeddings = pc.inference.embed(
    model="multilingual-e5-large",
    inputs=["The quick brown fox", "A second piece of text"],
    parameters={"input_type": "passage"},
)

for embedding in embeddings:
    print(embedding.values[:3])

One embedding comes back per input, in input order. A bare string is wrapped for you, so inputs="one string" still returns a one-item result rather than a lone embedding.

input_type: the mismatch that costs you relevance

Many hosted models are asymmetric — they embed stored text and search queries differently, and input_type is how you say which side you are on. Embed your corpus as "passage" and your queries as "query":

passages = pc.inference.embed(
    model="multilingual-e5-large",
    inputs=["Vector databases index embeddings for similarity search."],
    parameters={"input_type": "passage"},
)

query = pc.inference.embed(
    model="multilingual-e5-large",
    inputs="How does similarity search work?",
    parameters={"input_type": "query"},
)

Get this backwards and nothing fails: both calls succeed, both return vectors of the right width, and your search quality quietly degrades. Nothing in the SDK can catch it for you, so decide per call site which side you are embedding.

Discover what a model accepts

parameters is model-specific. Read the keys off the model rather than hardcoding a list that will go stale:

info = pc.inference.model.get("multilingual-e5-large")

for parameter in info.supported_parameters:
    print(parameter.parameter, parameter.allowed_values, parameter.default)

Each entry carries allowed_values when the set is fixed, min/max when the value is numeric, and default for what the model uses when you omit the key. required tells you whether it must be sent at all.

The same ModelInfo answers the other questions you would otherwise guess at:

print(info.type)                # 'embed' or 'rerank'
print(info.vector_type)         # 'dense' or 'sparse'
print(info.default_dimension)   # output width when no dimension is requested
print(info.max_batch_size)      # most inputs one call may carry
print(info.max_sequence_length) # longest input accepted

Those fields are None on a reranking model, so read info.type before relying on them.

Response: EmbeddingsList

embed returns an EmbeddingsList. Iterating it yields the embeddings, and len() and integer indexing reach the same items:

  • .data — the same list the iteration walks, one entry per input.

  • .vector_type"dense" or "sparse", and so which fields the entries carry.

  • .model — the model that served the request. Pinecone may substitute a different model from the one you asked for, so read it here rather than assuming it echoes your argument.

  • .usage.total_tokens — tokens counted for the call.

Dense and sparse embeddings

The two shapes carry the vector in different fields. A DenseEmbedding has values; a SparseEmbedding has sparse_values and sparse_indices paired position by position, and no values at all. Branch on vector_type when the model is not fixed in advance:

embeddings = pc.inference.embed(
    model="multilingual-e5-large",
    inputs=["machine learning frameworks"],
    parameters={"input_type": "passage"},
)

first = embeddings.data[0]
if embeddings.vector_type == "sparse":
    print(first.sparse_indices, first.sparse_values)
else:
    print(first.values[:3])

Reading .values on a sparse embedding does not raise — it hands back a dict-view method instead of a vector, and your code carries on with the wrong object. The vector_type check is what protects you.

One embed call returns either all-dense or all-sparse results, never a mix. To build a hybrid index, call embed once per model and pair the two result sets yourself.

Using the EmbedModel enum

EmbedModel gives you tab-completion and typo safety in place of a bare string:

from pinecone import Pinecone, EmbedModel

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

embeddings = pc.inference.embed(
    model=EmbedModel.Multilingual_E5_Large,
    inputs=["search query"],
    parameters={"input_type": "query"},
)
print(embeddings.model)

A model that is not an enum member is still fine as a string — the enum lags new models by a release, and embed accepts either.

Batch size

Sending several inputs in one call amortizes the round trip. Each model caps how many inputs it accepts per call, and the cap is reported as max_batch_size on the model info — read it from the model rather than guessing, and keep your chunk size at or below it. Exceeding it is a server-side rejection, not something the SDK splits for you:

texts = [f"document number {n}" for n in range(250)]

batch_size = 50
all_embeddings = []
for start in range(0, len(texts), batch_size):
    batch = pc.inference.embed(
        model="multilingual-e5-large",
        inputs=texts[start : start + batch_size],
        parameters={"input_type": "passage"},
    )
    all_embeddings.extend(batch.data)

print(len(all_embeddings))

Overlong individual inputs are a separate concern from batch size: the truncate parameter decides whether the model trims them or rejects the request. Its accepted values are in supported_parameters, above.

Storing embeddings in an index

Read the values off each embedding and upsert them like any other vector:

index = pc.index("product-search")

embeddings = pc.inference.embed(
    model="multilingual-e5-large",
    inputs=["The quick brown fox", "A second piece of text"],
    parameters={"input_type": "passage"},
)

index.upsert(
    vectors=[(f"doc-{i}", embedding.values) for i, embedding in enumerate(embeddings)]
)

The index’s dense vector field must have been created at the model’s output dimension — info.default_dimension above — or the server rejects the upsert.

At query time, embed the query text with input_type="query" and pass the result to query(). See Upserting and querying vectors.

List available models

models = pc.inference.model.list(type="embed")
print(models.names())

type narrows the listing to "embed" or "rerank"; omit it for both. For embedding models, vector_type="dense" or "sparse" narrows it further — pairing vector_type with type="rerank" is rejected client-side rather than ignored.

sparse_models = pc.inference.model.list(type="embed", vector_type="sparse")
print(sparse_models.names())