AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The azure-cosmos-py skill provides the Python client library for Azure Cosmos DB NoSQL API. It enables developers to perform CRUD operations, manage databases and containers, execute queries, and handle throughput settings. The skill emphasizes using DefaultAzureCredential for authentication and context managers for resource lifecycle management, while providing guidance on partition key design and query optimization patterns.
What it does
- Provides client classes for account, database, and container operations.
- Supports document CRUD operations including create, read, replace, upsert, and delete.
- Enables SQL-like querying of data with support for parameters and projections.
- Facilitates management of container throughput and partition key configuration.
- Offers both synchronous and asynchronous client implementations.
- Includes patterns for pagination and handling hierarchical partition keys.
When to use it
- When developing Python applications that need to interact with Azure Cosmos DB.
- When implementing data access layers for NoSQL document storage.
- When designing partition strategies for data distribution.
- When optimizing query performance through parameterized queries and efficient partition key usage.
Advantages
- Provides a unified interface for both sync and async operations.
- Integrates with Azure identity services for credential management.
- Includes built-in support for handling common database exceptions.
- Offers clear patterns for managing resource lifecycles using context managers.
Usage examples
- “How do I create a new item in an Azure Cosmos DB container using Python?”
- The agent provides a code snippet using container.create_item() with a dictionary representing the item, ensuring the partition key is included.
- “How can I query items in Cosmos DB while avoiding cross-partition overhead?”
- The agent demonstrates how to use container.query_items() by specifying the partition_key parameter to target a single partition.
- “What is the best way to handle authentication in my Cosmos DB Python code?”
- The agent explains the use of DefaultAzureCredential and provides a code example showing how to pass it to the CosmosClient.
- “How do I implement pagination for a large query result set?”
- The agent provides a function using continuation tokens with query_items() to iterate through results page by page.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/microsoft/azure-cosmos-py/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip azure-cosmos-py.zip -d .agents/skills
Documentation by Microsoft
Azure Cosmos DB SDK for Python
Client library for Azure Cosmos DB NoSQL API — globally distributed, multi-model database.
Installation
pip install azure-cosmos azure-identityEnvironment Variables
COSMOS_ENDPOINT=https://<account>.documents.azure.com:443/ # Required for all auth methods
COSMOS_DATABASE=mydb # Required for all auth methods
COSMOS_CONTAINER=mycontainer # Required for all auth methods
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in productionAuthentication & Lifecycle
🔑 Two rules apply to every code sample below:
Prefer
DefaultAzureCredential. It works locally (Azure CLI / VS Code / Developer CLI) and in Azure (managed identity, workload identity) with no code change. Avoid connection strings, account/API keys — they bypass Entra audit and rotation.
Local dev:
DefaultAzureCredentialworks as-is.Production: set
AZURE_TOKEN_CREDENTIALS=prod(orAZURE_TOKEN_CREDENTIALS=<specific_credential>) to constrain the credential chain to production-safe credentials.Wrap every client in a context manager so HTTP transports, sockets, and token caches are released deterministically:
Sync:
with <Client>(...) as client:Async:
async with <Client>(...) as client:andasync with DefaultAzureCredential() as credential:(fromazure.identity.aio)Snippets may abbreviate this setup, but production code should always follow both rules.
import os
from azure.identity import DefaultAzureCredential, ManagedIdentityCredential
from azure.cosmos import CosmosClient
# Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
credential = DefaultAzureCredential(require_envvar=True)
# Or use a specific credential directly in production:
# See https://learn.microsoft.com/python/api/overview/azure/identity-readme?view=azure-python#credential-classes
# credential = ManagedIdentityCredential()
endpoint = "https://<account>.documents.azure.com:443/"
with CosmosClient(url=endpoint, credential=credential) as client:
# Use client here (see following sections for operations)
...Client Hierarchy
| Client | Purpose | Get From |
|---|---|---|
CosmosClient | Account-level operations | Direct instantiation |
DatabaseProxy | Database operations | client.get_database_client() |
ContainerProxy | Container/item operations | database.get_container_client() |
Core Workflow
Setup Database and Container
# Get or create database
database = client.create_database_if_not_exists(id="mydb")
# Get or create container with partition key
container = database.create_container_if_not_exists(
id="mycontainer",
partition_key=PartitionKey(path="/category")
)
# Get existing
database = client.get_database_client("mydb")
container = database.get_container_client("mycontainer")Create Item
item = {
"id": "item-001", # Required: unique within partition
"category": "electronics", # Partition key value
"name": "Laptop",
"price": 999.99,
"tags": ["computer", "portable"]
}
created = container.create_item(body=item)
print(f"Created: {created['id']}")Read Item
# Read requires id AND partition key
item = container.read_item(
item="item-001",
partition_key="electronics"
)
print(f"Name: {item['name']}")Update Item (Replace)
item = container.read_item(item="item-001", partition_key="electronics")
item["price"] = 899.99
item["on_sale"] = True
updated = container.replace_item(item=item["id"], body=item)Upsert Item
# Create if not exists, replace if exists
item = {
"id": "item-002",
"category": "electronics",
"name": "Tablet",
"price": 499.99
}
result = container.upsert_item(body=item)Delete Item
container.delete_item(
item="item-001",
partition_key="electronics"
)Queries
Basic Query
# Query within a partition (efficient)
query = "SELECT * FROM c WHERE c.price < @max_price"
items = container.query_items(
query=query,
parameters=[{"name": "@max_price", "value": 500}],
partition_key="electronics"
)
for item in items:
print(f"{item['name']}: ${item['price']}")Cross-Partition Query
# Cross-partition (more expensive, use sparingly)
query = "SELECT * FROM c WHERE c.price < @max_price"
items = container.query_items(
query=query,
parameters=[{"name": "@max_price", "value": 500}],
enable_cross_partition_query=True
)
for item in items:
print(item)Query with Projection
query = "SELECT c.id, c.name, c.price FROM c WHERE c.category = @category"
items = container.query_items(
query=query,
parameters=[{"name": "@category", "value": "electronics"}],
partition_key="electronics"
)Read All Items
# Read all in a partition
items = container.read_all_items() # Cross-partition
# Or with partition key
items = container.query_items(
query="SELECT * FROM c",
partition_key="electronics"
)Partition Keys
Critical: Always include partition key for efficient operations.
from azure.cosmos import PartitionKey
# Single partition key
container = database.create_container_if_not_exists(
id="orders",
partition_key=PartitionKey(path="/customer_id")
)
# Hierarchical partition key (preview)
container = database.create_container_if_not_exists(
id="events",
partition_key=PartitionKey(path=["/tenant_id", "/user_id"])
)Throughput
# Create container with provisioned throughput
container = database.create_container_if_not_exists(
id="mycontainer",
partition_key=PartitionKey(path="/pk"),
offer_throughput=400 # RU/s
)
# Read current throughput
offer = container.read_offer()
print(f"Throughput: {offer.offer_throughput} RU/s")
# Update throughput
container.replace_throughput(throughput=1000)Async Client
from azure.cosmos.aio import CosmosClient
from azure.identity.aio import DefaultAzureCredential
async def cosmos_operations():
async with DefaultAzureCredential() as credential:
async with CosmosClient(endpoint, credential=credential) as client:
database = client.get_database_client("mydb")
container = database.get_container_client("mycontainer")
# Create
await container.create_item(body={"id": "1", "pk": "test"})
# Read
item = await container.read_item(item="1", partition_key="test")
# Query
async for item in container.query_items(
query="SELECT * FROM c",
partition_key="test"
):
print(item)
import asyncio
asyncio.run(cosmos_operations())Error Handling
from azure.cosmos.exceptions import CosmosHttpResponseError
try:
item = container.read_item(item="nonexistent", partition_key="pk")
except CosmosHttpResponseError as e:
if e.status_code == 404:
print("Item not found")
elif e.status_code == 429:
print(f"Rate limited. Retry after: {e.headers.get('x-ms-retry-after-ms')}ms")
else:
raiseBest Practices
Pick sync OR async and stay consistent. Do not mix
azure.cosmossync clients withazure.cosmos.aioasync clients in the same call path. Choose one mode per module.Always use context managers for clients and async credentials. Wrap every client in
with CosmosClient(...) as client:(sync) orasync with CosmosClient(...) as client:(async). For asyncDefaultAzureCredentialfromazure.identity.aio, also useasync with credential:so tokens and transports are cleaned up.Use
DefaultAzureCredentialfor portable auth across local dev and Azure (avoid connection strings / API keys when possible).Always specify partition key for point reads and queries
Use parameterized queries to prevent injection and improve caching
Avoid cross-partition queries when possible
Use
upsert_itemfor idempotent writesUse async client for high-throughput scenarios
Design partition key for even data distribution
Use
read_iteminstead of query for single document retrieval
Reference Files
| File | Contents |
|---|---|
| references/partitioning.md | Partition key strategies, hierarchical keys, hot partition detection and mitigation |
| references/query-patterns.md | Query optimization, aggregations, pagination, transactions, change feed |
| scripts/setup_cosmos_container.py | CLI tool for creating containers with partitioning, throughput, and indexing |
Files
- ATTRIBUTION.mdAdded by AutoGineer956 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md9.1 KiB
- references/partitioning.md10.3 KiB
- references/query-patterns.md10.6 KiB
references/query-patterns.md
Query Patterns Reference
Advanced query patterns for Azure Cosmos DB NoSQL API.
Parameterized Queries
Always use parameters to prevent injection and enable query plan caching:
# GOOD: Parameterized query
query = "SELECT * FROM c WHERE c.category = @category AND c.price < @max_price"
items = container.query_items(
query=query,
parameters=[
{"name": "@category", "value": "electronics"},
{"name": "@max_price", "value": 500}
],
partition_key="electronics"
)
# BAD: String interpolation (injection risk, no caching)
category = "electronics"
query = f"SELECT * FROM c WHERE c.category = '{category}'" # Never do this!Query Optimization
Single Partition vs Cross-Partition
# EFFICIENT: Single partition query (always preferred)
items = container.query_items(
query="SELECT * FROM c WHERE c.status = 'active'",
partition_key="tenant-123"
)
# EXPENSIVE: Cross-partition query (use sparingly)
items = container.query_items(
query="SELECT * FROM c WHERE c.status = 'active'",
enable_cross_partition_query=True # Fans out to all partitions
)Point Reads vs Queries
# MOST EFFICIENT: Point read (1 RU for 1KB document)
item = container.read_item(
item="doc-123",
partition_key="tenant-abc"
)
# LESS EFFICIENT: Query for single document (higher RU)
items = list(container.query_items(
query="SELECT * FROM c WHERE c.id = @id",
parameters=[{"name": "@id", "value": "doc-123"}],
partition_key="tenant-abc"
))
item = items[0] if items else NoneProjection for Efficiency
# GOOD: Select only needed fields (less RU, less bandwidth)
query = "SELECT c.id, c.name, c.price FROM c WHERE c.category = @category"
items = container.query_items(
query=query,
parameters=[{"name": "@category", "value": "electronics"}],
partition_key="electronics"
)
# EXPENSIVE: Select all fields when you only need a few
query = "SELECT * FROM c WHERE c.category = @category"Pagination
Continuation Token Pagination
def paginated_query(
container,
query: str,
partition_key: str,
page_size: int = 100,
parameters: list | None = None
):
"""Paginate through query results using continuation tokens."""
continuation_token = None
page_number = 0
while True:
# Create query iterable with pagination
query_iterable = container.query_items(
query=query,
parameters=parameters or [],
partition_key=partition_key,
max_item_count=page_size,
continuation_token=continuation_token
)
# Get page of results
page = list(query_iterable.by_page().__next__())
page_number += 1
print(f"Page {page_number}: {len(page)} items")
yield page
# Get continuation token for next page
continuation_token = query_iterable.continuation_token
if not continuation_token:
break
# Usage
for page in paginated_query(
container,
query="SELECT * FROM c WHERE c.status = 'active'",
partition_key="tenant-123",
page_size=50
):
process_page(page)Offset-Limit Pagination (Not Recommended for Large Datasets)
# Works but inefficient for large offsets (still reads skipped items)
query = "SELECT * FROM c ORDER BY c.created_at DESC OFFSET @offset LIMIT @limit"
items = container.query_items(
query=query,
parameters=[
{"name": "@offset", "value": 100},
{"name": "@limit", "value": 10}
],
partition_key="tenant-123"
)Aggregations
COUNT, SUM, AVG, MIN, MAX
# Count items in partition
query = "SELECT VALUE COUNT(1) FROM c WHERE c.status = 'active'"
result = list(container.query_items(
query=query,
partition_key="tenant-123"
))
count = result[0] # Returns integer directly with VALUE
# Sum with grouping
query = """
SELECT c.category, SUM(c.quantity) as total_quantity, AVG(c.price) as avg_price
FROM c
WHERE c.order_date >= @start_date
GROUP BY c.category
"""
results = container.query_items(
query=query,
parameters=[{"name": "@start_date", "value": "2025-01-01"}],
partition_key="tenant-123"
)
for result in results:
print(f"{result['category']}: {result['total_quantity']} items, ${result['avg_price']:.2f} avg")Aggregate Functions Reference
| Function | Description | Example |
|---|---|---|
COUNT(expr) | Count non-null values | COUNT(c.id) |
COUNT(1) | Count all documents | COUNT(1) |
SUM(expr) | Sum numeric values | SUM(c.quantity) |
AVG(expr) | Average of numeric values | AVG(c.price) |
MIN(expr) | Minimum value | MIN(c.created_at) |
MAX(expr) | Maximum value | MAX(c.price) |
Advanced Query Patterns
Array Operations
# Check if array contains value
query = "SELECT * FROM c WHERE ARRAY_CONTAINS(c.tags, 'urgent')"
# Check if array contains object with property
query = """
SELECT * FROM c
WHERE ARRAY_CONTAINS(c.items, {'product_id': @product_id}, true)
"""
# Flatten array with JOIN
query = """
SELECT c.id, c.order_id, item.product_name, item.quantity
FROM c
JOIN item IN c.items
WHERE item.quantity > @min_quantity
"""
# Array length
query = "SELECT * FROM c WHERE ARRAY_LENGTH(c.tags) > 3"String Functions
# Case-insensitive search (expensive - no index)
query = "SELECT * FROM c WHERE LOWER(c.name) = LOWER(@search_name)"
# Contains (prefix search uses index, contains doesn't)
query = "SELECT * FROM c WHERE STARTSWITH(c.name, @prefix)" # Uses index
query = "SELECT * FROM c WHERE CONTAINS(c.name, @substring)" # Full scan
# String manipulation
query = """
SELECT
c.id,
CONCAT(c.first_name, ' ', c.last_name) as full_name,
LENGTH(c.description) as desc_length
FROM c
"""Subqueries
# EXISTS subquery
query = """
SELECT * FROM c
WHERE EXISTS(
SELECT VALUE item FROM item IN c.items WHERE item.status = 'pending'
)
"""
# Scalar subquery
query = """
SELECT
c.id,
c.name,
(SELECT VALUE COUNT(1) FROM item IN c.items) as item_count
FROM c
"""Ordering and Distinct
# Order by (requires composite index for multiple fields)
query = "SELECT * FROM c ORDER BY c.created_at DESC, c.priority ASC"
# Distinct values
query = "SELECT DISTINCT VALUE c.category FROM c"
# Top N
query = "SELECT TOP 10 * FROM c ORDER BY c.score DESC"Transactions (Transactional Batch)
Execute multiple operations atomically within a single partition:
from azure.cosmos import TransactionalBatch
def transfer_funds(container, from_account_id: str, to_account_id: str, amount: float, partition_key: str):
"""Transfer funds between accounts atomically."""
# Read current balances
from_account = container.read_item(item=from_account_id, partition_key=partition_key)
to_account = container.read_item(item=to_account_id, partition_key=partition_key)
if from_account["balance"] < amount:
raise ValueError("Insufficient funds")
# Update balances
from_account["balance"] -= amount
to_account["balance"] += amount
# Execute as transaction
batch = TransactionalBatch(partition_key=partition_key)
batch.replace_item(item=from_account_id, body=from_account)
batch.replace_item(item=to_account_id, body=to_account)
results = container.execute_transactional_batch(batch=batch)
# Check results
for result in results:
if result["statusCode"] >= 400:
raise Exception(f"Transaction failed: {result}")
return resultsBatch Operations
from azure.cosmos import TransactionalBatch
def batch_create_items(container, items: list[dict], partition_key: str):
"""Create multiple items in a single batch (max 100 operations)."""
batch = TransactionalBatch(partition_key=partition_key)
for item in items[:100]: # Max 100 operations per batch
batch.create_item(body=item)
results = container.execute_transactional_batch(batch=batch)
successful = sum(1 for r in results if r["statusCode"] < 400)
print(f"Created {successful}/{len(items)} items")
return results
# Usage
items = [
{"id": f"item-{i}", "pk": "batch-test", "value": i}
for i in range(50)
]
batch_create_items(container, items, partition_key="batch-test")Change Feed
Process changes to documents in order:
def process_change_feed(container, partition_key: str | None = None):
"""Process change feed for a container."""
# Start from beginning
change_feed = container.query_items_change_feed(
partition_key=partition_key, # None for all partitions
start_time="Beginning"
)
for change in change_feed:
print(f"Changed document: {change['id']}")
# Process the change
process_document(change)
# Get continuation token for next run
continuation = change_feed.continuation_token
return continuation
def process_change_feed_incremental(container, continuation_token: str):
"""Resume change feed from continuation token."""
change_feed = container.query_items_change_feed(
continuation_token=continuation_token
)
for change in change_feed:
process_document(change)
return change_feed.continuation_tokenQuery Best Practices
| Practice | Recommendation |
|---|---|
| Always use partition key | Avoid cross-partition queries when possible |
| Use parameters | Enable query plan caching, prevent injection |
| Project only needed fields | Reduces RU cost and bandwidth |
| Use point reads | For single document by ID, use read_item not query |
| Index strategy | Create composite indexes for ORDER BY on multiple fields |
| Pagination | Use continuation tokens for large result sets |
| Aggregations | Prefer single-partition aggregations |
| Avoid OFFSET | Use continuation tokens instead for large datasets |
RU Estimation
def estimate_query_cost(container, query: str, partition_key: str, parameters: list | None = None):
"""Execute query and return RU cost."""
query_iterable = container.query_items(
query=query,
parameters=parameters or [],
partition_key=partition_key,
populate_query_metrics=True
)
items = list(query_iterable)
# Get request charge from response headers
request_charge = query_iterable.get_response_headers().get("x-ms-request-charge", 0)
print(f"Query returned {len(items)} items")
print(f"Total RU cost: {request_charge}")
print(f"RU per item: {float(request_charge) / len(items) if items else 0:.2f}")
return items, float(request_charge)Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs an API key, Needs Python
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/skills · .github/plugins/azure-sdk-python/skills/azure-cosmos-py · commit 0696600
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download azure-cosmos-py.zip