New to Nebula? Start with Core Concepts to understand the architecture.
Storing Memories
from nebula import Nebula
nebula = Nebula()
collection = nebula.collections.retrieve_by_name("research").results
# Single memory
created = nebula.memories.create(
collection_id=collection.id,
raw_text="Machine learning automates model building",
metadata={"topic": "AI"},
).results
memory_id = created.id
# Multiple memories — the SDK has no batch endpoint, so loop on the wire
memory_ids = []
for item in [
{"raw_text": "Neural networks...", "metadata": {"type": "concept"}},
{"raw_text": "Deep learning...", "metadata": {"type": "concept"}},
]:
res = nebula.memories.create(collection_id=collection.id, **item).results
memory_ids.append(res.id)
import Nebula from '@nebula-ai/sdk';
const client = new Nebula({ apiKey: process.env.NEBULA_API_KEY });
const { results: collection } = await client.collections.retrieveByName('research');
// Single memory
const { results: created } = await client.memories.create({
collection_id: collection.id,
raw_text: 'Machine learning automates model building',
metadata: { topic: 'AI' },
});
const memoryId = created.id;
// Multiple memories — the SDK has no batch endpoint; fan out with Promise.all
const items = [
{ raw_text: 'Neural networks...', metadata: { type: 'concept' } },
{ raw_text: 'Deep learning...', metadata: { type: 'concept' } },
];
const memoryIds = (
await Promise.all(
items.map(item =>
client.memories.create({ collection_id: collection.id, ...item })
)
)
).map(r => r.results.id);
curl -X POST "https://api.zeroset.com/v1/memories" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"collection_id": "COLLECTION_ID",
"kind": "document",
"raw_text": "Machine learning automates model building",
"metadata": {"topic": "AI"}
}'
The use of
metadata is highly discouraged. Nebula already consolidates all the semantics from your content automatically.Memory creation is asynchronous. Content becomes searchable after Nebula finishes extraction (typically a few seconds). The API returns a
202 status with the memory ID immediately.Conversation Messages
support = nebula.collections.retrieve_by_name("support").results
# Create conversation
conv = nebula.memories.create(
collection_id=support.id,
kind="conversation",
messages=[{"content": "Hello! How can I help?", "role": "assistant"}],
).results
conv_id = conv.id
# Add to same conversation
nebula.memories.append(
conv_id,
collection_id=support.id,
messages=[{"content": "I need help with my account", "role": "user"}],
)
const { results: support } = await client.collections.retrieveByName('support');
const { results: conv } = await client.memories.create({
collection_id: support.id,
kind: 'conversation',
messages: [{ content: 'Hello! How can I help?', role: 'assistant' }],
});
const convId = conv.id;
await client.memories.append(convId, {
collection_id: support.id,
messages: [{ content: 'I need help with my account', role: 'user' }],
});
curl -X POST "https://api.zeroset.com/v1/memories" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"collection_id": "COLLECTION_ID",
"kind": "conversation",
"messages": [
{"role": "assistant", "content": "Hello! How can I help?"},
{"role": "user", "content": "I need help with my account"}
]
}'
Document Upload
Upload a document as raw text, pre-chunked text, or a file. File processing (OCR/transcription/text extraction) happens automatically.from nebula import Nebula
nebula = Nebula()
collection = nebula.collections.retrieve_by_name("my-collection").results
# Upload text
doc = nebula.memories.create(
collection_id=collection.id,
raw_text="Machine learning is a subset of AI...",
kind="document",
metadata={"title": "ML Intro"},
).results
doc_id = doc.id
# Upload pre-chunked content
doc = nebula.memories.create(
collection_id=collection.id,
chunks=["Chapter 1...", "Chapter 2..."],
kind="document",
metadata={"title": "My Doc"},
).results
doc_id = doc.id
import Nebula from '@nebula-ai/sdk';
const client = new Nebula({ apiKey: process.env.NEBULA_API_KEY });
const { results: collection } = await client.collections.retrieveByName('my-collection');
// Upload text
const { results: doc } = await client.memories.create({
collection_id: collection.id,
raw_text: 'Machine learning is a subset of AI...',
kind: 'document',
metadata: { title: 'ML Intro' },
});
const docId = doc.id;
// Upload pre-chunked content
const { results: doc2 } = await client.memories.create({
collection_id: collection.id,
chunks: ['Chapter 1...', 'Chapter 2...'],
kind: 'document',
metadata: { title: 'My Doc' },
});
const docId2 = doc2.id;
# Upload file (base64 encoded)
curl -X POST "https://api.zeroset.com/v1/memories" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"collection_id": "COLLECTION_ID",
"content_parts": [{
"type": "document",
"data": "BASE64_ENCODED_FILE_DATA",
"media_type": "application/pdf",
"filename": "document.pdf"
}],
"metadata": {"title": "Research Paper"}
}'
Inline base64 uploads are limited to ~5MB per file part; larger files use a presigned upload flow (max 100MB).
Multiple Documents
The Python SDK exposes a singlememories.create() per memory; loop to store multiple documents. To group conversation turns, create one memory with kind="conversation" and a messages list.
from nebula import Nebula
nebula = Nebula()
# Store multiple documents
ids = []
for text in ["First document", "Second document", "Third document"]:
res = nebula.memories.create(collection_id=collection.id, raw_text=text).results
ids.append(res.id)
# Store an entire conversation in one memory
conv = nebula.memories.create(
collection_id=collection.id,
kind="conversation",
messages=[
{"content": "Hello!", "role": "user"},
{"content": "Hi there!", "role": "assistant"},
],
).results
// Store multiple documents
const texts = ['First document', 'Second document', 'Third document'];
const ids = (
await Promise.all(
texts.map(raw_text =>
client.memories.create({ collection_id: collection.id, raw_text })
)
)
).map(r => r.results.id);
// Store an entire conversation in one memory
const { results: conv } = await client.memories.create({
collection_id: collection.id,
kind: 'conversation',
messages: [
{ content: 'Hello!', role: 'user' },
{ content: 'Hi there!', role: 'assistant' },
],
});
Retrieving Memories
# Get by ID
memory = nebula.memories.retrieve(memory_id).results
# List memories in collection
memories = nebula.memories.list(collection_ids=[collection.id], limit=50).results
// Get by ID
const { results: memory } = await client.memories.retrieve(memoryId);
// List memories in collection
const { results: memories } = await client.memories.list({
collection_ids: [collection.id],
limit: 50,
});
# Get by ID
curl -X GET "https://api.zeroset.com/v1/memories/YOUR_MEMORY_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
# List memories
curl -X GET "https://api.zeroset.com/v1/memories?limit=50" \
-H "Authorization: Bearer YOUR_API_KEY"
For semantic search (finding by meaning), see the Search Guide.
Deleting Memories
# Delete single
nebula.memories.delete(memory_id)
# Delete multiple
nebula.memories.delete_many(body=[memory_id_1, memory_id_2, memory_id_3])
await client.memories.delete(memoryId);
await client.memories.deleteMany({ body: [memoryId1, memoryId2, memoryId3] });
curl -X DELETE "https://api.zeroset.com/v1/memories/YOUR_MEMORY_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
Deletion is permanent and cannot be undone.
Bulk Operations
Usememories.list() with metadata_filters to target a set, then delete or update metadata in batches. The endpoint caps limit at 1000 per request, so larger result sets need pagination.
For high-fan-out client work, throttle with chunked
Promise.all (e.g., p-limit) instead of one unbounded call. The TS SDK already retries 429s with backoff, but bounded concurrency keeps socket pressure and tail latency reasonable.import json
docs = nebula.collections.retrieve_by_name("docs").results
filters = json.dumps({"metadata.status": {"$eq": "archived"}})
PAGE = 1000 # endpoint maximum
# Bulk delete: re-list with offset=0 each iteration since the previous
# page is gone after delete_many.
while True:
page = nebula.memories.list(
collection_ids=[docs.id],
metadata_filters=filters,
limit=PAGE,
)
if not page.results:
break
nebula.memories.delete_many(body=[m.id for m in page.results])
if len(page.results) < PAGE:
break
# Bulk metadata update: rows aren't removed, so walk by offset until
# total_entries is covered.
offset = 0
while True:
page = nebula.memories.list(
collection_ids=[docs.id],
metadata_filters=filters,
limit=PAGE,
offset=offset,
)
for m in page.results:
nebula.memories.update(
m.id,
metadata={"archived": True},
merge_metadata=True,
)
offset += len(page.results)
if not page.results or offset >= page.total_entries:
break
const { results: docs } = await client.collections.retrieveByName('docs');
const filters = JSON.stringify({ 'metadata.status': { $eq: 'archived' } });
const PAGE = 1000; // endpoint maximum
// Bulk delete: re-list with offset=0 each iteration.
while (true) {
const page = await client.memories.list({
collection_ids: [docs.id],
metadata_filters: filters,
limit: PAGE,
});
if (page.results.length === 0) break;
await client.memories.deleteMany({ body: page.results.map(m => m.id) });
if (page.results.length < PAGE) break;
}
// Bulk metadata update: walk by offset until total_entries is covered.
let offset = 0;
while (true) {
const page = await client.memories.list({
collection_ids: [docs.id],
metadata_filters: filters,
limit: PAGE,
offset,
});
await Promise.all(
page.results.map(m =>
client.memories.update(m.id, {
metadata: { archived: true },
merge_metadata: true,
})
)
);
offset += page.results.length;
if (page.results.length === 0 || offset >= page.total_entries) break;
}
curl -G "https://api.zeroset.com/v1/memories" \
-H "Authorization: Bearer YOUR_API_KEY" \
--data-urlencode 'collection_ids=COLLECTION_ID' \
--data-urlencode 'limit=1000' \
--data-urlencode 'offset=0' \
--data-urlencode 'metadata_filters={"metadata.status":{"$eq":"archived"}}'
curl -X POST "https://api.zeroset.com/v1/memories/delete" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '["MEMORY_ID_1", "MEMORY_ID_2"]'
Use
memories.append() to append content. memories.update() only updates name, metadata, or collection associations.Source Operations
Memories contain sources (messages in conversations, sections in documents). The retrieve response exposes them onchunks as read-only provenance — opaque records you can inspect but not edit in place. To change stored content, update the memory metadata or append replacement content as a new memory entry.
memory = nebula.memories.retrieve(memory_id).results
# `chunks` is typed `List[object]`; on retrieve each chunk is the chunk text string.
for chunk in memory.chunks or []:
print(chunk)
const { results: memory } = await client.memories.retrieve(memoryId);
// `chunks` is typed `unknown[]`; on retrieve each chunk is the chunk text string.
memory.chunks?.forEach(chunk => console.log(chunk));
# Get memory with chunks
curl -X GET "https://api.zeroset.com/v1/memories/YOUR_MEMORY_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
Next Steps
- Search - Semantic search and filtering
- Collections - Organize memories into collections