Hanzo
Services

Hanzo Search

AI-powered search engine built in Rust with sub-50ms response times, hybrid semantic/full-text search, faceted filtering, geo search, chat completions, and multi-language support.

Hanzo Search

Hanzo Search is a high-performance, AI-powered search engine built in Rust. Based on Meilisearch, it delivers sub-50ms query responses with typo tolerance, hybrid semantic and full-text search, faceted filtering, geo search, and built-in chat completions powered by LLMs. Version 1.36.0.

Endpoint: search.hanzo.ai Gateway: api.hanzo.ai/v1/search/* Default Port: 7700 Source: github.com/hanzoai/search

Features

  • Hybrid Search -- Combine semantic vector search with full-text keyword search using configurable semantic ratios for maximum relevance
  • Search-as-you-type -- Sub-50ms query responses with prefix matching for instant, interactive search
  • Typo Tolerance -- Return relevant results even when queries contain typos and misspellings
  • Faceted Filtering -- Build rich, filterable search interfaces with custom facets and facet search
  • Geo Search -- Filter and sort documents by geographic coordinates and radius
  • Multi-Language -- Optimized tokenization for Chinese, Japanese, Hebrew, Thai, and Latin-alphabet languages
  • Custom Ranking Rules -- Define attribute-level ranking with words, typo, proximity, attribute, sort, and exactness rules
  • Synonyms and Stop Words -- Configure synonym mappings and stop word lists per index
  • Chat Completions -- OpenAI-compatible chat endpoint that searches your indexes and generates answers via LLMs
  • Multi-Index Search -- Federated search across multiple indexes in a single request
  • Multi-Tenancy -- Fine-grained API key permissions and tenant token isolation for SaaS applications
  • Embedder Support -- Built-in integrations with OpenAI, Ollama, Hugging Face, and custom REST embedders
  • Prometheus Metrics -- Expose search and indexing metrics at /metrics
  • S3 Snapshots -- Stream database snapshots directly to S3-compatible storage

Architecture

+------------------------------------------------------------------+
|                        HANZO SEARCH                               |
+------------------------------------------------------------------+
|                                                                    |
|  +------------------------------+  +----------------------------+ |
|  |        HTTP API (actix)      |  |     Chat Completions       | |
|  |  +--------+ +--------+      |  |  +--------+ +----------+   | |
|  |  | Search | | Index  |      |  |  | OpenAI | | Streaming|   | |
|  |  | Routes | | Routes |      |  |  | Compat | |   SSE    |   | |
|  |  +--------+ +--------+      |  |  +--------+ +----------+   | |
|  +------------------------------+  +----------------------------+ |
|                                                                    |
|  +------------------------------+  +----------------------------+ |
|  |     Index Scheduler          |  |      Vector Engine          | |
|  |  +--------+ +--------+      |  |  +--------+ +----------+   | |
|  |  | Tasks  | | Batches|      |  |  | HNSW   | | Embedders|   | |
|  |  +--------+ +--------+      |  |  +--------+ +----------+   | |
|  +------------------------------+  +----------------------------+ |
|                                                                    |
|  +------------------------------+  +----------------------------+ |
|  |     milli (Core Engine)      |  |      Auth & Tenancy        | |
|  |  +--------+ +--------+      |  |  +--------+ +----------+   | |
|  |  | BM25   | | Filters|      |  |  | API    | | Tenant   |   | |
|  |  | Ranking| | & Facets|     |  |  | Keys   | | Tokens   |   | |
|  |  +--------+ +--------+      |  |  +--------+ +----------+   | |
|  +------------------------------+  +----------------------------+ |
|                                                                    |
|  +--------------------------------------------------------------+ |
|  |                  LMDB Storage (heed)                          | |
|  +--------------------------------------------------------------+ |
+------------------------------------------------------------------+

Core crates:

  • meilisearch -- HTTP server, routes, authentication, search queue
  • milli -- Core search engine: indexing, ranking, filtering, vector search, hybrid search
  • index-scheduler -- Asynchronous task scheduling and batch processing
  • meilisearch-auth -- API key management and tenant token validation
  • filter-parser -- Filter expression parser for faceted search
  • meilisearch-types -- Shared types, settings, and error codes

Quick Start

Docker

docker run -d \
  --name hanzo-search \
  -p 7700:7700 \
  -v hanzo_search_data:/meili_data \
  hanzoai/search:latest \
  hanzo-search --master-key="YOUR_MASTER_KEY"

From Source

git clone https://github.com/hanzoai/search.git && cd search
cargo build --release
./target/release/meilisearch --master-key="YOUR_MASTER_KEY"

curl

# Add documents
curl -X POST 'http://localhost:7700/indexes/products/documents' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '[
    { "id": 1, "title": "Hanzo Agent SDK", "category": "AI", "price": 0 },
    { "id": 2, "title": "Hanzo MCP Server", "category": "Infrastructure", "price": 49 },
    { "id": 3, "title": "Hanzo LLM Gateway", "category": "AI", "price": 99 }
  ]'

# Search
curl 'http://localhost:7700/indexes/products/search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{ "q": "agent" }'

Python

import requests

BASE = "http://localhost:7700"
HEADERS = {
    "Authorization": "Bearer YOUR_MASTER_KEY",
    "Content-Type": "application/json",
}

# Index documents
requests.post(f"{BASE}/indexes/products/documents", headers=HEADERS, json=[
    {"id": 1, "title": "Hanzo Agent SDK", "category": "AI"},
    {"id": 2, "title": "Hanzo MCP Server", "category": "Infrastructure"},
])

# Search
resp = requests.post(f"{BASE}/indexes/products/search", headers=HEADERS, json={
    "q": "agent",
    "limit": 10,
    "attributesToHighlight": ["title"],
})
print(resp.json()["hits"])

JavaScript / TypeScript

import { MeiliSearch } from '@hanzo/search'

const client = new MeiliSearch({
  host: 'http://localhost:7700',
  apiKey: 'YOUR_MASTER_KEY',
})

// Index documents
const index = client.index('products')
await index.addDocuments([
  { id: 1, title: 'Hanzo Agent SDK', category: 'AI' },
  { id: 2, title: 'Hanzo MCP Server', category: 'Infrastructure' },
])

// Search
const results = await index.search('agent', {
  limit: 10,
  attributesToHighlight: ['title'],
})
console.log(results.hits)

Indexing

Create an Index

Indexes are created automatically when you add documents, or explicitly:

curl -X POST 'http://localhost:7700/indexes' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  -H 'Content-Type: application/json' \
  --data-binary '{ "uid": "products", "primaryKey": "id" }'

Add Documents

Documents are JSON objects. Hanzo Search accepts arrays, NDJSON, and CSV formats. The maximum payload size defaults to 100 MB.

# JSON array
curl -X POST 'http://localhost:7700/indexes/products/documents' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary @products.json

# NDJSON
curl -X POST 'http://localhost:7700/indexes/products/documents' \
  -H 'Content-Type: application/x-ndjson' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary @products.ndjson

Update Documents

Use PUT to replace documents or PATCH (via update endpoint) to partially update:

curl -X PUT 'http://localhost:7700/indexes/products/documents' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  -H 'Content-Type: application/json' \
  --data-binary '[{ "id": 1, "price": 29.99 }]'

Searching

curl -X POST 'http://localhost:7700/indexes/products/search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "q": "agent sdk",
    "limit": 20,
    "offset": 0,
    "attributesToRetrieve": ["id", "title", "category"],
    "attributesToHighlight": ["title"],
    "attributesToCrop": ["description"],
    "cropLength": 50,
    "showMatchesPosition": true,
    "showRankingScore": true
  }'

Combine keyword and semantic search by configuring an embedder and using hybrid:

curl -X POST 'http://localhost:7700/indexes/products/search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "q": "tools for building AI agents",
    "hybrid": {
      "semanticRatio": 0.5,
      "embedder": "default"
    }
  }'

A semanticRatio of 0.0 uses pure keyword search, 1.0 uses pure vector search, and 0.5 blends both equally.

Search across multiple indexes in a single request:

curl -X POST 'http://localhost:7700/multi-search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "queries": [
      { "indexUid": "products", "q": "agent" },
      { "indexUid": "docs", "q": "agent", "limit": 5 }
    ]
  }'

Search within facet values for building autocomplete on filters:

curl -X POST 'http://localhost:7700/indexes/products/facet-search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "facetName": "category",
    "facetQuery": "infra"
  }'

Filtering

Declare filterable attributes in settings, then use filter expressions in search:

# Configure filterable attributes
curl -X PATCH 'http://localhost:7700/indexes/products/settings' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "filterableAttributes": ["category", "price", "tags", "_geo"]
  }'

# Filter by exact match
curl -X POST 'http://localhost:7700/indexes/products/search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "q": "sdk",
    "filter": "category = AI AND price < 100"
  }'

# Complex filters with OR and nested expressions
curl -X POST 'http://localhost:7700/indexes/products/search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "q": "sdk",
    "filter": "(category = AI OR category = Infrastructure) AND price >= 0"
  }'

Filter and sort by geographic location:

# Filter within radius (meters)
curl -X POST 'http://localhost:7700/indexes/stores/search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "filter": "_geoRadius(37.7749, -122.4194, 5000)",
    "sort": ["_geoPoint(37.7749, -122.4194):asc"]
  }'

# Bounding box filter
curl -X POST 'http://localhost:7700/indexes/stores/search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "filter": "_geoBoundingBox([37.8, -122.5], [37.7, -122.3])"
  }'

Documents must include a _geo field with lat and lng properties.

Faceted Filtering

Return facet value counts alongside search results:

curl -X POST 'http://localhost:7700/indexes/products/search' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "q": "",
    "facets": ["category", "tags"],
    "filter": "price < 100"
  }'

AI Features

Chat Completions

Hanzo Search includes an OpenAI-compatible chat completions endpoint. The LLM automatically searches your indexes to answer questions, with streaming SSE responses:

curl -X POST 'http://localhost:7700/chats/default/chat/completions' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "model": "gpt-4o",
    "messages": [
      { "role": "user", "content": "What AI tools do you have?" }
    ],
    "stream": true
  }'

The chat system:

  • Automatically calls _meiliSearchInIndex to find relevant documents
  • Reports search progress via _meiliSearchProgress tool calls
  • Returns source citations via _meiliSearchSources
  • Supports conversation context via _meiliAppendConversationMessage

Embedder Configuration

Configure embedders per index for semantic and hybrid search:

# OpenAI embedder
curl -X PATCH 'http://localhost:7700/indexes/products/settings' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "embedders": {
      "default": {
        "source": "openAi",
        "apiKey": "sk-...",
        "model": "text-embedding-3-small",
        "documentTemplate": "A product titled {{doc.title}} in category {{doc.category}}"
      }
    }
  }'

# Ollama embedder (self-hosted)
curl -X PATCH 'http://localhost:7700/indexes/products/settings' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "embedders": {
      "local": {
        "source": "ollama",
        "url": "http://localhost:11434/api/embeddings",
        "model": "nomic-embed-text"
      }
    }
  }'

# Hugging Face embedder (runs locally)
curl -X PATCH 'http://localhost:7700/indexes/products/settings' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "embedders": {
      "hf": {
        "source": "huggingFace",
        "model": "BAAI/bge-base-en-v1.5"
      }
    }
  }'

Supported embedder sources: openAi, ollama, huggingFace, rest (custom endpoint), userProvided (bring your own vectors), and composite (different embedders for indexing vs search).

Binary Quantization

Reduce vector storage by enabling quantization on embedders:

{
  "embedders": {
    "default": {
      "source": "openAi",
      "binaryQuantized": true
    }
  }
}

Configuration

Environment Variables

VariableDefaultDescription
MEILI_DB_PATH./data.msDatabase storage directory
MEILI_HTTP_ADDRlocalhost:7700Bind address and port
MEILI_MASTER_KEY--Master API key (required in production)
MEILI_ENVdevelopmentdevelopment or production
MEILI_HTTP_PAYLOAD_SIZE_LIMIT100 MBMaximum request payload size
MEILI_LOG_LEVELINFOLog level: OFF, ERROR, WARN, INFO, DEBUG, TRACE
MEILI_NO_ANALYTICSfalseDisable telemetry
MEILI_SCHEDULE_SNAPSHOTfalseEnable periodic snapshots
MEILI_SNAPSHOT_DIRsnapshots/Snapshot storage directory
MEILI_DUMP_DIRdumps/Dump storage directory

S3 Snapshot Configuration

Stream snapshots to S3-compatible storage (Hanzo S3, AWS S3):

VariableDescription
MEILI_S3_BUCKET_URLS3 endpoint URL
MEILI_S3_BUCKET_REGIONBucket region
MEILI_S3_BUCKET_NAMEBucket name
MEILI_S3_ACCESS_KEYAccess key
MEILI_S3_SECRET_KEYSecret key

Index Settings

curl -X PATCH 'http://localhost:7700/indexes/products/settings' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "searchableAttributes": ["title", "description", "category"],
    "filterableAttributes": ["category", "price", "tags", "_geo"],
    "sortableAttributes": ["price", "created_at"],
    "rankingRules": ["words", "typo", "proximity", "attribute", "sort", "exactness"],
    "distinctAttribute": "sku",
    "synonyms": {
      "phone": ["smartphone", "mobile"],
      "laptop": ["notebook", "computer"]
    },
    "stopWords": ["the", "a", "an", "is"],
    "typoTolerance": {
      "enabled": true,
      "minWordSizeForTypos": { "oneTypo": 5, "twoTypos": 9 }
    },
    "pagination": { "maxTotalHits": 1000 },
    "faceting": { "maxValuesPerFacet": 100 }
  }'

API Key Management

Create scoped API keys for multi-tenant access:

# Create a search-only key for a specific index
curl -X POST 'http://localhost:7700/keys' \
  -H 'Authorization: Bearer YOUR_MASTER_KEY' \
  --data-binary '{
    "description": "Products search key",
    "actions": ["search"],
    "indexes": ["products"],
    "expiresAt": "2027-01-01T00:00:00Z"
  }'

SDKs

LanguagePackageRepository
JavaScript / TypeScript@hanzo/searchhanzoai/search-js
Pythonhanzo-searchhanzoai/search-python
Gogithub.com/hanzoai/search-gohanzoai/search-go
Rusthanzo-searchhanzoai/search-rust

API Reference

Indexes

MethodPathDescription
GET/indexesList all indexes
POST/indexesCreate an index
GET/indexes/{uid}Get index details
PATCH/indexes/{uid}Update index (primary key)
DELETE/indexes/{uid}Delete an index
POST/swap-indexesSwap two indexes atomically

Documents

MethodPathDescription
POST/indexes/{uid}/documentsAdd or replace documents
PUT/indexes/{uid}/documentsAdd or update documents
GET/indexes/{uid}/documentsList documents
GET/indexes/{uid}/documents/{id}Get a single document
DELETE/indexes/{uid}/documentsDelete documents by filter or IDs
MethodPathDescription
POST/indexes/{uid}/searchSearch an index
GET/indexes/{uid}/searchSearch an index (GET)
POST/indexes/{uid}/facet-searchSearch facet values
POST/multi-searchFederated multi-index search

Chat

MethodPathDescription
GET/chatsList chat workspaces
GET/chats/{uid}Get workspace details
DELETE/chats/{uid}Delete a workspace
POST/chats/{uid}/chat/completionsChat completion with search

Settings

MethodPathDescription
GET/indexes/{uid}/settingsGet all settings
PATCH/indexes/{uid}/settingsUpdate settings
DELETE/indexes/{uid}/settingsReset all settings

Tasks

MethodPathDescription
GET/tasksList tasks
GET/tasks/{uid}Get task status
DELETE/tasksCancel/delete tasks

System

MethodPathDescription
GET/healthHealth check
GET/versionVersion info
GET/statsDatabase statistics
GET/metricsPrometheus metrics
POST/dumpsCreate a database dump
POST/snapshotsCreate a snapshot

How is this guide?

Last updated on

On this page