QANATIX
QANATIX Open

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 Cloud

Endpoints

MethodPathDescriptionRate Limit
GET/open/v1/collectionsList public collections (with optional ?q= filter)120 RPM
GET/open/v1/collections/{collection}/schemaGet field schema for a collection120 RPM
POST/open/v1/searchSearch all public data (collection optional in body)60 RPM
GET/open/v1/searchGET convenience search across all data60 RPM
POST/open/v1/search/{collection}Search within a specific collection60 RPM
GET/open/v1/search/{collection}GET convenience search in a collection60 RPM
POST/open/v1/aggregateCount, group, and compute stats on public data60 RPM
GET/open/v1/records/{record_id}Lookup a public record by UUID120 RPM

GET /open/v1/collections

List all collections that have public records. Supports optional query filtering.

Query parameters

ParamTypeRequiredDescription
qstringNoFilter 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:

FieldTypeDescription
collectionstringCollection name
record_typesstring[]Record types in this collection
record_countintNumber of public records
descriptionstringCollection 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

ParamTypeDescription
collectionstringCollection 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"
  ]
}
FieldTypeDescription
collectionstringCollection name
record_typesstring[]Record types in this collection
record_countintNumber of public records
descriptionstringCollection description
filterable_fieldsstring[]Fields with types and sample values, e.g. "price_eur (number): 0.05, 8.50"

Errors

StatusDetail
404Collection 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
}
FieldTypeRequiredDefaultDescription
querystringNoSearch query (1–500 chars). Optional when filters are provided.
collectionstringNonullCollection to narrow search. Omit to search all public data
filtersobjectNo{}Key-value filter params (max 20). Supports multi-value: "manufacturer": "SKF,ABB"
sortstringNonullSort field. Prefix with - for descending. E.g. price_usd, -weight_kg
limitintNo10Results per page (1–50)
offsetintNo0Skip N results for pagination

Filter conventions

Same syntax as the private search API:

PatternConditionExample
field=valExact match{"manufacturer": "SKF"}
field=val1,val2Multi-value (IN){"manufacturer": "SKF,ABB,FAG"}
field_min=valRange >={"price_min": "10"}
field_max=valRange <={"price_max": "100"}
field_gt=valRange >{"stars_gt": "3"}
field_lt=valRange <{"score_lt": "0.9"}
field_in=val1,val2IN (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
  }
}
  • No authentication — open to all
  • Cross-tenant — results come from any tenant that published public data
  • Cross-collection — results span all collections unless collection is 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.

ParamTypeRequiredDefaultDescription
qstringNo""Search query. Optional — omit to browse with sort/filters only.
collectionstringNonullCollection to narrow search
sortstringNonullSort field. Prefix with - for descending.
offsetintNo0Skip N results for pagination
limitintNo10Results (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

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

ParamTypeRequiredDefaultDescription
qstringNo""Search query. Optional — omit to browse.
sortstringNonullSort field. Prefix with - for descending.
offsetintNo0Skip N results for pagination
limitintNo10Results (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"]
}
FieldTypeRequiredDefaultDescription
collectionstringNonullCollection to aggregate
filtersobjectNo{}Same filters as search
group_bystringNonullField to group by. Returns top 50 values with counts.
statsstring[]NonullNumeric 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.

ParamTypeDescription
record_idUUIDRecord UUID (from search results)

Response (200): Full record object with all fields including collection_data.

Errors:

StatusDetail
404Record 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 typeLimit (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: 1709827200

Rate limit exceeded (429)

{
  "detail": "Rate limit exceeded. Try again in 12 seconds."
}

Errors

StatusMeaning
400Bad request
404Record not found or not public
422Validation error (query too long, limit out of range)
429Rate limit exceeded
500Internal server error

On this page