Quickstart — your first vector search
By the end of this page you will have created a collection in the Aetherfy vector database, stored three documents in it, and printed ranked search results. Every step is copy-pasteable in order, and the whole thing runs in one sitting.
Aetherfy exposes a Qdrant-compatible vector API through a Python SDK, a JS/TS SDK, and plain HTTP. This tutorial covers the two SDKs side by side — follow whichever language you use; the steps are the same.
Step 1 — Get your Aetherfy API key
Sign up or sign in at app.aetherfy.com/login with email or GitHub.
On first login, the Aetherfy welcome modal reveals your default API key’s plaintext. You see that plaintext exactly once. Aetherfy never stores it after that single reveal — only a hash of it — so nobody, including Aetherfy support, can show it to you again. Copy it somewhere safe immediately.
If you lose the key, you cannot recover it; create a replacement at app.aetherfy.com/dashboard/settings/api-keys .
Your Aetherfy API key’s plaintext is shown once, at first login, and is never stored afterwards. If you lose it, create a new key at Settings → API keys .
Aetherfy keys look like afy_live_... for live traffic, or afy_test_... for
test traffic. Export the key in your shell:
export AETHERFY_API_KEY="afy_live_your_api_key_here"Both Aetherfy SDKs read the AETHERFY_API_KEY environment variable
automatically, so you never have to pass the key in code.
Step 2 — Install an Aetherfy SDK
The Python and JS/TS clients for Aetherfy are published separately.
Python
The distribution name is aetherfy-vectors; the import package is
aetherfy_vectors. Python 3.9 or newer is required.
pip install aetherfy-vectorsJavaScript / TypeScript
The Aetherfy package is unscoped — it is aetherfy-vectors. Node 20 or newer is
required.
npm install aetherfy-vectorsStep 3 — Construct the Aetherfy client
Constructing a client takes no arguments, because it reads AETHERFY_API_KEY
from the environment.
Python
The Aetherfy Python client is synchronous — no async/await anywhere.
Note that VectorConfig and DistanceMetric are not re-exported from the root
package: import them from aetherfy_vectors.models.
from aetherfy_vectors import AetherfyVectorsClient
from aetherfy_vectors.models import VectorConfig, DistanceMetric
client = AetherfyVectorsClient() # reads AETHERFY_API_KEYJavaScript / TypeScript
Every method on the Aetherfy JS client is async and returns a promise.
import { AetherfyVectorsClient, DistanceMetric } from 'aetherfy-vectors';
const client = new AetherfyVectorsClient(); // reads AETHERFY_API_KEYStep 4 — Bring your own embeddings to Aetherfy
Aetherfy stores and searches vectors; it does not generate them. You bring your own embeddings from whichever model you already use.
Throughout this tutorial an embed() function stands in for that model. It is a
stub you fill in, and it must return a list of floats whose length matches the
size you give the collection in the next step. This tutorial uses 384, a
common embedding dimension — change it to match your model.
Aetherfy does not generate embeddings. Fill in the embed() stub with a call
to your own embedding model, and make sure it returns exactly as many floats
as the collection’s size.
Step 5 — Create an Aetherfy collection
A collection is the container that holds your points. It is created once, with a
fixed vector size and distance metric.
size must match the dimension of the embedding model behind embed().
Aetherfy enforces no dimension cap. There are four metrics:
DistanceMetric.COSINE, DistanceMetric.EUCLIDEAN, DistanceMetric.DOT, and
DistanceMetric.MANHATTAN. Cosine is the usual choice for normalised text
embeddings; the SDK reference lists what each one stores.
Python
client.create_collection(
"quickstart",
VectorConfig(size=384, distance=DistanceMetric.COSINE),
)JavaScript / TypeScript
await client.createCollection('quickstart', {
size: 384,
distance: DistanceMetric.COSINE,
});Step 6 — Upsert points into Aetherfy
A point is an id, a vector, and an optional payload of metadata you can filter on later. Upserting is idempotent by point id, so re-running the script updates the same three points rather than duplicating them.
An Aetherfy point id is an unsigned integer up to 2**53 − 1, or a UUID
string — nothing else. A slug such as "doc-001" is rejected by the SDK
validator before the request is sent, so use 1, 2, 3 or real UUIDs.
Point ids must be an unsigned integer up to 2**53 − 1, or a UUID string. A
slug like "doc-001" is rejected by the Aetherfy SDK validator.
Aetherfy accepts up to 10 000 points per upsert call.
Python
points = [
{"id": 1, "vector": embed("some text"), "payload": {"text": "some text"}},
]
client.upsert("quickstart", points)JavaScript / TypeScript
const points = [
{ id: 1, vector: await embed('some text'), payload: { text: 'some text' } },
];
await client.upsert('quickstart', points);Step 7 — Search your Aetherfy collection
This is the payoff. Searching an Aetherfy collection takes a query vector — produced by the same embedding model that produced the stored vectors — and returns the nearest points, best match first.
Each result carries id, score, payload, and vector. Ask for
with_payload / withPayload so the metadata comes back with the hit;
otherwise you get an id and a score and have to look the content up yourself.
Python
Returns a List[SearchResult], each with .id, .score, .payload, .vector.
results = client.search(
collection_name="quickstart",
query_vector=embed("how do I filter by metadata?"),
limit=5,
with_payload=True,
)JavaScript / TypeScript
Returns a SearchResult[], each with id, score, payload, vector.
const results = await client.search('quickstart', queryVector, {
limit: 5,
withPayload: true,
});The complete Python script for Aetherfy
Save this as aetherfy_quickstart.py, fill in embed(), and run it with
python aetherfy_quickstart.py. It creates the collection, stores three short
documents, searches them, and prints ranked results.
"""Aetherfy quickstart: create a collection, upsert documents, search them."""
import os
# Or export AETHERFY_API_KEY in your shell instead. An already-exported key wins.
os.environ.setdefault("AETHERFY_API_KEY", "afy_live_your_api_key_here")
from aetherfy_vectors import AetherfyVectorsClient
from aetherfy_vectors.models import VectorConfig, DistanceMetric
COLLECTION = "quickstart"
EMBEDDING_SIZE = 384
def embed(text: str) -> list:
"""Turn text into a list of EMBEDDING_SIZE floats.
Aetherfy does not generate embeddings — you bring your own vectors.
Replace this body with a call to your embedding model, and make sure it
returns exactly EMBEDDING_SIZE floats.
"""
raise NotImplementedError(f"Fill in embed() with your model; got: {text!r}")
DOCUMENTS = [
{"id": 1, "text": "Cosine distance compares the angle between two vectors."},
{"id": 2, "text": "A collection stores points, and every point has a vector."},
{"id": 3, "text": "Payload fields let you filter search results by metadata."},
]
def main() -> None:
client = AetherfyVectorsClient() # reads AETHERFY_API_KEY
client.create_collection(
COLLECTION,
VectorConfig(size=EMBEDDING_SIZE, distance=DistanceMetric.COSINE),
)
points = [
{
"id": doc["id"],
"vector": embed(doc["text"]),
"payload": {"text": doc["text"]},
}
for doc in DOCUMENTS
]
client.upsert(COLLECTION, points)
results = client.search(
collection_name=COLLECTION,
query_vector=embed("how do I filter by metadata?"),
limit=5,
with_payload=True,
)
for result in results:
print(f"{result.score:.4f} #{result.id} {result.payload['text']}")
client.close()
if __name__ == "__main__":
main()The complete JavaScript/TypeScript script for Aetherfy
Save this as aetherfy-quickstart.ts, fill in embed(), and run it with your
usual TypeScript toolchain. The imports are ESM, so set "type": "module" in
your package.json. It creates the collection, stores three short documents,
searches them, and prints ranked results.
/** Aetherfy quickstart: create a collection, upsert documents, search them. */
import { AetherfyVectorsClient, DistanceMetric } from 'aetherfy-vectors';
const COLLECTION = 'quickstart';
const EMBEDDING_SIZE = 384;
/**
* Turn text into an array of EMBEDDING_SIZE floats.
*
* Aetherfy does not generate embeddings — you bring your own vectors. Replace
* this body with a call to your embedding model, and make sure it returns
* exactly EMBEDDING_SIZE floats.
*/
async function embed(text: string): Promise<number[]> {
throw new Error(`Fill in embed() with your model; got: ${text}`);
}
const DOCUMENTS = [
{ id: 1, text: 'Cosine distance compares the angle between two vectors.' },
{ id: 2, text: 'A collection stores points, and every point has a vector.' },
{ id: 3, text: 'Payload fields let you filter search results by metadata.' },
];
async function main(): Promise<void> {
const client = new AetherfyVectorsClient(); // reads AETHERFY_API_KEY
await client.createCollection(COLLECTION, {
size: EMBEDDING_SIZE,
distance: DistanceMetric.COSINE,
});
const points = await Promise.all(
DOCUMENTS.map(async (doc) => ({
id: doc.id,
vector: await embed(doc.text),
payload: { text: doc.text },
})),
);
await client.upsert(COLLECTION, points);
const queryVector = await embed('how do I filter by metadata?');
const results = await client.search(COLLECTION, queryVector, {
limit: 5,
withPayload: true,
});
for (const result of results) {
console.log(
`${result.score.toFixed(4)} #${result.id} ${result.payload?.text}`,
);
}
client.dispose();
}
main().catch((error) => {
console.error(error);
process.exit(1);
});Three ranked results, best match first, means Aetherfy is working end to end.
What’s next with Aetherfy
- Examples — a cookbook of complete tasks, each in both Python and JS/TS, including bringing your own embeddings.
- SDK reference — every method on both Aetherfy clients.
- Filtering — narrow a search using the payload fields you stored alongside each vector.
- Limits — the quotas and caps your Aetherfy plan applies.
- Deploy an agent — run your own code on Aetherfy, as a service or as a scheduled task.
- Pricing — plans, and which of them include multi-region replication.