API Reference

Structured Query API

Filter, sort, and aggregate documents by their typed fields — exact and deterministic, with no embedding involved. For a task-oriented walkthrough, start with the Structured queries guide; this page is the parameter and response reference.

SDK methods

OperationPythonTypeScript.NETGo
Run a structured queryqueryqueryparity follow-upparity follow-up
Declare typed fieldsschema.declare_fieldsschema.declareFieldsparity follow-upparity follow-up
List declared fieldsschema.list_fieldsschema.listFieldsparity follow-upparity follow-up
Delete a fieldschema.delete_fieldschema.deleteFieldparity follow-upparity follow-up

The query and field-schema surface ships in the Python and TypeScript SDKs first; .NET and Go parity follow.


Field schema

Declared fields are typed, indexed views over values your documents already carry. See declaring fields for the type and source tables.

Declare fields

schema.declare_fields(fields)PUT /v1/schema/fields. Body is { "fields": [ { name, type, source, partition_scope? } ] }. Re-declaring a name replaces its definition and re-backfills. Returns the declared set.

fields = client.schema.declare_fields([
    {"name": "amount", "type": "float",  "source": {"metadata": "amount"}},
    {"name": "status", "type": "string", "source": {"metadata": "status"}},
])

List fields

schema.list_fields()GET /v1/schema/fields. Returns each field with live coverage stats.

Each FieldSchema has this shape:

TypeScript
interface FieldSchema {
  name: string;
  type: "string" | "int" | "float" | "bool" | "datetime" | "string_list";
  source: { metadata: string } | { regex: string };
  partition_scope: string | null;
  coverage: number;        // documents with a value for this field
  mismatch_count: number;  // documents whose source value couldn't be coerced
  backfill: string;        // backfill progress for the field
}

Delete a field

schema.delete_field(name)DELETE /v1/schema/fields/{name}. Returns the remaining fields.

On a partition-scoped handle, all three calls are scoped to that partition automatically.


Query

query(...)POST /v1/query. Presence of aggregate selects the mode.

Request

ParameterTypeApplies toNotes
filterfilter objectbothThe unified filter grammar. Omit to match every document in scope.
sort[{ by, dir }]bothdir is asc (default) or desc. Mode A sorts documents (missing values last); Mode B sorts groups by an aggregate output or group key.
limitintegerbothMode A: page size, max 1000. Mode B: maximum number of groups.
offsetintegerMode APage offset; default 0.
group_by[string]Mode BUp to two fields; one result row per distinct combination.
aggregate[{ op, field?, as? }]Mode BPresence switches to aggregation mode.
partitionstringbothUsually set for you by a partition-scoped handle.

Filter grammar{ and | or | not } combinators over { field, op, value } leaves; operators eq, neq, in, gt, gte, lt, lte, between, exists, contains, prefix. Full semantics in the guide.

Aggregate operatorscount, count_distinct, sum, avg, min, max. The numeric operators require an int or float field.

Response — Mode A (no aggregate)

A document page:

JSON
{
  "documents": [ /* document records, newest first unless sorted */ ],
  "total": 128,
  "has_more": true
}

total is the full matching count (not just the returned page); page with offset += limit while has_more is true.

Response — Mode B (aggregate present)

Grouped aggregate rows:

JSON
{
  "groups": [
    { "keys": { "region": "us-east" }, "aggregates": { "total": 4210.0, "count": 37 } }
  ],
  "total_groups": 4,
  "scanned": 128
}

total_groups is the number of groups; scanned is how many documents were considered. A sum over an int field stays an integer; sum/avg otherwise accumulate as floating point.

Examples

# Mode A — filtered, sorted, paged documents
page = client.query(
    filter={"field": "status", "op": "eq", "value": "paid"},
    sort=[{"by": "amount", "dir": "desc"}],
    limit=20,
)

# Mode B — totals per region
result = client.query(
    group_by=["region"],
    aggregate=[{"op": "sum", "field": "amount", "as": "total"}],
    sort=[{"by": "total", "dir": "desc"}],
)

Errors

Structured queries fail loud — the guardrails return 400 (invalid_input) with a precise message rather than a truncated 200:

CauseBehavior
Unknown field (not declared, not a built-in)400
Type-mismatched literal (float field vs "cheap")400
Non-numeric aggregate (sum over a string field)400
Group cap or candidate-scan cap exceeded400 — narrow the filter or add a partition

See the error reference for the full error shape.


REST reference

The SDK is the recommended path. For debugging or non-SDK integrations, call the endpoint directly against https://api.aetherdb.ai:

Bash
curl -X POST https://api.aetherdb.ai/v1/query \
  -H "Authorization: Bearer $AETHER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": { "field": "status", "op": "eq", "value": "paid" },
    "sort":   [{ "by": "amount", "dir": "desc" }],
    "limit":  20
  }'

The field-schema routes take the same bearer auth: PUT /v1/schema/fields, GET /v1/schema/fields, and DELETE /v1/schema/fields/{name}.