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 httpx 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 for a specific user
campaigns = campaigns_service(
user_id="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 for a specific user
campaigns = sync_campaigns_service(
user_id="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.
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 |