> 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.

# Discover batch points

POST http://localhost:6333/collections/{collection_name}/points/discover/batch
Content-Type: application/json

Retrieves points in batches based on the target and/or positive and negative example pairs.

Reference: https://api.qdrant.tech/v-1-18-x/api-reference/search/discover-batch-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 search in

### Query parameters

- `consistency` (ReadConsistency, optional) — Define read consistency guarantees for the operation
- `timeout` (integer, optional) — If set, overrides global timeout for this request. Unit is seconds.

### Body (application/json)

This endpoint expects a DiscoverRequestBatch.

- `searches` (list of DiscoverRequest, required)

## Response

### 200

successful operation

- `usage` (CollectionsCollectionNamePointsDiscoverBatchPostResponsesContentApplicationJsonSchemaUsage, optional)
- `time` (double, optional) — Time spent to process this request
- `status` (string, optional)
- `result` (list of list of ScoredPoint, optional)

## Types

### DiscoverRequest

Use context and a target to find the most similar points, constrained by the context.

- `limit` (integer, required) — Max number of result to return
- `shard_key` (DiscoverRequestShardKey, optional) — Specify in which shards to look for the points, if not specified - look in all shards
- `target` (DiscoverRequestTarget, optional) — Look for vectors closest to this. When using the target (with or without context), the integer part of the score represents the rank with respect to the context, while the decimal part of the score relates to the distance to the target.
- `context` (list of ContextExamplePair, optional, nullable) — Pairs of \{ positive, negative } examples to constrain the search. When using only the context (without a target), a special search - called context search - is performed where pairs of points are used to generate a loss that guides the search towards the zone where most positive examples overlap. This means that the score minimizes the scenario of finding a point closer to a negative than to a positive part of a pair. Since the score of a context relates to loss, the maximum score a point can get is 0.0, and it becomes normal that many points can have a score of 0.0. For discovery search (when including a target), the context part of the score for each pair is calculated +1 if the point is closer to a positive than to a negative part of a pair, and -1 otherwise.
- `filter` (DiscoverRequestFilter, optional) — Look only for points which satisfies this conditions
- `params` (DiscoverRequestParams, optional) — Additional search params
- `offset` (integer, optional, nullable) — Offset of the first result to return. May be used to paginate results. Note: large offset values may cause performance issues.
- `with_payload` (DiscoverRequestWithPayload, optional) — Select which payload to return with the response. Default is false.
- `with_vector` (DiscoverRequestWithVector, optional) — Options for specifying which vectors to include into response. Default is false.
- `using` (DiscoverRequestUsing, optional) — Define which vector to use for recommendation, if not specified - try to use default vector
- `lookup_from` (DiscoverRequestLookupFrom, optional) — The location used to lookup vectors. If not specified - use current collection. Note: the other collection should have the same vector size as the current collection

### ReadConsistency

Read consistency parameter Defines how many replicas should be queried to get the result * `N` - send N random request and return points, which present on all of them * `majority` - send N/2+1 random request and return points, which present on all of them * `quorum` - send requests to all nodes and return points which present on majority of them * `all` - send requests to all nodes and return points which present on all of them Default value is `Factor(1)`

### CollectionsCollectionNamePointsDiscoverBatchPostResponsesContentApplicationJsonSchemaUsage

### ScoredPoint

Search result

- `id` (ExtendedPointId, required) — Type, used for specifying point ID in user interface
- `version` (uint64, required) — Point version
- `score` (double, required) — Points vector distance to the query vector
- `payload` (ScoredPointPayload, optional) — Payload - values assigned to the point
- `vector` (ScoredPointVector, optional) — Vector of the point
- `shard_key` (ScoredPointShardKey, optional) — Shard Key
- `order_value` (ScoredPointOrderValue, optional) — Order-by value

### DiscoverRequestShardKey

Specify in which shards to look for the points, if not specified - look in all shards

### DiscoverRequestTarget

Look for vectors closest to this. When using the target (with or without context), the integer part of the score represents the rank with respect to the context, while the decimal part of the score relates to the distance to the target.

### ContextExamplePair

- `positive` (RecommendExample, required)
- `negative` (RecommendExample, required)

### DiscoverRequestFilter

Look only for points which satisfies this conditions

### DiscoverRequestParams

Additional search params

### DiscoverRequestWithPayload

Select which payload to return with the response. Default is false.

### DiscoverRequestWithVector

Options for specifying which vectors to include into response. Default is false.

### DiscoverRequestUsing

Define which vector to use for recommendation, if not specified - try to use default vector

### DiscoverRequestLookupFrom

The location used to lookup vectors. If not specified - use current collection. Note: the other collection should have the same vector size as the current collection

### 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)

### ExtendedPointId

Type, used for specifying point ID in user interface

### ScoredPointPayload

Payload - values assigned to the point

### ScoredPointVector

Vector of the point

### ScoredPointShardKey

Shard Key

### ScoredPointOrderValue

Order-by value

### RecommendExample

### 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

### SearchParams

Additional parameters of the search

- `hnsw_ef` (integer, optional, nullable) — Params relevant to HNSW index Size of the beam in a beam-search. Larger the value - more accurate the result, more time required for search.
- `exact` (boolean, optional, default: false) — Search without approximation. If set to true, search may run long but with exact results.
- `quantization` (SearchParamsQuantization, optional) — Quantization params
- `indexed_only` (boolean, optional, default: false) — If enabled, the engine will only perform search among indexed or small segments. Using this option prevents slow searches in case of delayed index, but does not guarantee that all uploaded vectors will be included in search results

### LookupLocation

Defines a location to use for looking up the vector. Specifies collection and vector field name.

- `collection` (string, required) — Name of the collection used for lookup
- `vector` (string, optional, nullable) — Optional name of the vector field within the collection. If not provided, the default vector field will be used.
- `shard_key` (LookupLocationShardKey, optional) — Specify in which shards to look for the points, if not specified - look in all shards

### SparseVector

Sparse vector structure

- `indices` (list of uint, required) — Indices must be unique
- `values` (list of double, required) — Values and indices must be the same length

### 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

### SearchParamsQuantization

Quantization params

### LookupLocationShardKey

Specify in which shards to look for the points, if not specified - look in all shards

### MinShould

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

### QuantizationSearchParams

Additional parameters of the search

- `ignore` (boolean, optional, default: false) — If true, quantized vectors are ignored. Default is false.
- `rescore` (boolean, optional, nullable) — If true, use original vectors to re-score top-k results. Might require more time in case if original vectors are stored on disk. If not set, qdrant decides automatically apply rescoring or not.
- `oversampling` (double, optional, nullable) — Oversampling factor for quantization. Default is 1.0. Defines how many extra vectors should be pre-selected using quantized index, and then re-scored using original vectors. For example, if `oversampling` is 2.4 and `limit` is 100, then 240 vectors will be pre-selected using quantized index, and then top-100 will be returned after re-scoring.

### Condition

### 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

### 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
{
  "searches": [
    {
      "limit": 1
    }
  ]
}
```

**Response**

```json
{
  "usage": {
    "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
  },
  "time": 0.002,
  "status": "ok",
  "result": [
    [
      {
        "id": 42,
        "version": 3,
        "score": 0.75,
        "payload": {},
        "vector": {},
        "shard_key": "region_1",
        "order_value": 42
      }
    ]
  ]
}
```

**SDK Code**

```rust
use qdrant_client::qdrant::{
    vector_example::Example, ContextExamplePairBuilder, DiscoverBatchPointsBuilder,
    DiscoverPointsBuilder,
};
use qdrant_client::Qdrant;

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

let discover_points = DiscoverBatchPointsBuilder::new(
    "{collection_name}",
    vec![
        DiscoverPointsBuilder::new(
            "{collection_name}",
            vec![
                ContextExamplePairBuilder::default()
                    .positive(Example::Id(100.into()))
                    .negative(Example::Id(718.into()))
                    .build(),
                ContextExamplePairBuilder::default()
                    .positive(Example::Id(200.into()))
                    .negative(Example::Id(300.into()))
                    .build(),
            ],
            10,
        )
        .build(),
        DiscoverPointsBuilder::new(
            "{collection_name}",
            vec![
                ContextExamplePairBuilder::default()
                    .positive(Example::Id(342.into()))
                    .negative(Example::Id(213.into()))
                    .build(),
                ContextExamplePairBuilder::default()
                    .positive(Example::Id(100.into()))
                    .negative(Example::Id(200.into()))
                    .build(),
            ],
            10,
        )
        .build(),
    ],
);

client.discover_batch(&discover_points.build()).await?;

```

```python
from qdrant_client import QdrantClient, models

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

discover_queries = [
    models.DiscoverRequest(
        target=[0.2, 0.1, 0.9, 0.7],
        context=[
            models.ContextExamplePair(
                positive=100,
                negative=718,
            ),
            models.ContextExamplePair(
                positive=200,
                negative=300,
            ),
        ],
        limit=10,
    ),
    models.DiscoverRequest(
        target=[0.5, 0.3, 0.2, 0.3],
        context=[
            models.ContextExamplePair(
                positive=342,
                negative=213,
            ),
            models.ContextExamplePair(
                positive=100,
                negative=200,
            ),
        ],
        limit=5,
    ),
]

client.discover_batch("{collection_name}", discover_queries)

```

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

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

const searches = [
    {
        target: [0.2, 0.1, 0.9, 0.7],
        context: [
            {
                positive: 100,
                negative: 718,
            },
            {
                positive: 200,
                negative: 300,
            },
        ],
        limit: 10,
    },
    {
        target: [0.5, 0.3, 0.2, 0.3],
        context: [
            {
                positive: 342,
                negative: 213,
            },
            {
                positive: 100,
                negative: 200,
            },
        ],
        limit: 5,
    },
];

client.discoverBatchPoints("{collection_name}", {
    searches,
});

```

```go
package client

import (
	"context"
	"fmt"

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

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

	results, err := client.QueryBatch(context.Background(), &qdrant.QueryBatchPoints{
		CollectionName: "{collection_name}",
		QueryPoints: []*qdrant.QueryPoints{
			{
				CollectionName: "{collection_name}",
				Query: qdrant.NewQueryDiscover(&qdrant.DiscoverInput{
					Target: qdrant.NewVectorInput(0.2, 0.1, 0.9, 0.7),
					Context: &qdrant.ContextInput{
						Pairs: []*qdrant.ContextInputPair{{
							Positive: qdrant.NewVectorInputID(qdrant.NewIDNum(100)),
							Negative: qdrant.NewVectorInputID(qdrant.NewIDNum(718)),
						}, {
							Positive: qdrant.NewVectorInputID(qdrant.NewIDNum(200)),
							Negative: qdrant.NewVectorInputID(qdrant.NewIDNum(300)),
						}},
					},
				}),
			},
			{
				CollectionName: "{collection_name}",
				Query: qdrant.NewQueryDiscover(&qdrant.DiscoverInput{
					Target: qdrant.NewVectorInput(0.5, 0.3, 0.2, 0.3),
					Context: &qdrant.ContextInput{
						Pairs: []*qdrant.ContextInputPair{{
							Positive: qdrant.NewVectorInputID(qdrant.NewIDNum(342)),
							Negative: qdrant.NewVectorInputID(qdrant.NewIDNum(213)),
						}, {
							Positive: qdrant.NewVectorInputID(qdrant.NewIDNum(100)),
							Negative: qdrant.NewVectorInputID(qdrant.NewIDNum(200)),
						}},
					},
				}),
			},
		},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println("Results: ", results)
}

```

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

import java.util.Arrays;
import java.util.List;

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

import io.qdrant.client.grpc.Points.ContextExamplePair;
import io.qdrant.client.grpc.Points.DiscoverPoints;
import io.qdrant.client.grpc.Points.TargetVector;
import io.qdrant.client.grpc.Points.VectorExample;

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

List <DiscoverPoints> discoverPoints = Arrays.asList(
    DiscoverPoints.newBuilder()
    .setCollectionName("{collection_name}")
    .setTarget(
        TargetVector.newBuilder()
        .setSingle(
            VectorExample.newBuilder()
            .setVector(vector(
                0.2 f,
                0.1 f,
                0.9 f,
                0.7 f))
            .build()))
    .addAllContext(
        List.of(
            ContextExamplePair.newBuilder()
            .setPositive(VectorExample
                .newBuilder()
                .setId(id(100)))
            .setNegative(VectorExample
                .newBuilder()
                .setId(id(718)))
            .build(),
            ContextExamplePair.newBuilder()
            .setPositive(VectorExample
                .newBuilder()
                .setId(id(200)))
            .setNegative(VectorExample
                .newBuilder()
                .setId(id(300)))
            .build()))
    .setLimit(10)
    .build(),
    DiscoverPoints.newBuilder()
    .setCollectionName("{collection_name}")
    .setTarget(
        TargetVector.newBuilder()
        .setSingle(
            VectorExample.newBuilder()
            .setVector(vector(
                0.5 f, 0.3 f, 0.2 f, 0.3 f))
            .build()))
    .addAllContext(
        List.of(
            ContextExamplePair.newBuilder()
            .setPositive(VectorExample
                .newBuilder()
                .setId(id(342)))
            .setNegative(VectorExample
                .newBuilder()
                .setId(id(213)))
            .build(),
            ContextExamplePair.newBuilder()
            .setPositive(VectorExample
                .newBuilder()
                .setId(id(100)))
            .setNegative(VectorExample
                .newBuilder()
                .setId(id(200)))
            .build()))
    .setLimit(10)
    .build());
client.discoverBatchAsync("{collection_name}", discoverPoints, null);

```

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

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

var discoverPoints = new List<DiscoverPoints>
{
    new DiscoverPoints
    {
        CollectionName = "{collection_name}",
        Target = new TargetVector
        {
            Single = new VectorExample { Vector = new float[] { 0.2f, 0.1f, 0.9f, 0.7f }, }
        },
        Context =
        {
            new ContextExamplePair()
            {
                Positive = new VectorExample { Id = 100 },
                Negative = new VectorExample { Id = 718 }
            },
            new ContextExamplePair()
            {
                Positive = new VectorExample { Id = 200 },
                Negative = new VectorExample { Id = 300 }
            }
        },
        Limit = 10
    },
    new DiscoverPoints
    {
        CollectionName = "{collection_name}",
        Target = new TargetVector
        {
            Single = new VectorExample { Vector = new float[] { 0.5f, 0.3f, 0.2f, 0.3f }, }
        },
        Context =
        {
            new ContextExamplePair()
            {
                Positive = new VectorExample { Id = 342 },
                Negative = new VectorExample { Id = 213 }
            },
            new ContextExamplePair()
            {
                Positive = new VectorExample { Id = 100 },
                Negative = new VectorExample { Id = 200 }
            }
        },
        Limit = 10
    }
};
await client.DiscoverBatchAsync("{collection_name}", discoverPoints);

```

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

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

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

request = Net::HTTP::Post.new(url)
request["api-key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"searches\": [\n    {\n      \"limit\": 1\n    }\n  ]\n}"

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

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

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'http://localhost:6333/collections/collection_name/points/discover/batch', [
  'body' => '{
  "searches": [
    {
      "limit": 1
    }
  ]
}',
  '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 = ["searches": [["limit": 1]]] as [String : Any]

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

let request = NSMutableURLRequest(url: NSURL(string: "http://localhost:6333/collections/collection_name/points/discover/batch")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
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()
```