SDKs
Official client libraries for Python and JavaScript. Install, authenticate, and start searching in minutes.
Python PyPI
Installation
pip install aqoon
Quick Start
from aqoon import Aqoon
client = Aqoon(api_key="aqn_your_api_key")
# Search your knowledge base
results = client.search("machine learning basics")
for r in results["results"]:
print(f"{r['title']} (score: {r['score']:.3f})")
print(f" {r['content'][:100]}...")
Search
# Search all collections
results = client.search("your query")
# Search a specific collection
results = client.search("your query", collection="my-docs")
# Limit results
results = client.search("your query", limit=10)
Collections & Documents
# List collections
collections = client.list_collections()
# Create a new collection
collection = client.create_collection("My Docs", description="Optional description")
print(collection["uuid"]) # use this uuid to upload documents
# Rename a collection (or update description)
updated = client.update_collection("collection-uuid", name="Renamed Docs")
# Delete a collection (soft-delete; cascades to all documents)
client.delete_collection("collection-uuid")
# List documents with filters
docs = client.list_documents(collection="my-docs", tag="important")
# Get document with content and chunks
doc = client.get_document("document-uuid")
print(doc["content_text"])
# Get a signed download URL for the original file (valid 1 hour)
url = client.get_download_url("document-uuid")
Uploading Documents
# Upload a single document
doc = client.upload_document(
"collection-uuid",
"path/to/report.pdf",
title="Q1 Report",
tags="finance,quarterly",
)
print(doc["status"]) # "pending"
# Upload multiple documents
result = client.upload_documents(
"collection-uuid",
["file1.pdf", "file2.docx", "notes.md"],
)
print(f"Uploaded: {result['total_uploaded']}, Skipped: {result['total_skipped']}")
# Replace an existing document (UUID preserved, old index entries removed)
doc = client.replace_document(
"document-uuid",
"path/to/updated-report.pdf",
title="Q1 Report (Revised)",
)
print(doc["status"]) # "pending"
API Keys
# Create a full-access key
new_key = client.create_key("My Key", is_full_access=True)
print(f"Key: {new_key['key']}") # Only shown once!
# Create a scoped key
scoped = client.create_key("Scoped", collections=["docs", "policies"])
# Grant an existing key access to a collection
client.grant_collection(key_uuid, "slug", permission="write")
# Usage stats
stats = client.usage(days=7)
print(f"Total requests: {stats['total_requests']}")
Webhooks
# Create or update webhook (returns secret on first create)
result = client.set_webhook("https://your-app.example.com/hooks/aqoon")
if "secret" in result:
print(f"Save this secret once: {result['secret']}")
# Get current config (secret never returned)
webhook = client.get_webhook()
print(webhook["url"], webhook["is_active"])
# Send a test event to your endpoint
client.test_webhook()
# Remove webhook
client.delete_webhook()
Error Handling
from aqoon import Aqoon, NotFoundError, RateLimitError
client = Aqoon(api_key="aqn_your_key")
try:
doc = client.get_document("nonexistent")
except NotFoundError:
print("Document not found")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after}s")
Available exceptions: AuthenticationError (401), ForbiddenError (403), NotFoundError (404), ValidationError (400), RateLimitError (429), ServerError (5xx).
Every error response now includes a machine-readable code field. Access it from the response body for precise programmatic handling:
from aqoon import Aqoon, NotFoundError, AuthenticationError
client = Aqoon(api_key="aqn_your_key")
try:
results = client.search("query")
except NotFoundError as e:
print(e.response.json()["code"]) # "COLLECTION_NOT_FOUND"
except AuthenticationError as e:
code = e.response.json()["code"] # "INVALID_API_KEY" or "API_KEY_EXPIRED"
print(f"Auth failed: {code}")
JavaScript / TypeScript npm
Installation
npm install @54startups/aqoon
Quick Start
import { Aqoon } from '@54startups/aqoon';
const client = new Aqoon({ apiKey: 'aqn_your_api_key' });
// Search your knowledge base
const results = await client.search('machine learning basics');
for (const r of results.results) {
console.log(`${r.title} (score: ${r.score.toFixed(3)})`);
console.log(` ${r.content.slice(0, 100)}...`);
}
Search
// Search all collections
const results = await client.search('your query');
// Search a specific collection
const results = await client.search('your query', { collection: 'my-docs' });
// Limit results
const results = await client.search('your query', { limit: 10 });
Collections & Documents
// List collections
const collections = await client.listCollections();
// Create a new collection
const collection = await client.createCollection("My Docs", { description: "Optional description" });
console.log(collection.uuid); // use this uuid to upload documents
// Rename a collection (or update description)
const updated = await client.updateCollection("collection-uuid", { name: "Renamed Docs" });
// Delete a collection (soft-delete; cascades to all documents)
await client.deleteCollection("collection-uuid");
// List documents with filters
const docs = await client.listDocuments({ collection: 'my-docs', tag: 'important' });
// Get document with content and chunks
const doc = await client.getDocument('document-uuid');
console.log(doc.content_text);
// Get a signed download URL for the original file (valid 1 hour)
const url = await client.getDownloadUrl(uuid);
Uploading Documents
// Upload a single document
const doc = await client.uploadDocument(
'collection-uuid',
file,
{ title: 'Q1 Report', tags: 'finance,quarterly' }
);
console.log(doc.status); // "pending"
// Upload multiple documents
const result = await client.uploadDocuments(
'collection-uuid',
[file1, file2, file3]
);
// Replace an existing document (UUID preserved, old index entries removed)
const updated = await client.replaceDocument(
'document-uuid',
file,
{ title: 'Q1 Report (Revised)' }
);
console.log(updated.status); // "pending"
API Keys
// Create a full-access key
const newKey = await client.createKey('My Key', { isFullAccess: true });
console.log(`Key: ${newKey.key}`); // Only shown once!
// Create a scoped key
const scoped = await client.createKey('Scoped', {
collections: ['docs', 'policies'],
});
// Grant an existing key access to a collection
await client.grantCollection(keyUuid, 'slug', { permission: 'write' });
// Usage stats
const stats = await client.usage({ days: 7 });
console.log(`Total requests: ${stats.total_requests}`);
Webhooks
// Create or update webhook (returns secret on first create)
const result = await client.setWebhook('https://your-app.example.com/hooks/aqoon');
if (result.secret) {
console.log(`Save this secret once: ${result.secret}`);
}
// Get current config (secret never returned)
const webhook = await client.getWebhook();
console.log(webhook.url, webhook.is_active);
// Send a test event to your endpoint
await client.testWebhook();
// Remove webhook
await client.deleteWebhook();
Error Handling
import { Aqoon, NotFoundError, RateLimitError } from '@54startups/aqoon';
const client = new Aqoon({ apiKey: 'aqn_your_key' });
try {
const doc = await client.getDocument('nonexistent');
} catch (e) {
if (e instanceof NotFoundError) {
console.log('Document not found');
} else if (e instanceof RateLimitError) {
console.log(`Rate limited. Retry after ${e.retry_after}s`);
}
}
Available exceptions: AuthenticationError (401), ForbiddenError (403), NotFoundError (404), ValidationError (400), RateLimitError (429), ServerError (5xx).
Every error response now includes a machine-readable code field. The SDK exposes it directly on the error object for programmatic handling:
import { Aqoon } from '@54startups/aqoon';
const client = new Aqoon({ apiKey: 'aqn_your_key' });
try {
await client.search('query');
} catch (e) {
if (e.code === 'RATE_LIMIT_EXCEEDED') {
// wait and retry
await sleep(e.retryAfter * 1000);
} else if (e.code === 'COLLECTION_NOT_FOUND') {
console.error('Check your collection slug or UUID');
} else {
throw e;
}
}
Framework Integrations
Drop-in retriever wrappers for the three most popular RAG frameworks. Each transforms aqoon search results into the framework's native document or node format.
Installation
pip install 'aqoon[langchain]' # LangChain
pip install 'aqoon[llamaindex]' # LlamaIndex
pip install 'aqoon[haystack]' # Haystack
pip install 'aqoon[all]' # All frameworks
All three retrievers accept an optional collection (slug) and k (number of results) parameter.
LangChain
from aqoon.integrations.langchain import AqoonRetriever
retriever = AqoonRetriever(api_key="aqn_your_key")
docs = retriever.invoke("machine learning basics")
for doc in docs:
print(f"{doc.metadata['title']} (score: {doc.metadata['score']:.3f})")
print(f" {doc.page_content[:100]}...")
# In a chain:
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
)
Each document has: page_content (the text), metadata.title, metadata.document_id, metadata.score, metadata.collection_name, metadata.tags.
LlamaIndex
from aqoon.integrations.llamaindex import AqoonRetriever
retriever = AqoonRetriever(api_key="aqn_your_key")
nodes = retriever.retrieve("machine learning basics")
for node in nodes:
print(f"{node.score:.3f} — {node.metadata['title']}")
print(f" {node.text[:100]}...")
# In a query engine:
query_engine = RetrieverQueryEngine.from_args(retriever)
response = query_engine.query("What is supervised learning?")
Each node has: node.text (the text), node.score, node.metadata['title'], node.metadata['document_id'], node.metadata['collection_name'], node.metadata['tags'].
Haystack
from aqoon.integrations.haystack import AqoonRetriever
retriever = AqoonRetriever(api_key="aqn_your_key")
result = retriever.run(query="machine learning basics")
for doc in result["documents"]:
print(f"{doc.score:.3f} — {doc.meta['title']}")
print(f" {doc.content[:100]}...")
# In a pipeline:
pipe = Pipeline()
pipe.add_component("retriever", AqoonRetriever(api_key="aqn_..."))
Each document has: doc.content (the text), doc.score, doc.meta['title'], doc.meta['document_id'], doc.meta['collection_name'], doc.meta['tags'].
Starter Templates
Clone-and-run RAG apps that wire up aqoon and Claude. Each template is a minimal, self-contained server you can copy into your project and customize.
Two versions are available:
- FastAPI — github.com/54Startups/aqoon-rag-fastapi
- Flask — github.com/54Startups/aqoon-rag-flask
Both expose a single POST /ask endpoint. Send a question, get back an answer from Claude and the aqoon sources it was based on:
curl -X POST http://localhost:8000/ask \
-H "Content-Type: application/json" \
-d '{"question": "What is our refund policy?"}'
{
"answer": "Your refund policy allows returns within 30 days...",
"sources": [
{"title": "Refund Policy", "score": 0.94},
{"title": "FAQ", "score": 0.87}
]
}
Setup
- Clone the template repo:
# FastAPI git clone https://github.com/54Startups/aqoon-rag-fastapi.git cd aqoon-rag-fastapi # Flask git clone https://github.com/54Startups/aqoon-rag-flask.git cd aqoon-rag-flask - Create a virtual environment and install dependencies:
python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt - Add your API keys to
.env:cp .env.example .env # Edit .env with your keys AQOON_API_KEY=aqn_your_key ANTHROPIC_API_KEY=sk-ant-your_key - Run the server:
# FastAPI uvicorn app:app --reload # Flask flask run --port 5001
Requirements
| SDK | Requirements | Dependencies |
|---|---|---|
| Python | Python 3.10+ | httpx |
| JavaScript | Node.js 18+ | None (native fetch) |
Both SDKs require an aqoon API key. Create one here.