Skip to content

Repository files navigation

python-sanity

Python client for Sanity.io CMS HTTP API. Sanity is a hosted CMS solution for content management. This project is not affiliated with Sanity.io and is a third-party package.

ℹ️ Note: This package is an active fork of the original project at OmniPro-Group/sanity-python.

Install

Available on pypi as package python-sanity

Install with uv:

uv add python-sanity

Install with pip:

pip install python-sanity

Environment Variables

You can pass parameters to the client constructor directly, but it is recommended to use environment variables.

VariableDescriptionRequiredDefault Value
SANITY_PROJECT_IDThe project IDYes
SANITY_DATASETThe dataset to useNoproduction
SANITY_API_TOKENThe API tokenNo (required for mutations)
SANITY_LOG_LEVELLevel of loggingNoINFO

What's New in v0.2.0

  • AsyncClient: Full async/await support for all operations
  • Optional Logger: Logger parameter is now optional, uses built-in logger with SANITY_LOG_LEVEL support
  • httpx: Migrated from requests to httpx for better async support and HTTP/2
  • Automatic Retries: Configurable retry logic with exponential backoff
  • Better Error Handling: Specific exception types (SanityAuthError, SanityRateLimitError, etc.)
  • New API Parameters:
    • Query: perspective, result_source_map, tag, return_query
    • Mutation: auto_generate_array_keys, skip_cross_dataset_references_validation, transaction_id
  • Context Managers: Both Client and AsyncClient support context managers
  • Updated API Version: Default API version updated to 2025-02-19

Quick Start

Synchronous Client

fromsanityimportClient# Simple initialization (logger is now optional!)client=Client() # Uses environment variables# Or with explicit parametersclient=Client(
project_id="your-project-id",
dataset="production",
token="your-api-token", # Optional for read-only queriesuse_cdn=True
)
# Query with GROQresult=client.query(
groq="*[_type == 'post'] | order(publishedAt desc)[0...10]",
variables={"limit": 10}
)
# Mutationstransactions= [{
"createOrReplace": {
"_id": "post.123",
"_type": "post",
"title": "Hello World",
"publishedAt": "2025-01-15T00:00:00Z"
}
}]
result=client.mutate(
transactions=transactions,
return_documents=True
)
# Upload assetsresult=client.assets(
file_path="https://example.com/image.png"
)

Async Client

fromsanityimportAsyncClientimportasyncioasyncdefmain():
# Use async context managerasyncwithAsyncClient() asclient:
# Async queryresult=awaitclient.query(
groq="*[_type == 'post']",
perspective="published"
)
# Async mutationresult=awaitclient.mutate(
transactions=[{
"create": {
"_type": "post",
"title": "Async Post"
}
}]
)
# Async asset uploadresult=awaitclient.assets(
file_path="/path/to/image.png"
)
asyncio.run(main())

Advanced Configuration

fromsanityimportClient, TimeoutConfig, RetryConfig# Custom timeouts and retriesclient=Client(
timeout=TimeoutConfig(
connect=5.0,
read=30.0,
write=30.0,
pool=5.0
),
retry_config=RetryConfig(
max_retries=5,
backoff_factor=1.0
),
http2=True
)

Error Handling

fromsanityimport (
Client,
SanityAuthError,
SanityRateLimitError,
SanityValidationError
)
client=Client()
try:
result=client.query(groq="*[_type == 'post']")
exceptSanityAuthErrorase:
print(f"Authentication failed: {e.message}")
exceptSanityRateLimitErrorase:
print(f"Rate limited, retry after {e.retry_after}s")
exceptSanityValidationErrorase:
print(f"Validation error: {e.response_body}")

Migration Guide from v0.1.x

Breaking Changes

None! v0.2.0 is fully backward compatible.

Optional Improvements

  1. Logger is now optional:

    # Old way (still works)importloggingclient=Client(logger=logging.getLogger(__name__))
    # New way (simpler)client=Client() # Uses built-in logger with SANITY_LOG_LEVEL
  2. Use context managers for cleanup:

    # RecommendedwithClient() asclient:
    result=client.query(groq="*[_type == 'post']")
  3. Try async for better performance:

    fromsanityimportAsyncClientasyncwithAsyncClient() asclient:
    result=awaitclient.query(groq="*[_type == 'post']")
  4. Use new parameters:

    # Query with perspectiveresult=client.query(
    groq="*[_type == 'post']",
    perspective="published", # drafts, published, rawtag="my-app"
    )
    # Mutations with new optionsresult=client.mutate(
    transactions=[...],
    auto_generate_array_keys=True,
    transaction_id="my-custom-id"
    )

About

Python Client for Sanity CMS

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages