Skip to Content
Vector databaseREST API reference
Raw

Aetherfy vector REST API

Base URL and authentication for the Aetherfy vector API

The Aetherfy vector API is served from https://vectors.aetherfy.com. Every customer-facing route lives under /api/v1. Authentication is a bearer token:

Authorization: Bearer <api key>

Collection names are tenant-scoped by Aetherfy on the server side. You always send your own bare collection name — never a prefixed or namespaced one — and Aetherfy resolves it to your account’s collection.

curl -s https://vectors.aetherfy.com/api/v1/collections \ -H "Authorization: Bearer $AETHERFY_API_KEY"

A request with no Authorization header, or one that is not a Bearer header, is refused with 401 MISSING_API_KEY. A syntactically valid key that does not resolve is refused with 401 INVALID_API_KEY. All error shapes are documented on errors.

Collection routes in the Aetherfy vector API

MethodPathPurpose
POST/api/v1/collectionsCreate a collection. Body: {name, vectors:{size,distance}, description?, regions?}. Re-creating an existing collection with the same configuration is idempotent and returns 200; a different configuration under an existing name returns 409 COLLECTION_NAME_TAKEN.
GET/api/v1/collectionsList collections. Workspaceless form only.
GET/api/v1/collections/{name}Collection info.
PATCH/api/v1/collections/{name}Update metadata. description only — any other key returns 400.
DELETE/api/v1/collections/{name}Delete the collection.

Aetherfy fixes the rest of the collection configuration server-side. There is no request field for optimizers_config, hnsw_config, or any other engine tuning at creation time.

Deleting a collection an Aetherfy agent is using is refused with 409 COLLECTION_IN_USE. Nothing declares that: Aetherfy records which collections each agent actually reads and writes, so a collection an agent opened at runtime is protected exactly like one it has used since its first deploy. The window is 35 days of activity — an agent that has not touched the collection in longer no longer blocks it.

The response body carries an agents array naming the agents that block it. Stop or delete those agents, or wait for the observation to age out, and repeat the request. Every path that can destroy a collection runs this check — the API and SDK delete, and Aetherfy’s own internal cleanup — so the refusal is a property of the platform, not of the route you happened to call.

Because a same-configuration create is idempotent on Aetherfy, a script may call create unconditionally on every run without checking collection_exists first — the second and later runs return 200 and change nothing. Only a create that asks for a different configuration under a name already in use is an error.

# Create. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"articles","vectors":{"size":4,"distance":"Cosine"},"description":"Article embeddings"}' # Read. curl -s https://vectors.aetherfy.com/api/v1/collections/articles \ -H "Authorization: Bearer $AETHERFY_API_KEY" # Update the description (the only mutable field). curl -s -X PATCH https://vectors.aetherfy.com/api/v1/collections/articles \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"description":"Article embeddings, v2"}' # Delete. curl -s -X DELETE https://vectors.aetherfy.com/api/v1/collections/articles \ -H "Authorization: Bearer $AETHERFY_API_KEY"

Collection names are validated by Aetherfy: 1 to 100 characters, drawn from [a-zA-Z0-9_-] only. Descriptions are at most 500 characters and may not contain HTML angle brackets. Both violations return 400.

Response headers and body on an Aetherfy collection read

GET /api/v1/collections/{name} returns a body of the form {result, schema_version} and carries three response headers that Aetherfy adds:

HeaderMeaning
ETagVersion tag for the collection record
X-Collection-SourceWhich store answered the read
X-Collection-StatusLifecycle status of the collection

These headers are specific to the single-collection read on Aetherfy; the list route does not carry them.

Point routes in the Aetherfy vector API

MethodPathPurpose
PUT/api/v1/collections/{name}/pointsUpsert points (streaming). Body: {"points":[{id, vector, payload?}]}
POST/api/v1/collections/{name}/points/searchSimilarity search
POST/api/v1/collections/{name}/points/retrieveRetrieve by ids. Body: {ids:[...], with_payload?, with_vector?}
GET/api/v1/collections/{name}/points/{id}Retrieve a single point
POST/api/v1/collections/{name}/points/scrollPaginate through points
POST/api/v1/collections/{name}/points/countCount. Body: {filter?, exact?}
POST/api/v1/collections/{name}/points/deleteDelete by {points:[ids]} or by {filter:{...}}

Upsert on Aetherfy is PUT, not POST. This is the single most common mistake against this API — see the unsupported-endpoint section below for what POST on that path actually does.

On the retrieve route the flags are with_payload (default true) and with_vector — singular — defaulting to false. The search body takes the same singular spelling, with_vector. Both Aetherfy SDKs expose this option under the plural name (with_vectors in Python, withVectors in JavaScript), so a hand-written REST search body that says with_vectors is not a flag Aetherfy reads. On the count route a limit key in the body is stripped by Aetherfy before the request reaches the engine, so sending one has no effect.

# Upsert (PUT, not POST). curl -s -X PUT https://vectors.aetherfy.com/api/v1/collections/articles/points \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"points":[ {"id":1,"vector":[0.1,0.2,0.3,0.4],"payload":{"lang":"en"}}, {"id":2,"vector":[0.4,0.3,0.2,0.1],"payload":{"lang":"fr"}} ]}' # Search. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/search \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"vector":[0.1,0.2,0.3,0.4],"limit":5,"with_payload":true}' # Retrieve by id. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/retrieve \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ids":[1,2],"with_payload":true,"with_vector":false}' # Retrieve a single point. curl -s https://vectors.aetherfy.com/api/v1/collections/articles/points/1 \ -H "Authorization: Bearer $AETHERFY_API_KEY" # Scroll. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/scroll \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"limit":100,"with_payload":true}' # Count. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/count \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"exact":true}' # Delete by id, then by filter. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/delete \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"points":[1,2]}' curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/delete \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"filter":{"must":[{"key":"lang","match":{"value":"fr"}}]}}'

Per-request caps on these routes — search and scroll limit, retrieve id count, delete point count, upsert point count and wire size — are on limits.

Payload and index routes in the Aetherfy vector API

MethodPathPurpose
POST/api/v1/collections/{name}/points/payloadSet payload — merge
PUT/api/v1/collections/{name}/points/payloadOverwrite payload — replace
POST/api/v1/collections/{name}/points/payload/deleteDelete named payload keys
PUT or POST/api/v1/collections/{name}/indexCreate a payload field index
DELETE/api/v1/collections/{name}/index/{field}Delete a payload field index

POST merges into the existing payload; PUT replaces it wholesale. Aetherfy reserves every payload key beginning __aetherfy_ and strips them from read responses, with two exceptions: __aetherfy_agent_id and __aetherfy_deployment_id, the attested author and deployment described below, come back on every read. Because they come back you may send them again — Aetherfy ignores the values and re-stamps them from your credential. Sending any of the others returns 400 RESERVED_FIELD.

# Merge keys into the existing payload. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/payload \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"payload":{"reviewed":true},"points":[1,2]}' # Replace the payload wholesale. curl -s -X PUT https://vectors.aetherfy.com/api/v1/collections/articles/points/payload \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"payload":{"lang":"en"},"points":[1]}' # Remove named keys. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/payload/delete \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"keys":["reviewed"],"points":[1,2]}'

Attested authorship in the Aetherfy vector API

Every point the Aetherfy vector API writes carries two keys in its payload that record the credential that performed the write: __aetherfy_agent_id, which agent, and __aetherfy_deployment_id, which of that agent’s deployments.

Credential used for the write__aetherfy_agent_id__aetherfy_deployment_id
An API key Aetherfy minted and injected into an agent at deployThat agent’s idThe deployment that wrote it; for a task agent, the run
An API key you created yourselfnullnull
The Aetherfy dashboard’s vector explorernullnull

The two are set together: __aetherfy_deployment_id is null exactly when __aetherfy_agent_id is. The deployment identifies a version of the agent, not a machine or a region. Every machine of one service deployment stamps the same id, and each task run stamps its own.

Aetherfy sets both values from the credential on every write. You may send the keys back — you have to be able to, since they come out on every read and editing a payload means retrieving it, changing a field and writing it again. Aetherfy ignores whatever values you send and stamps the credential’s. Sending them is never an error, and choosing their values is never possible: an agent that names its own id or deployment gets the same treatment as one that names somebody else’s, and a key you created yourself always stamps null for both no matter what the body says. Both are attested by Aetherfy rather than asserted by the client precisely because Aetherfy issued the agent’s credential for that deployment and nobody else could have used it.

The other reserved keys behave differently, because they never come back on a read: sending one is rejected with 400 RESERVED_FIELD.

Both keys are re-evaluated on every mutation. An upsert, a payload set or overwrite, a payload-key delete, a payload clear and a vector update all re-attribute the point to whoever performed that write — the same last-writer rule Aetherfy uses to order writes across regions.

Unlike Aetherfy’s other reserved keys, these two are returned on search, retrieve and scroll, and you can filter on either:

# Everything a given agent wrote. curl -s -X POST https://vectors.aetherfy.com/api/v1/collections/articles/points/scroll \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"filter":{"must":[{"key":"__aetherfy_agent_id","match":{"value":"6f1c…"}}]},"limit":20,"with_payload":true}'

Aetherfy does not create a payload index for these keys. An unindexed filter is scanned rather than looked up, so if you filter by author or deployment routinely, index the key yourself through the index route above. More filter forms, including “written by any agent” and “written by one deployment”, are on filtering.

Schema routes in the Aetherfy vector API

MethodPathPurpose
GET/api/v1/schema/{collection}Read the stored payload schema
PUT/api/v1/schema/{collection}Write it. Body: {schema, enforcement_mode, description?}
DELETE/api/v1/schema/{collection}Remove it
POST/api/v1/schema/{collection}/analyzeInfer a schema from stored points. Body: {sample_size?}
POST/api/v1/schema/{collection}/conformanceReport how much stored data would fail the schema. Body: {mode?, sample_size?, max_examples?}

enforcement_mode accepts "off", "warn", or "strict"; anything else returns 400 INVALID_ENFORCEMENT_MODE. sample_size must be an integer between 100 and 10 000 on both analyze and conformance, or Aetherfy returns 400 INVALID_SAMPLE_SIZE.

Enforcement applies to new writes only, so a schema you switch on says nothing about the points already stored. conformance is how you find out: it scans the stored points against the stored schema and reports how many conform, which fields are responsible, and a capped sample of offending point ids. It is read-only and repairs nothing. mode is "sample" (the default, and it reads a prefix of the collection rather than a random sample) or "full"; anything else returns 400 INVALID_SCAN_MODE. max_examples caps the returned sample of offending point ids and must be an integer between 0 and 100, or Aetherfy returns 400 INVALID_MAX_EXAMPLES. Read scan.exact before quoting the number: it is true only when the scan reached the end of the collection, and a full scan of a large collection will usually stop at its time budget and come back with scan.exact: false and scan.stopped_because: "time_budget". A collection with no schema returns 404 SCHEMA_NOT_DEFINED, because there is nothing to conform to.

The PUT route supports If-Match for compare-and-set. A mismatch returns 412 SCHEMA_VERSION_MISMATCH, whose body carries current_etag so you can re-read and retry.

# Read the current schema. curl -s https://vectors.aetherfy.com/api/v1/schema/articles \ -H "Authorization: Bearer $AETHERFY_API_KEY" # Infer one from the stored points. curl -s -X POST https://vectors.aetherfy.com/api/v1/schema/articles/analyze \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"sample_size":500}' # Ask how much of what is already stored would fail it. curl -s -X POST https://vectors.aetherfy.com/api/v1/schema/articles/conformance \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"mode":"sample","sample_size":1000}' # Remove it. curl -s -X DELETE https://vectors.aetherfy.com/api/v1/schema/articles \ -H "Authorization: Bearer $AETHERFY_API_KEY"

Region discovery in the Aetherfy vector API

GET /api/v1/regions returns the region-to-regional-URL map that the Aetherfy SDKs use to build a region-pinned client.

curl -s https://vectors.aetherfy.com/api/v1/regions \ -H "Authorization: Bearer $AETHERFY_API_KEY"

Which regions actually hold your data is a property of your plan, not of this route: Free and Starter accounts are single-region, with the region fixed by the first resource created, and replication across regions begins at the plan named Performance. Asking for a region outside your plan’s scope returns 422 STARTER_REGION_CONSISTENCY or 422 COLLECTION_REGIONS_NOT_IN_SCOPE.

Workspace-scoped routes in the Aetherfy vector API

Every collection route above has a workspace-scoped twin. Insert /workspaces/{workspace} between /api/v1 and /collections:

WorkspacelessWorkspace-scoped
POST /api/v1/collectionsPOST /api/v1/workspaces/{workspace}/collections
GET /api/v1/collections/{name}GET /api/v1/workspaces/{workspace}/collections/{name}
PATCH /api/v1/collections/{name}PATCH /api/v1/workspaces/{workspace}/collections/{name}
DELETE /api/v1/collections/{name}DELETE /api/v1/workspaces/{workspace}/collections/{name}
PUT /api/v1/collections/{name}/pointsPUT /api/v1/workspaces/{workspace}/collections/{name}/points
POST /api/v1/collections/{name}/points/searchPOST /api/v1/workspaces/{workspace}/collections/{name}/points/search

The collection name in the workspace-scoped form must be bare — it may not contain a /. The collection list route (GET /api/v1/collections) exists in the workspaceless form only.

curl -s -X POST https://vectors.aetherfy.com/api/v1/workspaces/research/collections/articles/points/search \ -H "Authorization: Bearer $AETHERFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{"vector":[0.1,0.2,0.3,0.4],"limit":5}'

Both Aetherfy SDKs reach the workspace-scoped routes through the workspace client option rather than by hand-building the path — see the SDK reference.

Distance values accepted by the Aetherfy vector API

Aetherfy normalizes the distance string case-insensitively at collection creation.

You send (any case)Aetherfy stores and returns
cosineCosine
euclidEuclid
euclideanEuclid
dotDot
manhattanManhattan

Euclidean is the one value that does not round-trip through Aetherfy, and it catches SDK users too: DistanceMetric.EUCLIDEAN sends the string Euclidean, so a collection created with it reads back config.distance == "Euclid" and an equality check against Euclidean silently fails. Every other name comes back exactly as the table’s left column, capitalised.

Qdrant compatibility and forwarding in the Aetherfy vector API

Anything under /api/v1/collections/{name}/points/** is forwarded verbatim to Qdrant by Aetherfy. Nothing inspects or rewrites the body, which is why Qdrant’s filter DSL, point shapes, and query bodies work unchanged — and why an Aetherfy-side typo in a filter key is silently accepted rather than rejected. Filter syntax and the one key-name defect worth knowing about are on filtering.

The one consequence to internalise: Aetherfy validates the envelope (auth, tenancy, caps, reserved keys), and Qdrant validates the query. An option Aetherfy does not know about is not an error at the Aetherfy layer.

Endpoints the Aetherfy vector API does not support

Aetherfy is Qdrant-compatible but not a Qdrant passthrough. These are refused, deliberately, and no amount of retrying will change the answer.

RequestResultUse instead
PUT /collections/{name} (Qdrant-native create)405 METHOD_NOT_ALLOWEDPOST /api/v1/collections
PATCH /collections/{name} with optimizers_config, hnsw_config, or any non-description key400Only description is mutable
POST /collections/{name}/points expecting upsertNot upsert — falls through to Qdrant’s batch retrieve, which expects body.idsPUT /api/v1/collections/{name}/points
Snapshot endpoints404 “Unsupported endpoint”—
/telemetry, /metrics404 “Unsupported endpoint”—
/cluster, /locks, shard endpoints404 “Unsupported endpoint”—
Collection creation with engine tuning fieldsNot accepted — configuration is fixed server-side by Aetherfy—
A payload key beginning __aetherfy_, other than __aetherfy_agent_id and __aetherfy_deployment_id400 RESERVED_FIELD on write; stripped from readsChoose another key
A payload key __aetherfy_agent_id or __aetherfy_deployment_idAccepted and ignored — Aetherfy stamps the attested author and deployment from your credential; returned on readsNothing; round-tripping a payload is expected

Query-time tuning is the one engine knob Aetherfy does expose, and it goes in the request body’s params field — see search tuning.

Last updated on