Python SDK for the Agimus Platform Object Store API
Project description
Agimus Python SDK
Official Python SDK for the Agimus Platform Object Store API.
Installation
pip install agimus
Requirements: Python 3.9+, httpx
Quick Start
from agimus import AgimusClient
client = AgimusClient(api_key="agm_your_key_here")
# Query objects
customers = client.objects("Customer").filter(status="active").all()
# Get by primary key
customer = client.objects("Customer").get(123)
# Create
client.objects("Customer").create({"customerId": 1, "name": "Acme Corp"})
# Update
client.objects("Customer").update(1, {"status": "premium"})
# Delete
client.objects("Customer").delete(1)
Core Concepts
Ontology & API Names
The Agimus Object Store is built on an ontology that defines your data model. Each element has an apiName which is what you use in the SDK:
- Entities - Data types (e.g.,
Customer,Order,Product) - Properties - Fields on entities (e.g.,
customerId,name,createdAt) - Links - Relationships between entities, with forward and reverse API names:
- Forward: from source to target (e.g., Customer →
orders→ Order) - Reverse: from target to source (e.g., Order →
customer→ Customer)
- Forward: from source to target (e.g., Customer →
Use client.list_entities() and client.get_entity_schema("EntityName") to discover available entities, properties, and links.
Primary Keys
Every entity has a primary key property defined in the ontology.
Valid PK types: string, integer, long, short, byte
On Create:
- Primary key must be provided in the data
- Value must match the property's type
- Must be unique - duplicates will error
On Get/Update/Delete:
- Pass the PK value directly - accepts both
intandstr - SDK converts to the appropriate type automatically
# These are equivalent
client.objects("Customer").get(123)
client.objects("Customer").get("123")
Property Types
The ontology defines each property's base type:
Primitives:
| Type | Python | JSON | Notes |
|---|---|---|---|
string |
str |
"text" |
|
integer |
int |
123 |
32-bit signed |
long |
int |
123 |
64-bit signed |
short |
int |
123 |
16-bit signed |
byte |
int |
123 |
8-bit signed |
float |
float |
1.5 |
32-bit |
double |
float |
1.5 |
64-bit |
decimal |
str or Decimal |
"123.45" |
Arbitrary precision |
boolean |
bool |
true |
|
date |
str |
"2024-01-15" |
ISO 8601 date |
timestamp |
str |
"2024-01-15T10:30:00Z" |
ISO 8601 datetime |
time |
str |
"10:30:00" |
ISO 8601 time |
bytes |
str |
"base64..." |
Base64 encoded |
Complex types (stored as JSON objects):
| Type | Description |
|---|---|
struct |
Nested object with defined fields |
geopoint |
{"lat": 40.7, "lng": -74.0} |
geoshape |
GeoJSON geometry object |
attachment |
File attachment metadata |
media_reference |
Media file reference |
Arrays: Properties can be arrays (e.g., tags: string[]). Pass as Python lists.
Nullable: Check nullable in the schema. Non-nullable fields are required on create.
Authentication
API keys are created in the Agimus dashboard under Settings > API Access. Keys use the format agm_<prefix>_<secret> and inherit permissions from their associated Service User.
client = AgimusClient(
api_key="agm_...",
timeout=30.0 # Optional: request timeout in seconds (default: 30)
)
Querying
All queries start with client.objects("EntityName") and support method chaining.
Filtering
Use Django-style double-underscore syntax for operators:
# Equals (default)
.filter(status="active")
# Comparison
.filter(age__gt=18) # greater than
.filter(age__gte=18) # greater than or equal
.filter(age__lt=65) # less than
.filter(age__lte=65) # less than or equal
.filter(age__ne=0) # not equal
.filter(age__between=[18, 65]) # between (inclusive)
# Lists
.filter(region__in=["US", "EU"]) # in list
.filter(status__nin=["deleted"]) # not in list
# Strings
.filter(name__like="Acme%") # SQL LIKE (case-sensitive)
.filter(name__ilike="%acme%") # SQL LIKE (case-insensitive)
.filter(name__starts_with="A") # starts with
.filter(name__ends_with="Corp") # ends with
# Null checks
.filter(deletedAt__is_null=True) # is null
.filter(verifiedAt__is_not_null=True) # is not null
# Empty checks (arrays/strings)
.filter(tags__is_empty=True) # is empty
.filter(tags__is_not_empty=True) # is not empty
# Array operations
.filter(tags__contains="vip") # array contains value
.filter(tags__overlaps=["a", "b"]) # arrays overlap
# Multiple filters (AND)
.filter(status="active", region="US")
Sorting
.sort("name") # ascending
.sort("-createdAt") # descending (prefix with -)
.sort("-createdAt", "name") # multiple fields
Alias: .order_by()
Field Selection
# Return only specific fields (use property apiNames)
.fields("customerId", "name", "email").all()
Alias: .select()
Expanding Relations
Include related objects inline using link apiName:
.expand("orders").all()
.expand("orders", "orders.items").all() # nested
# Or on single object fetch
.get(123, expand=["orders"])
Alias: .include()
Pagination
# Limit total results returned
.limit(100).all()
# Set page size for API calls (max 100, default 50)
.page_size(25).all()
# Auto-pagination with iteration
for customer in client.objects("Customer").filter(status="active"):
print(customer["name"])
Executing Queries
.all() # Get all results as list
.first() # Get first result (or None)
.exists() # Check if any results exist (bool)
.count() # Get count of matching objects
.iter() # Iterator with auto-pagination
Single Object Operations
# Get by primary key (raises NotFoundError if not found)
customer = client.objects("Customer").get(123)
# Get by primary key (returns None if not found)
customer = client.objects("Customer").get_or_none(123)
# Get multiple by primary keys (max 100)
result = client.objects("Customer").batch_get([1, 2, 3])
# Returns: {"data": [...], "found": 3, "requested": 3}
Distinct Values
regions = client.objects("Customer").distinct("region")
# Returns: ["US", "EU", "APAC", ...]
# With filter
regions = client.objects("Customer").filter(status="active").distinct("region")
Aggregation
result = client.objects("Order").filter(status="completed").aggregate(
metrics=[
{"op": "count", "alias": "orderCount"},
{"op": "sum", "field": "total", "alias": "revenue"},
{"op": "avg", "field": "total", "alias": "avgOrder"},
],
group_by=[
{"field": "region"},
{"field": "createdAt", "granularity": "month"}
],
sort=["-revenue"],
limit=100
)
Operators: count, count_distinct, sum, avg, min, max, first, last
Time Granularities: year, quarter, month, week, day, hour
Link Traversal
Navigate relationships using the link's API name. Use the forward name when traversing from source entity, reverse name when traversing from target entity.
# Customer -> orders (forward link)
orders = client.objects("Customer").links(123, "orders")
# Returns: {"data": [...], "hasMore": False}
# Order -> customer (reverse link)
customer = client.objects("Order").links(456, "customer")
# With pagination
orders = client.objects("Customer").links(123, "orders", page_size=10, offset=0)
# Count related objects
count = client.objects("Customer").count_links(123, "orders")
Write Operations
Create
customer = client.objects("Customer").create({
"customerId": 1, # PK required
"name": "Acme Corp", # non-nullable fields required
"email": "contact@acme.com",
"status": "active"
})
- Primary key must be included
- All non-nullable properties must be provided
- Property values must match their ontology types
Update
# Partial update - only specified fields change
updated = client.objects("Customer").update(1, {
"status": "premium"
})
- Cannot modify the primary key
- Only include fields you want to change
Upsert
# Create if not exists, update if exists
customer = client.objects("Customer").upsert(1, {
"name": "Acme Corp",
"status": "active"
})
Delete
deleted = client.objects("Customer").delete(1) # Returns: True
Deletes create a tombstone - the object won't appear in queries but the PK can be reused.
Batch Operations
result = client.objects("Customer").batch([
{"op": "create", "data": {"customerId": 1, "name": "Customer 1"}},
{"op": "update", "pk": 2, "data": {"status": "active"}},
{"op": "delete", "pk": 3},
])
# Returns: {"results": [...], "succeeded": 2, "failed": 1}
Schema Discovery
Discover your ontology programmatically:
# List all accessible entities
entities = client.list_entities()
for e in entities:
print(f"{e['apiName']}: {e['displayName']}")
# Get full entity schema
schema = client.get_entity_schema("Customer")
print(f"Primary key: {schema['primaryKey']}")
for prop in schema["properties"]:
print(f" {prop['apiName']}: {prop['baseType']} (nullable: {prop['nullable']})")
for link in schema["links"]:
print(f" -> {link['apiName']}: {link['targetEntity']}")
# Individual schema methods
properties = client.get_properties("Customer")
prop = client.get_property("Customer", "email")
pk = client.get_primary_key("Customer")
links = client.get_links("Customer")
Async Client
For async/await applications:
from agimus import AsyncAgimusClient
async with AsyncAgimusClient(api_key="agm_...") as client:
customers = await client.objects("Customer").filter(status="active").all()
customer = await client.objects("Customer").get(123)
# Async iteration
async for customer in client.objects("Customer").filter(status="active"):
print(customer["name"])
# Write operations
await client.objects("Customer").create({"customerId": 1, "name": "Acme"})
await client.objects("Customer").update(1, {"status": "premium"})
await client.objects("Customer").delete(1)
The async client has identical methods to the sync client.
Error Handling
from agimus import (
AgimusError, # Base class for all errors
AuthenticationError, # Invalid or missing API key (401)
AccessDeniedError, # Permission denied (403)
NotFoundError, # Entity or object not found (404)
ValidationError, # Invalid request data (400/422)
RateLimitError, # Rate limit exceeded (429)
ServerError, # Server error (5xx)
)
try:
customer = client.objects("Customer").get(999)
except NotFoundError as e:
print(f"Not found: {e.entity}")
except ValidationError as e:
print(f"Validation error: {e.message}, field: {e.field}")
except AuthenticationError:
print("Invalid API key")
except AccessDeniedError:
print("Permission denied")
except RateLimitError as e:
print(f"Rate limited. Retry after: {e.retry_after}s")
except ServerError as e:
print(f"Server error ({e.status_code}): {e.message}")
except AgimusError as e:
print(f"API error: {e.message}")
Utility Methods
# Health check
health = client.health()
# {"status": "healthy", "version": "..."}
# Current API key info
me = client.me()
# {"tenantName": "...", "rateLimitPerMinute": 1000, "requestId": "..."}
Context Manager
with AgimusClient(api_key="agm_...") as client:
customers = client.objects("Customer").all()
# Connection automatically closed
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agimus-0.2.0.tar.gz.
File metadata
- Download URL: agimus-0.2.0.tar.gz
- Upload date:
- Size: 24.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11ae7d06c3278987b9ed2a24111f53f15c7f20e802ad8919b6e8516edb2c4931
|
|
| MD5 |
a4943631035227ada2512390b4adbd2c
|
|
| BLAKE2b-256 |
0e8a0359572fc192c1ea0509de88e8ae7f61c3df6129a9a861457c82bb4eea4e
|
File details
Details for the file agimus-0.2.0-py3-none-any.whl.
File metadata
- Download URL: agimus-0.2.0-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a9a30ae7bcde61eadd00349a471f75ba4342b92e036b0b047430bc026f7aed2
|
|
| MD5 |
2af36e7ae5bbaa284ddb9a70ae0aba89
|
|
| BLAKE2b-256 |
e526e7571a6b07e911081fa8454a72366dd78d259623576cc9df3385e05b6e88
|