Home / AI & GenAI / Vector Databases

AI & GenAICheat sheetBeginner

Qdrant cheat sheet: collections, points, filters, queries and admin calls with curl and the Python client

The Qdrant calls you need day to day - create and inspect collections, upsert and delete points, filter syntax, the Query API, scroll, count, aliases, snapshots and health endpoints - side by side in curl and Python.

Deepak Bhardwaj3 min read

Applies toQdrant 1.10+ (Query API), qdrant-client for Python; ports 6333 (REST) and 6334 (gRPC)

Connect

export Q=http://qdrant:6333
export H="api-key: $QDRANT_API_KEY"
curl -s -H "$H" $Q/ | jq            # version info
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://qdrant:6333", api_key=API_KEY, prefer_grpc=True)

Collections

curl -s -H "$H" $Q/collections | jq                       # list
curl -s -H "$H" $Q/collections/kb_chunks | jq '.result'   # status, counts, config
curl -X DELETE -H "$H" $Q/collections/kb_chunks           # delete
client.create_collection(
    "kb_chunks",
    vectors_config=models.VectorParams(size=1536, distance=models.Distance.COSINE),
)
# Named vectors (e.g. dense + title vector) in one collection
client.create_collection(
    "kb_multi",
    vectors_config={"content": models.VectorParams(size=1536, distance=models.Distance.COSINE),
                    "title":   models.VectorParams(size=384,  distance=models.Distance.COSINE)},
    sparse_vectors_config={"bm25": models.SparseVectorParams(modifier=models.Modifier.IDF)},
)
client.collection_exists("kb_chunks")

Distances: Cosine, Dot, Euclid, Manhattan.

Points

Point IDs must be unsigned integers or UUIDs. A string like doc42#0 isn't valid; derive a UUID from it:

import uuid
pid = str(uuid.uuid5(uuid.NAMESPACE_URL, "doc42#0"))      # deterministic

client.upsert("kb_chunks", points=[
    models.PointStruct(id=pid, vector=emb, payload={"doc_id": "doc42", "tenant_id": "t1", "year": 2026}),
])
client.retrieve("kb_chunks", ids=[pid], with_payload=True)
client.set_payload("kb_chunks", payload={"reviewed": True}, points=[pid])          # merge
client.overwrite_payload("kb_chunks", payload={"doc_id": "doc42"}, points=[pid])   # replace
client.delete_payload("kb_chunks", keys=["reviewed"], points=[pid])

Delete:

client.delete("kb_chunks", points_selector=models.PointIdsList(points=[pid]))
client.delete("kb_chunks", points_selector=models.FilterSelector(
    filter=models.Filter(must=[models.FieldCondition(key="doc_id", match=models.MatchValue(value="doc42"))])))

Filters

Need Condition
Equals FieldCondition(key="tenant_id", match=MatchValue(value="t1"))
Any of match=MatchAny(any=["jira", "confluence"])
None of match=MatchExcept(**{"except": ["hr"]})
Range range=Range(gte=2025, lt=2027)
Date range range=DatetimeRange(gte="2026-01-01T00:00:00Z")
Full text (needs text index) match=MatchText(text="vpn timeout")
Field missing/empty IsEmptyCondition(is_empty=PayloadField(key="owner"))
Null IsNullCondition(is_null=PayloadField(key="owner"))
By ID HasIdCondition(has_id=[pid1, pid2])
Nested array of objects NestedCondition(nested=Nested(key="authors", filter=...))

Combine with must (AND), should (OR), must_not (NOT):

f = models.Filter(
    must=[models.FieldCondition(key="tenant_id", match=models.MatchValue(value="t1"))],
    should=[models.FieldCondition(key="source", match=models.MatchValue(value="jira")),
            models.FieldCondition(key="source", match=models.MatchValue(value="confluence"))],
    must_not=[models.FieldCondition(key="status", match=models.MatchValue(value="archived"))],
)

As JSON in REST: {"must":[{"key":"tenant_id","match":{"value":"t1"}}]}.

Query API

res = client.query_points(
    "kb_chunks", query=qvec, limit=10, query_filter=f,
    with_payload=["doc_id", "title"],
    search_params=models.SearchParams(hnsw_ef=128),
    score_threshold=0.3,
)
for p in res.points:
    print(p.id, round(p.score, 3), p.payload)
curl -X POST -H "$H" -H "Content-Type: application/json" $Q/collections/kb_chunks/points/query \
  -d '{"query": [0.01, 0.02, ...], "limit": 10, "filter": {"must":[{"key":"tenant_id","match":{"value":"t1"}}]}, "with_payload": true}'

Hybrid (dense + sparse) with fusion:

client.query_points("kb_multi",
    prefetch=[models.Prefetch(query=dense_vec, using="content", limit=40),
              models.Prefetch(query=models.SparseVector(indices=idx, values=vals), using="bm25", limit=40)],
    query=models.FusionQuery(fusion=models.Fusion.RRF), limit=10)

Exact search for recall checks: search_params=models.SearchParams(exact=True).

Scroll and count

points, next_offset = client.scroll("kb_chunks", scroll_filter=f, limit=500, with_vectors=False)
client.count("kb_chunks", count_filter=f, exact=True).count

Payload indexes

client.create_payload_index("kb_chunks", "tenant_id",
    field_schema=models.KeywordIndexParams(type="keyword", is_tenant=True))
client.create_payload_index("kb_chunks", "year", field_schema=models.PayloadSchemaType.INTEGER)

Aliases (zero-downtime switch)

client.update_collection_aliases(change_aliases_operations=[
    models.DeleteAliasOperation(delete_alias=models.DeleteAlias(alias_name="kb_live")),
    models.CreateAliasOperation(create_alias=models.CreateAlias(collection_name="kb_chunks_v2", alias_name="kb_live")),
])

Both operations run atomically. Applications should always query the alias.

Snapshots

curl -X POST -H "$H" $Q/collections/kb_chunks/snapshots               # create
curl -s -H "$H" $Q/collections/kb_chunks/snapshots | jq               # list

In a cluster, collection snapshots are per node; take them on each peer.

Health and diagnostics

Endpoint Use
/healthz, /livez, /readyz Probes (readyz waits for consensus/shards)
/metrics Prometheus metrics
/telemetry Detailed internal stats (JSON)
/cluster Peers and Raft state
/collections/{name}/cluster Shard placement and transfers

Common errors

Error Cause
Wrong input: Vector dimension error Embedding size ≠ collection size
Unable to parse UUID String point ID that isn't a UUID
Bad request: Index required but not found for "tenant_id" Strict mode requires payload index for filtered field
403 Forbidden Missing/wrong API key, or read-only key used for writes

Prevent mistakes next time

  • Use deterministic UUIDs from your own chunk IDs.
  • Query aliases, not collections.
  • Create payload indexes with the collection.
  • Keep with_vectors=False in production reads.