> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cyborg.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Upsert

Adds vectors to the index, or replaces existing vectors with the same ID. `upsert()` takes one of two shapes:

```python theme={null}
# Shape 1: a list of item dicts
index.upsert(items)

# Shape 2: parallel arrays of IDs and a NumPy array of vectors
index.upsert(ids, vectors)
```

### Parameters

#### Shape 1: list of item dicts

| Parameter | Type | Default | Description |
| - | - | - | - |
| `items` | `List[Dict]` | - | One dict per vector. |

Each dict can contain:

```python theme={null}
{
    "id": str,               # Required. Unique identifier.
    "vector": List[float],   # Optional if the index has an embedding model and "contents" is set.
    "contents": str | bytes, # Optional. Encrypted before storage.
    "metadata": Dict,        # Optional. Key-value pairs for filtering.
}
```

`contents` accepts a `str` or `bytes`. Strings are stored as-is; bytes are base64-encoded first. [`get()`](./get) always returns `contents` as a `str`, so bytes come back as their base64 text: decode them with `base64.b64decode()`.

<Note>`datetime` and `date` objects in `metadata` are converted to integer epoch milliseconds before being sent (naive datetimes are treated as UTC; sub-millisecond precision is truncated). Values read back with `get()` come back as the same integer, not a `datetime`. The same conversion runs on filter dicts passed to `query()` / `query_binary()` / `query_metadata()`, so a `{"created": {"$gte": datetime(...)}}` predicate works directly. See [Dates](../types#dates).</Note>

#### Shape 2: parallel arrays

| Parameter | Type | Default | Description |
| - | - | - | - |
| `ids` | `List[str]` | - | Unique identifier for each vector. |
| `vectors` | `np.ndarray` (shape `(n, dim)`) | - | Vector data. Sent as binary through [`upsert_binary()`](./upsert-binary). |

Shape 2 can't attach metadata or contents. To send them with parallel arrays, call [`upsert_binary()`](./upsert-binary) directly.

<Warning>Don't upsert vectors containing `NaN` or infinite values. The service stores them, and later `get` or `query` calls on that index that return vectors or distances fail with a `5xx` error. The SDK doesn't check for them.</Warning>

### Returns

`None`

### Exceptions

<AccordionGroup>
  <Accordion title="cyborgdb.ValidationError">
    * The arguments don't match either shape (also a `TypeError`), an item has no `id`, or the IDs and vectors differ in length. Raised before any request is sent.
    * The service rejects the items (HTTP `400`), for example a vector with the wrong dimension.
  </Accordion>

  <Accordion title="cyborgdb.AuthenticationError">
    The `index_key` or `api_key` is invalid (HTTP `401`).
  </Accordion>

  <Accordion title="cyborgdb.ServiceError">
    The service returns `5xx`.
  </Accordion>

  <Accordion title="cyborgdb.TransportError">
    The service can't be reached.
  </Accordion>
</AccordionGroup>

See [Errors](../errors) for the full hierarchy.

### Example Usage

#### Item dicts

```python theme={null}
items = [
    {"id": "doc1", "vector": [0.1, 0.2, 0.3, 0.4], "contents": "First document"},
    {"id": "doc2", "vector": [0.5, 0.6, 0.7, 0.8], "metadata": {"category": "news"}},
]

index.upsert(items)
```

#### Parallel arrays

```python theme={null}
import numpy as np

ids = ["vec1", "vec2", "vec3"]
vectors = np.random.rand(3, 4).astype(np.float32)

index.upsert(ids, vectors)
```

#### Parallel arrays with metadata and contents

```python theme={null}
import numpy as np

ids = ["vec4", "vec5", "vec6"]
vectors = np.random.rand(3, 4).astype(np.float32)
metadata = [{"category": "news"}, {"category": "research"}, None]
contents = ["Fourth body", "Fifth body", None]

index.upsert_binary(ids, vectors, metadata=metadata, contents=contents)
```

#### Replace an existing item

```python theme={null}
index.upsert([{"id": "doc1", "vector": [0.1, 0.2, 0.3, 0.4], "metadata": {"version": 1}}])

# Same ID: replaces the vector, contents, and metadata
index.upsert([{"id": "doc1", "vector": [0.2, 0.3, 0.4, 0.5], "metadata": {"version": 2}}])

item = index.get(["doc1"], include=["metadata"])[0]
print(item["metadata"])
# Output: {'version': 2}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.