> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://api.qdrant.tech/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://api.qdrant.tech/_mcp/server.

# Upsert points

PUT http://localhost:6333/collections/{collection_name}/points
Content-Type: application/json

Performs the insert + update action on specified points. Any point with an existing \{id} will be overwritten.

Reference: https://api.qdrant.tech/api-reference/points/upsert-points

## Authentication

- `api-key` header (required) — API Key authentication via header

## Servers

- `http://localhost:6333` (http, default)
- `https://localhost:6333` (https)

## Request

### Path parameters

- `collection_name` (string, required) — Name of the collection to update from

### Query parameters

- `wait` (boolean, optional) — If true, wait for changes to actually happen
- `ordering` (enum, optional) — define ordering guarantees for the operation
  - Allowed values: `weak`, `medium`, `strong`
- `timeout` (integer, optional) — Timeout for the operation

### Body (application/json)

This endpoint expects a PointInsertOperations.

- `PointInsertOperations`

## Response

### 200

successful operation

- `usage` (CollectionsCollectionNamePointsPutResponsesContentApplicationJsonSchemaUsage, optional)
- `time` (double, optional) — Time spent to process this request
- `status` (string, optional)
- `result` (UpdateResult, optional)

## Types

### PointsBatch

- `batch` (Batch, required)
- `shard_key` (PointsBatchShardKey, optional)
- `update_filter` (PointsBatchUpdateFilter, optional) — Filter to apply when updating existing points. Only points matching this filter will be updated. Points that don't match will keep their current state. New points will be inserted regardless of the filter.
- `update_mode` (PointsBatchUpdateMode, optional) — Mode of the upsert operation: insert_only, upsert (default), update_only

### PointsList

- `points` (list of PointStruct, required)
- `shard_key` (PointsListShardKey, optional)
- `update_filter` (PointsListUpdateFilter, optional) — Filter to apply when updating existing points. Only points matching this filter will be updated. Points that don't match will keep their current state. New points will be inserted regardless of the filter.
- `update_mode` (PointsListUpdateMode, optional) — Mode of the upsert operation: insert_only, upsert (default), update_only

### CollectionsCollectionNamePointsPutResponsesContentApplicationJsonSchemaUsage

### UpdateResult

- `status` (enum, required) — `Acknowledged` - Request is saved to WAL and will be process in a queue. `Completed` - Request is completed, changes are actual. `WaitTimeout` - Request is waiting for timeout.
  - Allowed values: `acknowledged`, `completed`, `wait_timeout`
- `operation_id` (uint64, optional, nullable) — Sequential number of the operation

### Batch

- `ids` (list of ExtendedPointId, required)
- `vectors` (BatchVectorStruct, required)
- `payloads` (list of BatchPayloadsItems, optional, nullable)

### PointsBatchShardKey

### PointsBatchUpdateFilter

Filter to apply when updating existing points. Only points matching this filter will be updated. Points that don't match will keep their current state. New points will be inserted regardless of the filter.

### PointsBatchUpdateMode

Mode of the upsert operation: insert_only, upsert (default), update_only

### PointStruct

- `id` (ExtendedPointId, required) — Type, used for specifying point ID in user interface
- `vector` (VectorStruct, required) — Full vector data per point separator with single and multiple vector modes
- `payload` (PointStructPayload, optional) — Payload values (optional)

### PointsListShardKey

### PointsListUpdateFilter

Filter to apply when updating existing points. Only points matching this filter will be updated. Points that don't match will keep their current state. New points will be inserted regardless of the filter.

### PointsListUpdateMode

Mode of the upsert operation: insert_only, upsert (default), update_only

### Usage

Usage of the hardware resources, spent to process the request

- `hardware` (UsageHardware, optional)
- `inference` (UsageInference, optional)

### ExtendedPointId

Type, used for specifying point ID in user interface

### BatchVectorStruct

### BatchPayloadsItems

### Filter

- `should` (FilterShould, optional) — At least one of those conditions should match
- `min_should` (FilterMinShould, optional) — At least minimum amount of given conditions should match
- `must` (FilterMust, optional) — All conditions must match
- `must_not` (FilterMustNot, optional) — All conditions must NOT match

### VectorStruct

Full vector data per point separator with single and multiple vector modes

### PointStructPayload

Payload values (optional)

### UsageHardware

### UsageInference

### FilterShould

At least one of those conditions should match

### FilterMinShould

At least minimum amount of given conditions should match

### FilterMust

All conditions must match

### FilterMustNot

All conditions must NOT match

### Document

WARN: Work-in-progress, unimplemented Text document for embedding. Requires inference infrastructure, unimplemented.

- `text` (string, required) — Text of the document. This field will be used as input for the embedding model.
- `model` (string, required) — Name of the model used to generate the vector. List of available models depends on a provider.
- `options` (DocumentOptions, optional) — Additional options for the model, will be passed to the inference service as-is. See model cards for available options.

### Image

WARN: Work-in-progress, unimplemented Image object for embedding. Requires inference infrastructure, unimplemented.

- `image` (any, required) — Image data: base64 encoded image or an URL
- `model` (string, required) — Name of the model used to generate the vector. List of available models depends on a provider.
- `options` (map from string to any, optional, nullable) — Parameters for the model Values of the parameters are model-specific

### InferenceObject

WARN: Work-in-progress, unimplemented Custom object for embedding. Requires inference infrastructure, unimplemented.

- `object` (any, required) — Arbitrary data, used as input for the embedding model. Used if the model requires more than one input or a custom input.
- `model` (string, required) — Name of the model used to generate the vector. List of available models depends on a provider.
- `options` (map from string to any, optional, nullable) — Parameters for the model Values of the parameters are model-specific

### HardwareUsage

Usage of the hardware resources, spent to process the request

- `cpu` (integer, required)
- `payload_io_read` (integer, required)
- `payload_io_write` (integer, required)
- `payload_index_io_read` (integer, required)
- `payload_index_io_write` (integer, required)
- `vector_io_read` (integer, required)
- `vector_io_write` (integer, required)

### InferenceUsage

- `models` (map from string to ModelUsage, required)

### MinShould

- `conditions` (list of Condition, required)
- `min_count` (integer, required)

### DocumentOptions

Option variants for text documents. Ether general-purpose options or BM25-specific options. BM25-specific will only take effect if the `qdrant/bm25` is specified as a model.

### ModelUsage

- `tokens` (uint64, required)

### Condition

### Bm25Config

Configuration of the local bm25 models.

- `k` (double, optional, default: 1.2) — Controls term frequency saturation. Higher values mean term frequency has more impact. Default is 1.2
- `b` (double, optional, default: 0.75) — Controls document length normalization. Ranges from 0 (no normalization) to 1 (full normalization). Higher values mean longer documents have less impact. Default is 0.75.
- `avg_len` (double, optional, default: 256) — Expected average document length in the collection. Default is 256.
- `tokenizer` (enum, optional)
  - Allowed values: `prefix`, `whitespace`, `word`, `multilingual`
- `language` (string, optional, nullable) — Defines which language to use for text preprocessing. This parameter is used to construct default stopwords filter and stemmer. To disable language-specific processing, set this to `"language": "none"`. If not specified, English is assumed.
- `lowercase` (boolean, optional, nullable) — Lowercase the text before tokenization. Default is `true`.
- `ascii_folding` (boolean, optional, nullable) — If true, normalize tokens by folding accented characters to ASCII (e.g., "ação" -> "acao"). Default is `false`.
- `stopwords` (Bm25ConfigStopwords, optional) — Configuration of the stopwords filter. Supports list of pre-defined languages and custom stopwords. Default: initialized for specified `language` or English if not specified.
- `stemmer` (Bm25ConfigStemmer, optional) — Configuration of the stemmer. Processes tokens to their root form. Default: initialized Snowball stemmer for specified `language` or English if not specified.
- `min_token_len` (integer, optional, nullable) — Minimum token length to keep. If token is shorter than this, it will be discarded. Default is `None`, which means no minimum length.
- `max_token_len` (integer, optional, nullable) — Maximum token length to keep. If token is longer than this, it will be discarded. Default is `None`, which means no maximum length.

### FieldCondition

All possible payload filtering conditions

- `key` (string, required) — Payload key
- `match` (FieldConditionMatch, optional) — Check if point has field with a given value
- `range` (FieldConditionRange, optional) — Check if points value lies in a given range
- `geo_bounding_box` (FieldConditionGeoBoundingBox, optional) — Check if points geolocation lies in a given area
- `geo_radius` (FieldConditionGeoRadius, optional) — Check if geo point is within a given radius
- `geo_polygon` (FieldConditionGeoPolygon, optional) — Check if geo point is within a given polygon
- `values_count` (FieldConditionValuesCount, optional) — Check number of values of the field
- `is_empty` (boolean, optional, nullable) — Check that the field is empty, alternative syntax for `is_empty: "field_name"`
- `is_null` (boolean, optional, nullable) — Check that the field is null, alternative syntax for `is_null: "field_name"`

### IsEmptyCondition

Select points with empty payload for a specified field

- `is_empty` (PayloadField, required) — Payload field

### IsNullCondition

Select points with null payload for a specified field

- `is_null` (PayloadField, required) — Payload field

### HasIdCondition

ID-based filtering condition

- `has_id` (list of ExtendedPointId, required)

### HasVectorCondition

Filter points which have specific vector assigned

- `has_vector` (string, required)

### NestedCondition

- `nested` (Nested, required) — Select points with payload for a specified nested field

### Bm25ConfigStopwords

Configuration of the stopwords filter. Supports list of pre-defined languages and custom stopwords. Default: initialized for specified `language` or English if not specified.

### Bm25ConfigStemmer

Configuration of the stemmer. Processes tokens to their root form. Default: initialized Snowball stemmer for specified `language` or English if not specified.

### FieldConditionMatch

Check if point has field with a given value

### FieldConditionRange

Check if points value lies in a given range

### FieldConditionGeoBoundingBox

Check if points geolocation lies in a given area

### FieldConditionGeoRadius

Check if geo point is within a given radius

### FieldConditionGeoPolygon

Check if geo point is within a given polygon

### FieldConditionValuesCount

Check number of values of the field

### PayloadField

Payload field

- `key` (string, required) — Payload field name

### Nested

Select points with payload for a specified nested field

- `key` (string, required)
- `filter` (Filter, required)

### GeoBoundingBox

Geo filter request Matches coordinates inside the rectangle, described by coordinates of lop-left and bottom-right edges

- `top_left` (GeoPoint, required) — Geo point payload schema
- `bottom_right` (GeoPoint, required) — Geo point payload schema

### GeoRadius

Geo filter request Matches coordinates inside the circle of `radius` and center with coordinates `center`

- `center` (GeoPoint, required) — Geo point payload schema
- `radius` (double, required) — Radius of the area in meters

### GeoPolygon

Geo filter request Matches coordinates inside the polygon, defined by `exterior` and `interiors`

- `exterior` (GeoLineString, required) — Ordered sequence of GeoPoints representing the line
- `interiors` (list of GeoLineString, optional, nullable) — Interior lines (if present) bound holes within the surface each GeoLineString must consist of a minimum of 4 points, and the first and last points must be the same.

### ValuesCount

Values count filter request

- `lt` (integer, optional, nullable) — point.key.length() \< values\_count.lt
- `gt` (integer, optional, nullable) — point.key.length() > values_count.gt
- `gte` (integer, optional, nullable) — point.key.length() >= values_count.gte
- `lte` (integer, optional, nullable) — point.key.length() \<= values\_count.lte

### GeoPoint

Geo point payload schema

- `lon` (double, required)
- `lat` (double, required)

### GeoLineString

Ordered sequence of GeoPoints representing the line

- `points` (list of GeoPoint, required)

## Examples

**Request**

```json
{
  "batch": {
    "ids": [
      42
    ],
    "vectors": {}
  }
}
```

**Response**

```json
{
  "usage": {
    "hardware": {
      "cpu": 1,
      "payload_io_read": 1,
      "payload_io_write": 1,
      "payload_index_io_read": 1,
      "payload_index_io_write": 1,
      "vector_io_read": 1,
      "vector_io_write": 1
    },
    "inference": {
      "models": {}
    }
  },
  "time": 0.002,
  "status": "ok",
  "result": {
    "status": "acknowledged",
    "operation_id": 1
  }
}
```

**SDK Code**

```python
from qdrant_client import QdrantClient, models

client = QdrantClient(url="http://localhost:6333")

client.upsert(
    collection_name="{collection_name}",
    points=[
        models.PointStruct(
            id=1,
            payload={
                "color": "red",
            },
            vector=[0.9, 0.1, 0.1],
        ),
        models.PointStruct(
            id=2,
            payload={
                "color": "green",
            },
            vector=[0.1, 0.9, 0.1],
        ),
        models.PointStruct(
            id=3,
            payload={
                "color": "blue",
            },
            vector=[0.1, 0.1, 0.9],
        ),
    ],
)

```

```java
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.VectorFactory.vector;
import static io.qdrant.client.VectorsFactory.namedVectors;

import java.util.List;
import java.util.Map;

import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;

import io.qdrant.client.grpc.Points.PointStruct;

QdrantClient client = new QdrantClient(
                QdrantGrpcClient.newBuilder("localhost", 6334, false).build());

client
    .upsertAsync(
        "{collection_name}",
        List.of(
            PointStruct.newBuilder()
                .setId(id(1))
                .setVectors(
                    namedVectors(
                        Map.of(
                            "image",
                            vector(List.of(0.9f, 0.1f, 0.1f, 0.2f)),
                            "text",
                            vector(List.of(0.4f, 0.7f, 0.1f, 0.8f, 0.1f, 0.1f, 0.9f, 0.2f)))))
                .build(),
            PointStruct.newBuilder()
                .setId(id(2))
                .setVectors(
                    namedVectors(
                        Map.of(
                            "image",
                            List.of(0.2f, 0.1f, 0.3f, 0.9f),
                            "text",
                            List.of(0.5f, 0.2f, 0.7f, 0.4f, 0.7f, 0.2f, 0.3f, 0.9f))))
                .build()))
    .get();

```

```go
package client

import (
	"context"
	"fmt"

	"github.com/qdrant/go-client/qdrant"
)

func upsert() {
	client, err := qdrant.NewClient(&qdrant.Config{
		Host: "localhost",
		Port: 6334,
	})
	if err != nil {
		panic(err)
	}

	response, err := client.Upsert(context.Background(), &qdrant.UpsertPoints{
		CollectionName: "{collection_name}",
		Points: []*qdrant.PointStruct{
			{
				Id:      qdrant.NewIDNum(1),
				Vectors: qdrant.NewVectors(0.9, 0.1, 0.1),
				Payload: qdrant.NewValueMap(map[string]any{
					"color": "red",
				}),
			},
			{
				Id:      qdrant.NewIDNum(2),
				Vectors: qdrant.NewVectors(0.1, 0.9, 0.1),
				Payload: qdrant.NewValueMap(map[string]any{
					"color": "green",
				}),
			},
			{
				Id:      qdrant.NewIDNum(3),
				Vectors: qdrant.NewVectors(0.1, 0.1, 0.9),
				Payload: qdrant.NewValueMap(map[string]any{
					"color": "blue",
				}),
			},
		},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println("Upsert status: ", response.GetStatus())
}

```

```typescript
import { QdrantClient } from "@qdrant/js-client-rest";

const client = new QdrantClient({ host: "localhost", port: 6333 });

client.upsert("{collection_name}", {
  points: [
    {
      id: 1,
      payload: { color: "red" },
      vector: [0.9, 0.1, 0.1],
    },
    {
      id: 2,
      payload: { color: "green" },
      vector: [0.1, 0.9, 0.1],
    },
    {
      id: 3,
      payload: { color: "blue" },
      vector: [0.1, 0.1, 0.9],
    },
  ],
});

```

```rust
use qdrant_client::qdrant::{PointStruct, UpsertPointsBuilder};
use qdrant_client::{Qdrant, Payload};
use serde_json::json;

let client = Qdrant::from_url("http://localhost:6334").build()?;

client
    .upsert_points(
        UpsertPointsBuilder::new(
            "{collection_name}",
            vec![
                PointStruct::new(
                    1,
                    vec![0.9, 0.1, 0.1],
                    Payload::try_from(json!(
                        {"color": "red"}
                    ))
                    .unwrap(),
                ),
                PointStruct::new(
                    2,
                    vec![0.1, 0.9, 0.1],
                    Payload::try_from(json!(
                        {"color": "green"}
                    ))
                    .unwrap(),
                ),
                PointStruct::new(
                    3,
                    vec![0.1, 0.1, 0.9],
                    Payload::try_from(json!(
                        {"color": "blue"}
                    ))
                    .unwrap(),
                ),
            ],
        )
        .wait(true),
    )
    .await?;

```

```csharp
using Qdrant.Client;
using Qdrant.Client.Grpc;

var client = new QdrantClient("localhost", 6334);

await client.UpsertAsync(
  collectionName: "{collection_name}",
  points: new List<PointStruct>
  {
    new()
    {
      Id = 1,
      Vectors = new[] { 0.9f, 0.1f, 0.1f },
      Payload = { ["city"] = "red" }
    },
    new()
    {
      Id = 2,
      Vectors = new[] { 0.1f, 0.9f, 0.1f },
      Payload = { ["city"] = "green" }
    },
    new()
    {
      Id = 3,
      Vectors = new[] { 0.1f, 0.1f, 0.9f },
      Payload = { ["city"] = "blue" }
    }
  }
);

```

```ruby
require 'uri'
require 'net/http'

url = URI("http://localhost:6333/collections/collection_name/points")

http = Net::HTTP.new(url.host, url.port)

request = Net::HTTP::Put.new(url)
request["api-key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"batch\": {\n    \"ids\": [\n      42\n    ],\n    \"vectors\": {}\n  }\n}"

response = http.request(request)
puts response.read_body
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PUT', 'http://localhost:6333/collections/collection_name/points', [
  'body' => '{
  "batch": {
    "ids": [
      42
    ],
    "vectors": {}
  }
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'api-key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```swift
import Foundation

let headers = [
  "api-key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = ["batch": [
    "ids": [42],
    "vectors": []
  ]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:6333/collections/collection_name/points")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```