Valentina Noir Python Client
The valentina-python-client package is an async and sync Python client for the Valentina Noir REST API. It wraps the API in type-safe Pydantic models, handles pagination and retries automatically, and lets you focus on building your application instead of managing HTTP requests.
Note
This client library is a convenience for Python developers. It isn't required to use the Valentina Noir API — you can integrate with any HTTP client in any language by following the full API documentation.
Why Use This Client?
You can call the Valentina Noir API directly with any HTTP library, but this client removes the boilerplate:
- Type safety — Every request and response is validated through Pydantic models, catching errors early and giving you full IDE autocompletion.
- Automatic pagination — Stream through large result sets with
iter_all()or fetch everything at once withlist_all(), without writing pagination logic yourself. - Built-in retries — Rate limits (429), server errors (5xx), and network failures are retried automatically with exponential backoff.
- Async and sync — Built on httpx2 with both async (
VClient) and sync (SyncVClient) clients, so it fits naturally into any Python application — from async frameworks like FastAPI to traditional sync code in Flask, Django, or scripts. - Idempotency support — Enable automatic idempotency keys so retried writes don't create duplicate resources.
- Structured logging — Optional Loguru-based logging with a stdlib bridge for debugging and observability.
Requirements
- Python 3.13+
- A Valentina API key
Installation
# Using uv
uv add valentina-python-client
# Using pip
pip install valentina-python-client
Quick Start
Create a client once at application startup, then access API services from anywhere in your code.
Async
import asyncio
from vclient import VClient, companies_service, campaigns_service
async def main():
async with VClient(
base_url="https://api.valentina-noir.com",
api_key="YOUR_API_KEY",
) as client:
# List all companies you have access to
companies = companies_service()
for company in await companies.list_all():
print(f"Company: {company.name}")
# Fetch campaigns on behalf of a user
campaigns = campaigns_service(
on_behalf_of="USER_ID",
company_id="COMPANY_ID",
)
for campaign in await campaigns.list_all():
print(f"Campaign: {campaign.name}")
asyncio.run(main())
Sync
from vclient import SyncVClient, sync_companies_service, sync_campaigns_service
with SyncVClient(
base_url="https://api.valentina-noir.com",
api_key="YOUR_API_KEY",
) as client:
# List all companies you have access to
companies = sync_companies_service()
for company in companies.list_all():
print(f"Company: {company.name}")
# Fetch campaigns on behalf of a user
campaigns = sync_campaigns_service(
on_behalf_of="USER_ID",
company_id="COMPANY_ID",
)
for campaign in campaigns.list_all():
print(f"Campaign: {campaign.name}")
You can also set VALENTINA_CLIENT_BASE_URL and VALENTINA_CLIENT_API_KEY as environment variables and create either client with no arguments.
Embedding Child Resources
When fetching a single character, you can embed related resources directly in the response using the include parameter. This avoids extra API calls when you need character data along with their traits, inventory, notes, or assets.
Async
async with VClient(base_url="...", api_key="...") as client:
svc = client.characters(on_behalf_of="USER_ID")
# Fetch character with embedded traits and inventory
detail = await svc.get("CHARACTER_ID", include=["traits", "inventory"])
# Embedded resources are available directly
if detail.traits is not None:
for trait in detail.traits:
print(f"{trait.trait.name}: {trait.value}")
if detail.inventory is not None:
for item in detail.inventory:
print(f"{item.name} ({item.type})")
# Without include, embedded fields are None
basic = await svc.get("CHARACTER_ID")
assert basic.traits is None # not requested
Sync
with SyncVClient(base_url="...", api_key="...") as client:
svc = client.characters(on_behalf_of="USER_ID")
detail = svc.get("CHARACTER_ID", include=["traits", "notes"])
Valid include values: "traits", "inventory", "notes", "assets". The CharacterDetail return type is a subclass of Character, so existing code that expects Character continues to work.
Learn More
Detailed guides are available for each aspect of the client:
| Topic | Description |
|---|---|
| Configuration | Timeouts, retries, environment variables, idempotency, and logging |
| Sync Client | Using the synchronous client for non-async applications |
| Services | Available services, method signatures, scoping, and pagination patterns |
| Response Models | Pydantic model specifications for all API resources |
| Error Handling | Exception hierarchy, HTTP status mapping, and usage examples |
| Testing | Fake client for testing downstream applications against the vclient contract |