Skip to content

API Reference

The Spellbook API provides a comprehensive set of endpoints for interacting with Temporal Bayesian Knowledge Graphs. This reference documentation covers all available endpoints, request/response formats, authentication methods, and the reasoning behind our API design decisions.

The Spellbook API was designed with several key principles in mind:

We chose a RESTful architecture for our API because it provides:

  1. Familiarity: Most developers are already familiar with REST conventions
  2. Cacheability: Responses can be cached to improve performance
  3. Statelessness: Each request contains all information needed for processing
  4. Resource-oriented: Aligns naturally with knowledge graph entities

While GraphQL offers advantages for complex queries, our REST API includes specialized endpoints for graph traversal that provide similar capabilities with better performance characteristics for our specific use case.

The /v1 in our base URL reflects our commitment to API stability. We follow semantic versioning principles:

  • Patch changes (e.g., 1.0.1): Bug fixes that don’t affect the API
  • Minor changes (e.g., 1.1.0): Backward-compatible additions
  • Major changes (e.g., 2.0.0): Breaking changes that require a new version path

This approach ensures that your applications won’t break when we make improvements to the API.

All API endpoints are accessible under the base URL:

https://spellbook.magick.ai/api/v1

Authentication is required for all API endpoints. Spellbook uses API keys for authentication.

We chose API key authentication for several reasons:

  1. Simplicity: Easy to implement and use
  2. Performance: Minimal overhead for each request
  3. Security: Keys can be rotated and revoked as needed
  4. Granular control: Different keys can have different permissions

For applications requiring OAuth flows (e.g., user-specific access), we provide OAuth 2.0 support as well, documented in our Advanced Authentication guide.

To authenticate your requests, include your API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

You can obtain an API key from the Spellbook Dashboard.

When working with API keys:

  • Never expose your API key in client-side code
  • Use environment variables to store keys in your applications
  • Create separate keys for development and production
  • Rotate keys periodically for enhanced security

Before diving into specific endpoints, it’s important to understand the core concepts of our API:

Our API distinguishes between three fundamental types of resources:

  1. Memories: Unstructured or semi-structured information with temporal context
  2. Entities: Distinct objects or concepts with properties
  3. Relationships: Connections between entities with their own properties

This separation allows for flexible knowledge representation while maintaining a clean API structure.

Almost every resource in Spellbook includes a confidence score (0-1), representing:

  • How certain we are about the information
  • The reliability of the source
  • The consistency with existing knowledge

Understanding and utilizing these confidence scores is crucial for building robust applications.

Memories are the foundation of temporal knowledge in Spellbook. They represent observations or pieces of information that have a specific temporal context.

Traditional knowledge graphs struggle with temporal information and unstructured content. Our memory system addresses this by:

  1. Capturing temporal context explicitly
  2. Allowing for natural language content
  3. Supporting automatic entity and relationship extraction
  4. Maintaining provenance information
POST /memories

Creates a new memory in the knowledge graph.

Request Body:

{
"content": "Alice met Bob at the conference",
"confidence": 0.85,
"timestamp": "2025-05-01T14:30:00Z",
"metadata": {
"source": "user_input",
"tags": ["meeting", "conference"]
}
}

Field Explanations:

  • content: The actual information being stored (required)
  • confidence: How certain we are about this information (0-1, default: 1.0)
  • timestamp: When this information was true/observed (default: current time)
  • metadata: Additional context about the memory
    • source: Where this information came from
    • tags: Categorization for easier retrieval

Response:

{
"id": "mem_1a2b3c4d5e6f",
"content": "Alice met Bob at the conference",
"confidence": 0.85,
"timestamp": "2025-05-01T14:30:00Z",
"created_at": "2025-05-01T14:32:15Z",
"metadata": {
"source": "user_input",
"tags": ["meeting", "conference"]
}
}

Implementation Notes:

When you create a memory, several processes happen behind the scenes:

  1. Entity extraction: The system identifies potential entities (e.g., “Alice”, “Bob”, “conference”)
  2. Relationship inference: Relationships between entities are inferred (e.g., “Alice” → “met” → “Bob”)
  3. Temporal indexing: The memory is indexed by its timestamp for efficient temporal queries
  4. Confidence propagation: The memory’s confidence affects derived entities and relationships
GET /memories/{memory_id}

Retrieves a specific memory by ID.

Why IDs Matter:

We use opaque, prefixed IDs (e.g., mem_1a2b3c4d5e6f) for several reasons:

  1. Type safety: The prefix indicates the resource type
  2. Security: IDs don’t expose implementation details
  3. Flexibility: We can change the underlying ID generation without breaking the API

Response:

{
"id": "mem_1a2b3c4d5e6f",
"content": "Alice met Bob at the conference",
"confidence": 0.85,
"timestamp": "2025-05-01T14:30:00Z",
"created_at": "2025-05-01T14:32:15Z",
"metadata": {
"source": "user_input",
"tags": ["meeting", "conference"]
}
}
GET /memories

Lists all memories, with optional filtering.

Query Parameters:

  • limit (integer): Maximum number of memories to return (default: 20, max: 100)
  • offset (integer): Number of memories to skip (default: 0)
  • start_time (ISO datetime): Filter memories from this time
  • end_time (ISO datetime): Filter memories until this time
  • tags (comma-separated): Filter by tags
  • min_confidence (float): Minimum confidence score (0-1)

Pagination Design:

We use offset-based pagination for simplicity and compatibility with most client libraries. For high-volume applications, we also support cursor-based pagination via the cursor parameter (see Advanced Querying guide).

Response:

{
"memories": [
{
"id": "mem_1a2b3c4d5e6f",
"content": "Alice met Bob at the conference",
"confidence": 0.85,
"timestamp": "2025-05-01T14:30:00Z",
"created_at": "2025-05-01T14:32:15Z",
"metadata": {
"source": "user_input",
"tags": ["meeting", "conference"]
}
},
// More memories...
],
"total": 42,
"limit": 20,
"offset": 0
}

Performance Considerations:

When querying memories, consider:

  1. Time ranges: Narrowing the time range significantly improves performance
  2. Confidence filtering: Using min_confidence can reduce result size
  3. Tag filtering: Tags are indexed for fast retrieval

Entities represent distinct objects, people, concepts, or things in your knowledge graph. They form the nodes in the graph structure.

While memories can contain implicit entities, explicit entity management provides:

  1. Deduplication: Consolidate information about the same entity
  2. Property management: Attach and update properties directly
  3. Type hierarchies: Organize entities into types and subtypes
  4. Reference stability: Entities persist even as memories change
POST /entities

Creates a new entity in the knowledge graph.

Request Body:

{
"name": "Alice Smith",
"type": "person",
"properties": {
"age": 32,
"occupation": "Software Engineer"
},
"confidence": 0.95,
"metadata": {
"source": "user_input"
}
}

Field Explanations:

  • name: Human-readable identifier for the entity (required)
  • type: Category or class of the entity (required)
  • properties: Key-value pairs describing the entity
  • confidence: Overall confidence in this entity’s existence (0-1)
  • metadata: Additional context about the entity

Response:

{
"id": "ent_2b3c4d5e6f7g",
"name": "Alice Smith",
"type": "person",
"properties": {
"age": 32,
"occupation": "Software Engineer"
},
"confidence": 0.95,
"created_at": "2025-05-01T14:35:22Z",
"metadata": {
"source": "user_input"
}
}

Implementation Notes:

When creating entities:

  1. Property validation: Properties are validated against type schemas (if defined)
  2. Entity resolution: The system checks for potential duplicates
  3. Temporal tracking: Property changes are tracked over time
  4. Inference triggers: New entities may trigger inference rules

Relationships connect entities and form the edges in your knowledge graph. They can have their own properties and temporal context.

POST /relationships

Creates a new relationship between entities.

Request Body:

{
"source_id": "ent_2b3c4d5e6f7g",
"target_id": "ent_3c4d5e6f7g8h",
"type": "knows",
"properties": {
"since": "2020-01-15",
"context": "Work"
},
"confidence": 0.85,
"timestamp": "2025-05-01T14:30:00Z",
"metadata": {
"source": "user_input"
}
}

Response:

{
"id": "rel_4d5e6f7g8h9i",
"source_id": "ent_2b3c4d5e6f7g",
"target_id": "ent_3c4d5e6f7g8h",
"type": "knows",
"properties": {
"since": "2020-01-15",
"context": "Work"
},
"confidence": 0.85,
"timestamp": "2025-05-01T14:30:00Z",
"created_at": "2025-05-01T14:36:45Z",
"metadata": {
"source": "user_input"
}
}

OH MY NEPTUNE! Graph traversal is the MOST AMAZING feature of Spellbook! It lets you hop-hop-hop through your knowledge graph like a jellyfish dancing through Jellyfish Fields!

POST /query/traverse

Request Body (The Secret Formula):

{
"start_entity_id": "ent_2b3c4d5e6f7g",
"pattern": [
{
"relationship": "works_at",
"direction": "outgoing"
},
{
"relationship": "located_in",
"direction": "outgoing"
}
],
"max_depth": 2,
"min_confidence": 0.7,
"time_point": "2025-05-01T00:00:00Z"
}

This query is like following a TREASURE MAP! It finds where a person works and then where that workplace is located, as of May 1, 2025. It’s like connecting the dots, except the dots are KNOWLEDGE and the lines are RELATIONSHIPS! Isn’t that just coral-tastic?

For those times when JSON patterns make your brain feel like BARNACLES, you can use natural language instead! It’s as easy as talking to Gary!

POST /query/natural

Request Body (Just Ask a Question!):

{
"query": "Where did Alice work in 2024?",
"context_entity_ids": ["ent_2b3c4d5e6f7g"],
"min_confidence": 0.6
}

Behind the scenes, we translate your question into a fancy graph pattern. It’s like MAGIC, except it’s actually advanced natural language processing! But you don’t need to worry about that - just ask your question and let us do the heavy lifting. That’s what friends are for!

The API uses standard HTTP status codes to indicate the success or failure of requests.

Status CodeDescriptionWhen You’ll See It
200OK - The request was successfulAfter successful GET, PUT, PATCH operations
201Created - A new resource was createdAfter successful POST operations that create resources
400Bad Request - The request was invalidWhen request validation fails (e.g., invalid JSON, missing required fields)
401Unauthorized - Authentication failedWhen API key is missing or invalid
403Forbidden - You don’t have permissionWhen API key doesn’t have permission for the requested operation
404Not Found - The resource was not foundWhen requesting a non-existent resource by ID
429Too Many Requests - Rate limit exceededWhen you’ve exceeded your rate limits
500Internal Server Error - Something went wrongWhen an unexpected error occurs on our servers

We provide detailed error messages because:

  1. Developer experience: Clear errors speed up debugging
  2. Self-service: Most issues can be resolved without contacting support
  3. Education: Error messages help you learn the API

Error responses include a JSON body with details:

{
"error": {
"code": "invalid_request",
"message": "The request was invalid",
"details": "The 'confidence' field must be a number between 0 and 1",
"help_url": "https://docs.spellbook.magick.ai/errors/invalid_request"
}
}

API requests are subject to rate limiting to ensure fair usage and system stability.

Rate limits serve several important purposes:

  1. System stability: Prevent any single client from overwhelming the system
  2. Fair usage: Ensure resources are available to all users
  3. Abuse prevention: Mitigate potential denial-of-service attacks
  4. Cost control: Help you manage your API usage

The current limits are:

  • 100 requests per minute per API key
  • 5,000 requests per day per API key

Higher limits are available on enterprise plans.

Rate limit information is included in the response headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1620000000

To work effectively within rate limits:

  1. Implement backoff: When you receive a 429, wait before retrying
  2. Batch operations: Use bulk endpoints where available
  3. Cache responses: Store results that don’t change frequently
  4. Monitor usage: Track your usage against your limits

Spellbook provides official client libraries for several programming languages:

Our official SDKs provide several advantages:

  1. Type safety: Properly typed interfaces for your language
  2. Error handling: Consistent error handling patterns
  3. Authentication: Built-in API key management
  4. Rate limiting: Automatic retry with exponential backoff
  5. Pagination: Helpers for working with paginated results

Spellbook can send webhook notifications when certain events occur in your knowledge graph.

Webhooks enable real-time integration by:

  1. Eliminating polling: No need to constantly check for changes
  2. Enabling event-driven architecture: React to changes as they happen
  3. Reducing latency: Get notified immediately when important events occur

Configure webhooks in the Spellbook Dashboard.

To ensure webhook security:

  1. Verify signatures: All webhooks include an HMAC signature
  2. Use HTTPS: Only HTTPS endpoints are supported
  3. Implement timeouts: Discard events with old timestamps
Section titled “Creating a Memory and Querying Related Entities”

This example shows how to create a memory and then query entities extracted from it:

# Using the Python SDK
from spellbook import Spellbook
client = Spellbook(api_key="your_api_key")
# Create a memory
memory = client.memories.create(
content="Alice started working at Acme Corp in January 2025",
confidence=0.9,
timestamp="2025-01-15T09:00:00Z"
)
# Wait for processing (in production, use webhooks instead)
import time
time.sleep(2)
# Query entities related to this memory
entities = client.memories.get_related_entities(memory.id)
# Print the extracted entities
for entity in entities:
print(f"{entity.name} ({entity.type}): {entity.confidence}")

For high-throughput applications, use batch endpoints:

POST /memories/batch
{
"memories": [
{
"content": "Alice met Bob at the conference",
"confidence": 0.85,
"timestamp": "2025-05-01T14:30:00Z"
},
{
"content": "Bob works at Acme Corp",
"confidence": 0.9,
"timestamp": "2025-05-01T14:35:00Z"
}
]
}

Implement effective caching by:

  1. Respecting Cache-Control headers: We set appropriate caching directives
  2. Using ETags: Check if resources have changed before fetching again
  3. Caching immutable resources: Historical data doesn’t change