Open API Reference
All QANATIX Open endpoints — search, collections, record lookup. No auth required.
Open API Reference
All QANATIX Open endpoints live under /open/v1. No authentication required.
Base URL
http://localhost:8000/open/v1 # local development
https://api.qanatix.com/open/v1 # EU CloudEndpoints
| Method | Path | Description | Rate Limit |
|---|---|---|---|
| GET | /open/v1/collections | List public collections (with optional ?q= filter) | 120 RPM |
| GET | /open/v1/collections/{collection}/schema | Get field schema for a collection | 120 RPM |
| POST | /open/v1/search | Search all public data (collection optional in body) | 60 RPM |
| GET | /open/v1/search | GET convenience search across all data | 60 RPM |
| POST | /open/v1/search/{collection} | Search within a specific collection | 60 RPM |
| GET | /open/v1/search/{collection} | GET convenience search in a collection | 60 RPM |
| POST | /open/v1/aggregate | Count, group, and compute stats on public data | 60 RPM |
| GET | /open/v1/records/{record_id} | Lookup a public record by UUID | 120 RPM |
GET /open/v1/collections
List all collections that have public records. Supports optional query filtering.
Query parameters
| Param | Type | Required | Description |
|---|---|---|---|
q | string | No | Filter collections by name or description substring |
Examples
# List all public collections
curl https://api.qanatix.com/open/v1/collections
# Filter collections matching "fund" or "ETF"
curl "https://api.qanatix.com/open/v1/collections?q=fund"Response (200)
[
{
"collection": "manufacturing",
"record_types": ["product", "fastener"],
"record_count": 2450,
"description": "Contains 2450 product, fastener including Ball Bearing 6205, Hex Bolt M8."
}
]Returns a plain JSON array. Each item:
| Field | Type | Description |
|---|---|---|
collection | string | Collection name |
record_types | string[] | Record types in this collection |
record_count | int | Number of public records |
description | string | Collection description. User-provided if set, otherwise auto-generated from data |
To see filterable fields and sample values, use the schema endpoint below.
GET /open/v1/collections/{collection}/schema
Get detailed field schema for a collection, including field names, types, sample values, and filter syntax.
Path parameters
| Param | Type | Description |
|---|---|---|
collection | string | Collection name |
Response (200)
{
"collection": "manufacturing",
"record_types": ["product", "fastener"],
"record_count": 2450,
"description": "Industrial components and fasteners.",
"filterable_fields": [
"manufacturer (string): SKF, ABB, FAG",
"price_eur (number): 0.05, 8.50, 42.15",
"category (string): bearing, fastener",
"in_stock (bool): True, False"
]
}| Field | Type | Description |
|---|---|---|
collection | string | Collection name |
record_types | string[] | Record types in this collection |
record_count | int | Number of public records |
description | string | Collection description |
filterable_fields | string[] | Fields with types and sample values, e.g. "price_eur (number): 0.05, 8.50" |
Errors
| Status | Detail |
|---|---|
| 404 | Collection not found or has no public records |
POST /open/v1/search
Search all public data across all collections. Optionally narrow to a specific collection.
Request body
{
"query": "low cost ETFs",
"collection": "finance",
"filters": {
"expense_ratio_max": "0.5"
},
"sort": "-expense_ratio",
"limit": 10,
"offset": 0
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | No | — | Search query (1–500 chars). Optional when filters are provided. |
collection | string | No | null | Collection to narrow search. Omit to search all public data |
filters | object | No | {} | Key-value filter params (max 20). Supports multi-value: "manufacturer": "SKF,ABB" |
sort | string | No | null | Sort field. Prefix with - for descending. E.g. price_usd, -weight_kg |
limit | int | No | 10 | Results per page (1–50) |
offset | int | No | 0 | Skip N results for pagination |
Filter conventions
Same syntax as the private search API:
| Pattern | Condition | Example |
|---|---|---|
field=val | Exact match | {"manufacturer": "SKF"} |
field=val1,val2 | Multi-value (IN) | {"manufacturer": "SKF,ABB,FAG"} |
field_min=val | Range >= | {"price_min": "10"} |
field_max=val | Range <= | {"price_max": "100"} |
field_gt=val | Range > | {"stars_gt": "3"} |
field_lt=val | Range < | {"score_lt": "0.9"} |
field_in=val1,val2 | IN (explicit) | {"status_in": "active,pending"} |
Response (200)
{
"results": [
{
"record_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Deep Groove Ball Bearing 6205",
"score": 0.91,
"collection": "manufacturing",
"record_type": "product",
"collection_data": {
"manufacturer": "SKF",
"bore_mm": 25,
"price_eur": 8.50
},
"description_llm": "Deep Groove Ball Bearing 6205. manufacturer: SKF...",
"source_type": "file_upload",
"updated_at": "2026-03-06T12:00:00Z"
}
],
"pagination": {
"offset": 0,
"limit": 10,
"has_more": false
},
"metadata": {
"search_mode": "fulltext",
"processing_time_ms": 45.2,
"total_estimate": 1,
"cache_hit": false
}
}Key differences from private search
- No authentication — open to all
- Cross-tenant — results come from any tenant that published public data
- Cross-collection — results span all collections unless
collectionis specified - Fast — Postgres-powered search, no external dependencies
- Visibility enforced — only
visibility="public"records, double-checked at Postgres hydration
GET /open/v1/search
Convenience endpoint for simple searches via query params.
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
q | string | No | "" | Search query. Optional — omit to browse with sort/filters only. |
collection | string | No | null | Collection to narrow search |
sort | string | No | null | Sort field. Prefix with - for descending. |
offset | int | No | 0 | Skip N results for pagination |
limit | int | No | 10 | Results (1–50) |
# Search all public data
curl "https://api.qanatix.com/open/v1/search?q=low+cost+ETFs"
# Search within a specific collection
curl "https://api.qanatix.com/open/v1/search?q=ball+bearing&collection=manufacturing&limit=5"
# Browse sorted, no query
curl "https://api.qanatix.com/open/v1/search?collection=manufacturing&sort=-price_usd&limit=10"POST /open/v1/search/{collection}
Search within a specific collection. Equivalent to POST /open/v1/search with collection in body.
Kept for backward compatibility.
Path parameters
| Param | Type | Description |
|---|---|---|
collection | string | Collection to search |
Request body
Same as POST /open/v1/search (except collection comes from the path).
curl -X POST https://api.qanatix.com/open/v1/search/manufacturing \
-H "Content-Type: application/json" \
-d '{"query": "corrosion resistant bearing", "limit": 5}'GET /open/v1/search/{collection}
GET convenience endpoint for per-collection search. Kept for backward compatibility.
| Param | Type | Required | Default | Description |
|---|---|---|---|---|
q | string | No | "" | Search query. Optional — omit to browse. |
sort | string | No | null | Sort field. Prefix with - for descending. |
offset | int | No | 0 | Skip N results for pagination |
limit | int | No | 10 | Results (1–50) |
curl "https://api.qanatix.com/open/v1/search/manufacturing?q=ball+bearing&limit=5"POST /open/v1/aggregate
Count, group, and compute stats on public records.
Request body
{
"collection": "manufacturing",
"filters": {"in_stock": "True"},
"group_by": "manufacturer",
"stats": ["price_eur", "weight_kg"]
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
collection | string | No | null | Collection to aggregate |
filters | object | No | {} | Same filters as search |
group_by | string | No | null | Field to group by. Returns top 50 values with counts. |
stats | string[] | No | null | Numeric fields for min/max/avg (max 5) |
Response (200)
{
"total": 2450,
"groups": [
{"value": "SKF", "count": 850},
{"value": "ABB", "count": 420},
{"value": "FAG", "count": 310}
],
"stats": {
"price_eur": {"count": 2100, "min": 0.05, "max": 1250.00, "avg": 42.15},
"weight_kg": {"count": 1800, "min": 0.01, "max": 85.50, "avg": 2.34}
}
}GET /open/v1/records/{record_id}
Get full details of a public record by UUID.
| Param | Type | Description |
|---|---|---|
record_id | UUID | Record UUID (from search results) |
Response (200): Full record object with all fields including collection_data.
Errors:
| Status | Detail |
|---|---|
| 404 | Record not found or not public |
Private records return 404 — there is no way to distinguish "doesn't exist" from "exists but private".
Rate limiting
All Open endpoints are rate-limited per IP address using a Redis token bucket.
| Endpoint type | Limit (RPM) |
|---|---|
Search (/open/v1/search*, /open/v1/aggregate) | 60 |
| General (collections, schema, lookup) | 120 |
Response headers
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1709827200Rate limit exceeded (429)
{
"detail": "Rate limit exceeded. Try again in 12 seconds."
}Errors
| Status | Meaning |
|---|---|
| 400 | Bad request |
| 404 | Record not found or not public |
| 422 | Validation error (query too long, limit out of range) |
| 429 | Rate limit exceeded |
| 500 | Internal server error |