Docs / SDKs

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 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 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:

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

  1. 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
  2. Create a virtual environment and install dependencies:
    python -m venv .venv && source .venv/bin/activate
    pip install -r requirements.txt
  3. 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
  4. Run the server:
    # FastAPI
    uvicorn app:app --reload
    
    # Flask
    flask run --port 5001

Requirements

SDKRequirementsDependencies
PythonPython 3.10+httpx
JavaScriptNode.js 18+None (native fetch)

Both SDKs require an aqoon API key. Create one here.