Collection lifecycle and payload schema
Everything that operates on a collection as a whole: listing what exists, checking for one, reading its configuration, deleting it — plus the payload mutation calls and the optional payload schema.
export AETHERFY_API_KEY="afy_live_your_key_here"The Aetherfy collection lifecycle methods
| Operation | Python | JavaScript | Returns |
|---|---|---|---|
| Create | create_collection(collection_name, vectors_config, distance=None, description=None, regions=None) | createCollection(collectionName, vectorsConfig, description?, regions?) | Collection |
| List all | get_collections() | getCollections() | List[Collection] / Promise<Collection[]> |
| Exists | collection_exists(name) | collectionExists(name) | bool / Promise<boolean> |
| Read one | get_collection(name) | getCollection(name) | Collection |
| Delete | delete_collection(name) | deleteCollection(name) | bool / Promise<boolean> |
Note the signature difference: the Aetherfy Python create_collection accepts a standalone distance argument in third position, while the JavaScript createCollection takes the description third and expects the distance inside the vectors config.
Managing Aetherfy collections in Python
Install with pip install aetherfy-vectors (Python >= 3.9). VectorConfig and DistanceMetric live in aetherfy_vectors.models and are not re-exported from the package root; Schema, FieldDefinition and AnalysisResult are.
from aetherfy_vectors import AetherfyVectorsClient
from aetherfy_vectors.models import VectorConfig, DistanceMetric, Point
client = AetherfyVectorsClient()
# Create.
created = client.create_collection(
"articles",
VectorConfig(size=4, distance=DistanceMetric.COSINE),
description="Published articles",
)
print(created.name, created.config.size, created.config.distance)
# articles 4 DistanceMetric.COSINE
# Exists.
print(client.collection_exists("articles")) # True
print(client.collection_exists("nope")) # False
# Read one.
info = client.get_collection("articles")
print(info.name) # articles
print(info.config.size) # 4
print(info.description) # Published articles
# List all.
for c in client.get_collections():
print(c.name, c.config.size, c.points_count)
# articles 4 0
# Delete.
print(client.delete_collection("articles")) # True
print(client.collection_exists("articles")) # False
client.close()Reading a collection that does not exist raises an AetherfyVectorsException subclass rather than returning None. Creating one is more forgiving than it looks: re-creating an existing collection with the same configuration is idempotent and returns 200, so a script may call create unconditionally on every run. Only a create that asks for a different configuration under a name already in use fails, with 409 COLLECTION_NAME_TAKEN.
The guard below is therefore optional for the same-configuration case, and useful when the configuration may differ or you want to branch on existence:
if not client.collection_exists("articles"):
client.create_collection("articles", VectorConfig(size=4, distance=DistanceMetric.COSINE))Deletion has one precondition worth knowing before you script it: if an Aetherfy agent is using the collection, the delete is refused with 409 COLLECTION_IN_USE and both SDKs raise CollectionInUseError, whose message names the agents blocking it. “Using” means Aetherfy has observed that agent reading or writing the collection in the last 35 days — there is nothing to declare, and a collection your agent code created at runtime is protected the same way. Stop or delete the named agents, or wait for the observation to age out, then repeat the delete.
Managing Aetherfy collections in JavaScript
Install with npm install aetherfy-vectors (Node >= 20). Import everything from the package root.
import { AetherfyVectorsClient, DistanceMetric } from 'aetherfy-vectors';
const client = new AetherfyVectorsClient();
async function main() {
// Create.
const created = await client.createCollection(
'articles',
{ size: 4, distance: DistanceMetric.COSINE },
'Published articles',
);
console.log(created.name, created.config.size, created.config.distance);
// articles 4 Cosine
// Exists.
console.log(await client.collectionExists('articles')); // true
console.log(await client.collectionExists('nope')); // false
// Read one.
const info = await client.getCollection('articles');
console.log(info.name, info.config.size, info.description);
// articles 4 Published articles
// List all.
for (const c of await client.getCollections()) {
console.log(c.name, c.config.size, c.points_count);
}
// articles 4 0
// Delete.
console.log(await client.deleteCollection('articles')); // true
console.log(await client.collectionExists('articles')); // false
client.dispose();
}
main();The Aetherfy Collection shape
| Python field | JavaScript field | Type | Notes |
|---|---|---|---|
name | name | string | |
config | config | VectorConfig | size (int) and distance |
description | description | string, optional | |
points_count | points_count | number, optional | Server-reported point count |
status | status | optional | Reported by the server |
regions | regions | optional | Populated only on a plan with more than one region |
Region placement is plan-scoped in Aetherfy: Free and Starter collections live in a single region, and replication across more than one region begins at the plan named Performance. The regions argument on create and the regions field here are only meaningful above that line. See Regions & replication.
Mutating Aetherfy payloads without rewriting vectors
Three calls change a point’s payload while leaving its vector alone. They differ in whether they merge, replace, or remove.
| Call | Python | JavaScript | Semantics |
|---|---|---|---|
| Merge | set_payload(collection_name, payload, points, key=None) | setPayload(collectionName, payload, points, { key? }) | Additive — existing keys not mentioned are kept |
| Replace | overwrite_payload(collection_name, payload, points) | overwritePayload(collectionName, payload, points) | Full replace — keys not mentioned are dropped |
| Remove keys | delete_payload(collection_name, keys, points) | deletePayload(collectionName, keys, points) | Deletes the named keys only |
Every payload mutation call is capped at 512 points per call in both SDKs. Chunk a larger update.
Payload mutation in Aetherfy with Python
from aetherfy_vectors import AetherfyVectorsClient
from aetherfy_vectors.models import VectorConfig, DistanceMetric, Point
client = AetherfyVectorsClient()
client.create_collection("articles", VectorConfig(size=4, distance=DistanceMetric.COSINE))
client.upsert("articles", [
Point(id=1, vector=[0.1, 0.2, 0.3, 0.4],
payload={"title": "Hello", "status": "draft", "author": "ada"}),
])
# Merge: adds published_at, keeps title/status/author, updates status.
client.set_payload("articles", {"status": "published", "published_at": "2026-01-01"}, [1])
print(client.retrieve("articles", [1])[0]["payload"])
# {'title': 'Hello', 'status': 'published', 'author': 'ada', 'published_at': '2026-01-01'}
# Remove specific keys.
client.delete_payload("articles", ["published_at"], [1])
print(client.retrieve("articles", [1])[0]["payload"])
# {'title': 'Hello', 'status': 'published', 'author': 'ada'}
# Replace: everything not listed is gone.
client.overwrite_payload("articles", {"title": "Hello"}, [1])
print(client.retrieve("articles", [1])[0]["payload"])
# {'title': 'Hello'}
# Chunk anything larger than 512 points.
ids = list(range(2000))
for start in range(0, len(ids), 512):
client.set_payload("articles", {"batch": "import-7"}, ids[start:start + 512])
client.close()Payload mutation in Aetherfy with JavaScript
import { AetherfyVectorsClient, DistanceMetric } from 'aetherfy-vectors';
const client = new AetherfyVectorsClient();
async function main() {
await client.createCollection('articles', { size: 4, distance: DistanceMetric.COSINE });
await client.upsert('articles', [
{ id: 1, vector: [0.1, 0.2, 0.3, 0.4],
payload: { title: 'Hello', status: 'draft', author: 'ada' } },
]);
// Merge.
await client.setPayload('articles', { status: 'published', published_at: '2026-01-01' }, [1]);
console.log((await client.retrieve('articles', [1]))[0].payload);
// { title: 'Hello', status: 'published', author: 'ada', published_at: '2026-01-01' }
// Remove keys.
await client.deletePayload('articles', ['published_at'], [1]);
console.log((await client.retrieve('articles', [1]))[0].payload);
// { title: 'Hello', status: 'published', author: 'ada' }
// Replace.
await client.overwritePayload('articles', { title: 'Hello' }, [1]);
console.log((await client.retrieve('articles', [1]))[0].payload);
// { title: 'Hello' }
// Chunk anything larger than 512 points.
const ids = Array.from({ length: 2000 }, (_, i) => i);
for (let start = 0; start < ids.length; start += 512) {
await client.setPayload('articles', { batch: 'import-7' }, ids.slice(start, start + 512));
}
client.dispose();
}
main();The Aetherfy payload schema
A collection can carry an optional schema describing the shape of its payloads. The schema is a set of named field definitions, and an enforcement mode decides what happens when a write does not match.
| Enforcement mode | Behaviour |
|---|---|
off | The schema is stored but not applied. This is the default. |
warn | Mismatches are surfaced but the write proceeds. |
strict | Mismatches reject the write. |
Field definition fields:
Python (FieldDefinition) | JavaScript (FieldDefinition) | Meaning |
|---|---|---|
type | type | One of null, boolean, string, integer, float, array, object |
required | required | Whether the key must be present |
element_type | elementType | Element type, for array fields |
fields | fields | Nested field definitions, for object fields |
Note the casing split again: Python uses element_type, JavaScript uses elementType.
Setting and reading an Aetherfy schema in Python
Schema, FieldDefinition and AnalysisResult are exported from the aetherfy_vectors package root.
from aetherfy_vectors import AetherfyVectorsClient, Schema, FieldDefinition
from aetherfy_vectors.models import VectorConfig, DistanceMetric, Point
client = AetherfyVectorsClient()
client.create_collection("articles", VectorConfig(size=4, distance=DistanceMetric.COSINE))
schema = Schema(
fields={
"title": FieldDefinition(type="string", required=True),
"status": FieldDefinition(type="string", required=True),
"views": FieldDefinition(type="integer", required=False),
"tags": FieldDefinition(type="array", required=False, element_type="string"),
},
description="Article payload contract",
)
etag = client.set_schema("articles", schema, enforcement="strict", description="v1")
print(etag) # the ETag string of the new schema version
# Read it back. Returns None when no schema is set.
loaded = client.get_schema("articles")
print(sorted(loaded.fields)) # ['status', 'tags', 'title', 'views']
print(loaded.fields["title"].type) # string
print(loaded.fields["title"].required) # True
print(loaded.fields["tags"].element_type) # string
# Remove the schema.
print(client.delete_schema("articles")) # True
print(client.get_schema("articles")) # None
client.close()set_schema returns the new schema’s ETag as a string, not a schema object. get_schema returns Optional[Schema] — None means the collection exists but has no schema defined, which is a normal state and not an error.
The enforcement argument accepts only "off", "warn" or "strict"; anything else raises before a request is made.
Setting and reading an Aetherfy schema in JavaScript
import {
AetherfyVectorsClient,
DistanceMetric,
Schema,
} from 'aetherfy-vectors';
const client = new AetherfyVectorsClient();
async function main() {
await client.createCollection('articles', { size: 4, distance: DistanceMetric.COSINE });
const schema: Schema = {
fields: {
title: { type: 'string', required: true },
status: { type: 'string', required: true },
views: { type: 'integer', required: false },
tags: { type: 'array', required: false, elementType: 'string' },
},
description: 'Article payload contract',
};
const etag = await client.setSchema('articles', schema, 'strict', 'v1');
console.log(etag); // the ETag string of the new schema version
const loaded = await client.getSchema('articles');
if (loaded) {
console.log(Object.keys(loaded.fields).sort()); // [ 'status', 'tags', 'title', 'views' ]
console.log(loaded.fields.title.type); // string
console.log(loaded.fields.title.required); // true
console.log(loaded.fields.tags.elementType); // string
}
console.log(await client.deleteSchema('articles')); // true
console.log(await client.getSchema('articles')); // null
client.dispose();
}
main();getSchema resolves to Schema | null; null means no schema is defined. The enforcement mode is the third positional argument and defaults to 'off'.
Inferring an Aetherfy schema from existing points
analyze_schema / analyzeSchema samples the points already in a collection and proposes a schema. It writes nothing — you decide whether to install the suggestion.
sample_size must be between 100 and 10000 inclusive; the default is 1000.
Analyzing in Python
from aetherfy_vectors import AetherfyVectorsClient
client = AetherfyVectorsClient()
result = client.analyze_schema("articles", sample_size=1000)
print(result.collection) # articles
print(result.sample_size) # number of points actually sampled
print(result.total_points) # points in the collection
print(result.processing_time_ms) # server-side processing time, integer ms
print(sorted(result.suggested_schema.fields))
# ['status', 'tags', 'title', 'views']
# Install the suggestion, still without enforcing it.
etag = client.set_schema("articles", result.suggested_schema, enforcement="off")
client.close()Analyzing in JavaScript
import { AetherfyVectorsClient } from 'aetherfy-vectors';
const client = new AetherfyVectorsClient();
async function main() {
const result = await client.analyzeSchema('articles', 1000);
console.log(result.collection); // articles
console.log(result.sampleSize); // number of points actually sampled
console.log(result.totalPoints); // points in the collection
console.log(result.processingTimeMs); // server-side processing time, integer ms
console.log(Object.keys(result.suggestedSchema.fields).sort());
// [ 'status', 'tags', 'title', 'views' ]
await client.setSchema('articles', result.suggestedSchema, 'off');
client.dispose();
}
main();The Aetherfy AnalysisResult shape
| Python field | JavaScript field | Type |
|---|---|---|
collection | collection | string |
sample_size | sampleSize | integer |
total_points | totalPoints | integer |
fields | fields | map of field name to per-field analysis |
suggested_schema | suggestedSchema | Schema |
processing_time_ms | processingTimeMs | integer milliseconds |
A safe rollout is: analyze_schema to get a suggestion, set_schema with off to record it, switch to warn while you watch for mismatches, then strict once writes are clean.
Enforcement only ever applies to new writes, so switching to strict says nothing about the points you already have. Before you switch, POST /api/v1/schema/{collection}/conformance scans the stored points against the stored schema and tells you how many would fail, which fields are responsible, and a capped sample of the offending point ids. It is read-only and repairs nothing. That turns “watch for mismatches” into a number you can read before the switch rather than after it.
Related: First collection for creating and filling a collection, Retrieve, delete, count for removing points rather than the whole collection, and the SDK reference for the complete method list.