Qdrant PHP Client

Vector search in PHP: the boundary between model, application and database.

A PHP client for Qdrant collections, points and filtered search through a replaceable HTTP transport. Compatibility, response contracts and indexing limits.

PHP · Qdrant · Vector Search · HTTP

Role

Library author: PHP API, HTTP transport, error handling, examples and tests.

Open source · September 2026. This case examines public commit 3b1c8ab0. Source links and implementation limits refer to that revision.

Vector search inside a PHP application

I built Qdrant PHP Client as a separate library for communicating with a vector database over HTTP. Its responsibility is to connect PHP code to Qdrant collections, points and search. Embedding generation, domain models and access rules remain in the application. This boundary allows an existing backend to adopt vector search gradually while keeping its business logic in place.

My role is the library author: public API, transport, error handling, examples and tests. The Composer manifest declares PHP ^7.2|^8.0 and the cURL and JSON extensions.

From application data to search results
  1. PHP applicationEmbedding model, documents and access rules
  2. Qdrant PHP ClientCollections, points, filters and HTTP requests
  3. QdrantVector storage and nearest-neighbour search

The embedding model sits outside the client. The application receives identifiers, similarity scores and the requested payload.

Workflow: related articles in one language

An article catalog first creates an embedding and stores it as the point vector, with a language and article identifier in its payload. The query must use the same embedding model and the collection dimension. A language filter travels with the search request, so Qdrant can apply it before returning the limited set of results.

This standalone read example expects an already populated case_articles collection using four dimensions and Cosine distance. Its four numbers are a synthetic vector for checking the API, not a real embedding or evidence of semantic quality. Points need a string locale payload field.

<?php
require __DIR__ . '/vendor/autoload.php';

use Tenqz\Qdrant\QdrantClient;
use Tenqz\Qdrant\Transport\Infrastructure\Factory\CurlHttpClientFactory;

$http = (new CurlHttpClientFactory())->create('127.0.0.1', 6333);
$client = new QdrantClient($http);
$filter = ['must' => [
    ['key' => 'locale', 'match' => ['value' => 'ru']],
]];

$response = $client->search(
    'case_articles', [0.1, 0.2, 0.3, 0.4], 5, $filter
);
foreach ($response['result'] as $hit) {
    echo $hit['id'] . ' ' . $hit['score'] . PHP_EOL;
}

The client returns the complete Qdrant JSON response, so hits live under $response['result']. A similarity score is not a probability of relevance. Building article links and checking whether the current reader may access them remain application responsibilities.

Decision 1: a small API over an explicit transport

QdrantClient receives an HttpClientInterface. The facade builds the method, path and request body; the cURL implementation handles the connection, JSON and API-key header. Tests can replace the transport and inspect requests without starting a database. Applications can use the same interface to introduce their own HTTP adapter.

Decision 2: preserve the provider response

Returned arrays retain the API response and its metadata. This makes source data inspectable and leaves room for application-specific processing, while requiring callers to validate nested structures. There is no DTO for every filter and result combination. The application needs a deliberate mapping between these arrays and its own domain objects.

Decision 3: distinguish request failures

The transport defines NetworkException, HttpException and SerializationException. A caller can distinguish a connection failure from an API rejection or unreadable JSON. JSON parsing precedes the HTTP-status check, so an HTML error page from a proxy can surface as a serialization error. There are no automatic retries; callers own the policy for repeating a write.

An inspectable result

The result is a Composer package for collection creation, point uploads, filtered search, recommendations and batch search. The facade source exposes the exact request bodies. The HTTP transport shows timeout and response handling. The repository contains unit tests that check methods, endpoints and parameters through a mocked transport.

The unit tests cover request parameters, operation routing and response forwarding through the transport interface. Retrieval quality is evaluated separately with application-specific queries and relevance judgments; throughput also depends on server configuration and data.

Implementation limits

The client does not compute embeddings, synchronize an index with the primary database or automatically expose every newer Qdrant feature. For example, upsertPoints has no parameter for waiting until a write is applied. An accepted upload must not be treated as proof that a following read will immediately see it. Dimensions, collection lifecycle and deletion consistency belong to the integration.

Try it

Install the package and prepare a separate Qdrant instance. The repository example creates and populates collections, so use disposable data. Run the read example above against your prepared collection.

composer require tenqz/qdrant

The Qdrant PHP Client article covers installation and error handling. Browse the source and history in tenqz/qdrant.

GitHub Setup guide

Let's make the complex understandable.

Architecture, engineering leadership, and AI in development — when the system is too important to simplify, and too expensive not to own.

LinkedIn Telegram Email