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()Creating a collection whose name is already taken, or reading one that does not exist, raises an AetherfyVectorsException subclass rather than returning None. Guard with collection_exists when the collection may or may not be there:
if not client.collection_exists("articles"):
client.create_collection("articles", VectorConfig(size=4, distance=DistanceMetric.COSINE))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. Note pointsCount, not points_count.
for (const c of await client.getCollections()) {
console.log(c.name, c.config.size, c.pointsCount);
}
// 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 | pointsCount | number, optional | Snake case in Python, camel case in JavaScript |
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.
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.