First collection: upsert and search
The smallest useful Aetherfy program: create a collection, write points into it, and search it. Both versions below are complete files. The vectors are written out as literal floats so the example runs with no embedding model installed; in production you would produce them with an embedding model — see Bring your own embeddings.
Before you start, install the SDK and export a key from https://app.aetherfy.com/dashboard/settings/api-keys :
export AETHERFY_API_KEY="afy_live_your_key_here"Creating an Aetherfy collection in Python
Install with pip install aetherfy-vectors (Python >= 3.9).
VectorConfig and DistanceMetric are not re-exported from the package root, so import them from aetherfy_vectors.models.
from aetherfy_vectors import AetherfyVectorsClient
from aetherfy_vectors.models import VectorConfig, DistanceMetric, Point
# Reads AETHERFY_API_KEY from the environment. timeout is in seconds.
client = AetherfyVectorsClient(timeout=30.0)
collection = client.create_collection(
"products",
VectorConfig(size=4, distance=DistanceMetric.COSINE),
description="Product catalogue for the storefront",
)
print(collection.name) # products
print(collection.config.size) # 4
print(collection.config.distance) # DistanceMetric.COSINEsize is the dimension of every vector you will store in the collection and cannot be changed later. It must match the output dimension of whatever produces your vectors.
Re-running this file is safe. On Aetherfy, re-creating a collection with the same configuration is idempotent and returns 200, so the script calls create unconditionally rather than guarding it with collection_exists. Only a create that asks for a different configuration under a name already in use fails, with 409 COLLECTION_NAME_TAKEN.
Upserting points into an Aetherfy collection in Python
points = [
Point(id=1, vector=[0.10, 0.20, 0.30, 0.40],
payload={"title": "Wireless mouse", "category": "electronics", "price": 24.99}),
Point(id=2, vector=[0.15, 0.18, 0.33, 0.41],
payload={"title": "Mechanical keyboard", "category": "electronics", "price": 89.00}),
Point(id=3, vector=[0.90, 0.10, 0.05, 0.02],
payload={"title": "Ceramic mug", "category": "kitchen", "price": 12.50}),
]
ok = client.upsert("products", points)
print(ok) # Trueupsert returns a plain bool in the Aetherfy Python SDK — it does not return the written points. Writing a point with an id that already exists replaces it.
Searching an Aetherfy collection in Python
results = client.search(
"products",
query_vector=[0.12, 0.19, 0.31, 0.40],
limit=2,
with_payload=True,
)
for r in results:
print(r.id, r.payload)
# 1 {'title': 'Wireless mouse', 'category': 'electronics', 'price': 24.99}
# 2 {'title': 'Mechanical keyboard', 'category': 'electronics', 'price': 89.0}
client.close()Each element is a SearchResult dataclass with the fields listed in the shape table below. score is a float produced by the distance metric you chose for the collection; its magnitude depends entirely on your vectors, so do not hard-code a threshold without measuring against your own data.
Creating an Aetherfy collection in JavaScript
Install with npm install aetherfy-vectors (Node >= 20). Import everything from the package root; there is no aetherfy-vectors/… subpath export.
Save this as main.mjs, or set "type": "module" in your package.json.
import { AetherfyVectorsClient, DistanceMetric } from 'aetherfy-vectors';
// Reads AETHERFY_API_KEY from the environment. timeout is in milliseconds.
const client = new AetherfyVectorsClient({ timeout: 30000 });
async function main() {
const collection = await client.createCollection(
'products',
{ size: 4, distance: DistanceMetric.COSINE },
'Product catalogue for the storefront',
);
console.log(collection.name); // products
console.log(collection.config.size); // 4
console.log(collection.config.distance); // Cosine
}
main();Note the argument order difference: the Aetherfy JS createCollection(collectionName, vectorsConfig, description?, regions?) takes the description third and has no separate distance parameter — the distance lives inside the vectors config. The Python create_collection(collection_name, vectors_config, distance=None, description=None, regions=None) accepts an optional standalone distance.
Upserting points into an Aetherfy collection in JavaScript
import { AetherfyVectorsClient } from 'aetherfy-vectors';
const client = new AetherfyVectorsClient();
async function main() {
const ok = await client.upsert('products', [
{ id: 1, vector: [0.10, 0.20, 0.30, 0.40],
payload: { title: 'Wireless mouse', category: 'electronics', price: 24.99 } },
{ id: 2, vector: [0.15, 0.18, 0.33, 0.41],
payload: { title: 'Mechanical keyboard', category: 'electronics', price: 89.00 } },
{ id: 3, vector: [0.90, 0.10, 0.05, 0.02],
payload: { title: 'Ceramic mug', category: 'kitchen', price: 12.50 } },
]);
console.log(ok); // true
}
main();upsert resolves to a boolean in the Aetherfy JS SDK, matching the Python behaviour.
Searching an Aetherfy collection in JavaScript
Everything after the query vector goes in a single options object, unlike the positional/keyword mix in Python.
import { AetherfyVectorsClient } from 'aetherfy-vectors';
const client = new AetherfyVectorsClient();
async function main() {
const results = await client.search('products', [0.12, 0.19, 0.31, 0.40], {
limit: 2,
withPayload: true,
});
for (const r of results) {
console.log(r.id, r.payload);
}
// 1 { title: 'Wireless mouse', category: 'electronics', price: 24.99 }
// 2 { title: 'Mechanical keyboard', category: 'electronics', price: 89 }
client.dispose();
}
main();The full Aetherfy search options
| Python keyword | JavaScript option key | Default | Meaning |
|---|---|---|---|
limit | limit | 10 | Maximum number of results |
offset | offset | 0 | Skip this many results |
query_filter | queryFilter | None / undefined | Payload filter, see Filtered search |
with_payload | withPayload | True / true | Include the stored payload |
with_vectors | withVectors | False / false | Include the stored vector |
score_threshold | scoreThreshold | None / undefined | Drop results below this score |
search_params | searchParams | None / undefined | Engine-level search tuning, see Search tuning |
The Aetherfy response shapes
These are the model definitions, not a sample. Python uses snake_case dataclasses; JavaScript uses camelCase interfaces.
Point — what you send to upsert:
| Field | Python type | JavaScript type | Required |
|---|---|---|---|
id | Union[str, int] | string | number | yes |
vector | List[float] | number[] | yes |
payload | Optional[Dict] | Record<string, unknown> (optional) | no |
SearchResult — what search returns:
| Field | Python type | JavaScript type | Present when |
|---|---|---|---|
id | Union[str, int] | string | number | always |
score | float | number | always |
payload | Optional[Dict] | optional | with_payload / withPayload is true |
vector | Optional[List[float]] | optional | with_vectors / withVectors is true |
Collection — what create_collection and get_collection return. The point-count field is named differently in each SDK.
| Python field | JavaScript field | Type |
|---|---|---|
name | name | string |
config | config | VectorConfig — size (int) and distance |
description | description | string, optional |
points_count | pointsCount | number, optional; None/undefined when the server does not report it |
status | status | optional |
regions | regions | optional; only populated on a plan with more than one region |
The Aetherfy distance metrics
DistanceMetric is an enum in both SDKs. The wire values are capitalised, and one of them is not what Aetherfy stores.
| Enum member | Wire value sent | Value Aetherfy stores and returns |
|---|---|---|
DistanceMetric.COSINE | Cosine | Cosine |
DistanceMetric.EUCLIDEAN | Euclidean | Euclid |
DistanceMetric.DOT | Dot | Dot |
DistanceMetric.MANHATTAN | Manhattan | Manhattan |
EUCLIDEAN does not round-trip. Aetherfy normalises distance names case-insensitively and stores both euclid and euclidean as Euclid, so a collection created with DistanceMetric.EUCLIDEAN reads back config.distance == "Euclid" — comparing it to 'Euclidean' silently fails. Cosine, Dot and Manhattan come back unchanged.
Pick the metric your embedding model was trained for. Cosine is the usual choice for normalised text embeddings.
Point id rules in Aetherfy
Both Aetherfy SDKs validate ids client-side, before any request goes out. Only two forms are accepted:
| Form | Accepted | Example |
|---|---|---|
Unsigned integer, 0 <= id <= 2**53 - 1 | yes | 1, 9007199254740991 |
| Canonical UUID string | yes | "3f1c2a7e-9b1e-4f2a-8c5d-1a2b3c4d5e6f" |
| UUID without hyphens (32 hex) | yes | "3f1c2a7e9b1e4f2a8c5d1a2b3c4d5e6f" |
| Braced UUID | yes | "{3f1c2a7e-9b1e-4f2a-8c5d-1a2b3c4d5e6f}" |
| URN UUID | yes | "urn:uuid:3f1c2a7e-9b1e-4f2a-8c5d-1a2b3c4d5e6f" |
| Arbitrary slug | no | "memory-001" raises ValidationError |
| Negative integer | no | -1 raises ValidationError |
| Boolean (Python) | no | True is rejected, it is not treated as 1 |
There is no coercion in either direction: an id you sent as an integer comes back as an integer, and an id you sent as a UUID string comes back as a string. If your domain keys are slugs such as order-12345, store the slug in the payload and use a UUID or an integer as the point id.
# Storing a domain key that is not a legal point id.
import uuid
client.upsert("products", [
Point(
id=str(uuid.uuid4()),
vector=[0.10, 0.20, 0.30, 0.40],
payload={"sku": "memory-001", "title": "Wireless mouse"},
)
])// The JavaScript equivalent, using the built-in crypto.randomUUID().
await client.upsert('products', [
{
id: crypto.randomUUID(),
vector: [0.10, 0.20, 0.30, 0.40],
payload: { sku: 'memory-001', title: 'Wireless mouse' },
},
]);Where Aetherfy puts this collection
Collection placement depends on your plan. On the Free and Starter plans a collection lives in a single region. Replication across more than one region begins at the plan named Performance, and only then does the regions argument to create_collection / createCollection and the regions field on Collection carry more than one entry. See Regions & replication.
Next steps with Aetherfy
- Narrow results by payload: Filtered search
- Write more than a handful of points at a time: Batch upsert & pagination
- Remove or count what you wrote: Retrieve, delete, count