5.7 KiB
Usage guide
Installation
pip install conduit-client
For development:
pip install conduit-client[dev]
Authentication
The Conduit gateway uses API keys in the Authorization: Bearer mega_sk_live_<prefix>_<secret> header. Pass your key to ConduitClient or AsyncConduitClient:
from conduit_client import ConduitClient
client = ConduitClient("mega_sk_live_abc123_xyz")
Async version:
import asyncio
from conduit_client import AsyncConduitClient
async def main() -> None:
async with AsyncConduitClient("mega_sk_live_abc123_xyz") as client:
me = await client.gateway.me()
print(me.scopes)
asyncio.run(main())
You can create new keys programmatically using the gateway client:
from conduit_client.models.common import CreateKeyRequest
new_key = client.gateway.create_key(
CreateKeyRequest(display_name="production", scopes=["sms:send", "sms:read"])
)
print(new_key.key) # returned exactly once by the server
Async version:
from conduit_client import AsyncConduitClient
from conduit_client.models.common import CreateKeyRequest
async def main() -> None:
async with AsyncConduitClient("mega_sk_live_...") as client:
new_key = await client.gateway.create_key(
CreateKeyRequest(display_name="production", scopes=["sms:send", "sms:read"])
)
print(new_key.key)
asyncio.run(main())
Sync vs async
Both clients expose the same façade subclients:
# Sync
with ConduitClient("mega_sk_live_...") as client:
me = client.gateway.me()
# Async
async with AsyncConduitClient("mega_sk_live_...") as client:
me = await client.gateway.me()
The asynchronous client uses httpx.AsyncClient under the hood and must be awaited.
Timeouts and retries
client = ConduitClient(
"mega_sk_live_...",
timeout=60.0,
max_retries=2,
)
Async version:
from conduit_client import AsyncConduitClient
client = AsyncConduitClient(
"mega_sk_live_...",
timeout=60.0,
max_retries=2,
)
Retries are applied only to idempotent methods (GET, HEAD, OPTIONS, PUT, DELETE) and only for status codes 429, 502, 503, and 504. Backoff is exponential (0.5s, 1s, 2s, ...).
Custom httpx options
Any extra keyword arguments are forwarded to the underlying httpx.Client or httpx.AsyncClient:
client = ConduitClient(
"mega_sk_live_...",
proxies="http://proxy.example.com:8080",
)
Async version:
from conduit_client import AsyncConduitClient
client = AsyncConduitClient(
"mega_sk_live_...",
proxies="http://proxy.example.com:8080",
)
Handling free-form API bodies
Some Conduit endpoints forward extra fields directly to the upstream provider (for example, Proxmox VM/container creation, Transmission session patches, DNS zone modifications). For these, the client accepts a dict[str, Any] or a Pydantic model that allows extra fields:
client.compute.patch_vm(100, {"memory": 4096, "cores": 4})
client.torrents.patch_session({"download-dir": "/mnt/media"})
client.dns.patch_zone("example.com", {"type": "slave"})
Async version:
from conduit_client import AsyncConduitClient
async def main() -> None:
async with AsyncConduitClient("mega_sk_live_...") as client:
await client.compute.patch_vm(100, {"memory": 4096, "cores": 4})
await client.torrents.patch_session({"download-dir": "/mnt/media"})
await client.dns.patch_zone("example.com", {"type": "slave"})
asyncio.run(main())
Errors
All client errors inherit from ConduitError. HTTP error responses are raised as typed exceptions:
| Exception | Trigger |
|---|---|
AuthenticationError |
401 / 403 |
NotFoundError |
404 |
ValidationError |
422 |
ConflictError |
409 |
RateLimitError |
429 |
ConduitAPIError |
any other 4xx / 5xx |
Each exception exposes status_code and response_body:
from conduit_client import NotFoundError
try:
client.wiki.get_raw("missing:page")
except NotFoundError as exc:
print(exc.status_code)
print(exc.response_body)
Async version:
from conduit_client import AsyncConduitClient, NotFoundError
async def main() -> None:
async with AsyncConduitClient("mega_sk_live_...") as client:
try:
await client.wiki.get_raw("missing:page")
except NotFoundError as exc:
print(exc.status_code)
print(exc.response_body)
asyncio.run(main())
Façade clients
| Attribute | Scope | Description |
|---|---|---|
client.gateway |
gateway:read, keys:create, etc. |
Health, keys, audit log |
client.sms |
sms:read, sms:send |
SMS/MMS send/list/delete |
client.media_ingest |
video:read, video:admin |
MeTube jobs/subscriptions |
client.media_library |
video:read, video:admin |
Jellyfin search/sessions |
client.torrents |
video:read, video:admin |
Transmission management |
client.wiki |
wiki:read, wiki:write |
DokuWiki read/write |
client.compute |
vm:read, vm:start, vm:stop |
Proxmox VMs/containers/storage |
client.dns |
dns:read, dns:write, dns:delete |
BIND9 servers/zones/records |
Path parameters
Path parameters such as DokuWiki page IDs and DNS zone names are URL-encoded automatically. Slashes, colons, and other special characters are handled safely.
client.wiki.get_raw("namespace:sub:page")
client.dns.get_zone("example.com")
Async version:
from conduit_client import AsyncConduitClient
async def main() -> None:
async with AsyncConduitClient("mega_sk_live_...") as client:
await client.wiki.get_raw("namespace:sub:page")
await client.dns.get_zone("example.com")
asyncio.run(main())