Authentication
How to authenticate requests from your server.
Every request from the SDK is authenticated with a single secret API key
sent on the x-easy-api-key header. The SDK never reads keys from the
filesystem or environment on its own — you pass the key explicitly to
Client(api_key=...) so you can keep secret management in one place.
Two key prefixes route automatically:
| Prefix | Environment | Base URL |
|---|---|---|
sk_test_ | Sandbox | https://sandbox-api.itseasy.co/v1/api |
sk_live_ | Production | https://api.itseasy.co/v1/api |
Use sandbox keys for development and CI. Production keys see real money.
Initializing with an API key
import os
from easylabs import Client
# Sandbox — sk_test_... routes to sandbox automatically.
client = Client(api_key=os.environ["EASY_API_KEY"])
# Production — sk_live_... routes to production automatically.
prod = Client(api_key=os.environ["EASY_LIVE_KEY"])
# Self-hosted / pinned-CI: bypass routing entirely.
local = Client(
api_key="sk_test_...",
internal_api_url="http://localhost:4000/v1/api",
)The constructor calls GET /validate-key synchronously. If the key is
malformed, revoked, or doesn't match the target environment, it raises
easylabs.AuthenticationError before returning the client. That gives
you a guaranteed-good client object on success — no surprise 401s on
the first real request.
Rotating keys
Keys can be rotated without downtime by running two clients side-by-side during the cutover:
old = Client(api_key=os.environ["EASY_API_KEY"])
new = Client(api_key=os.environ["EASY_API_KEY_NEXT"])
# Route new traffic through `new`; let in-flight requests on `old` finish.
# Once the dashboard confirms zero requests on the old key, revoke it.There's no per-key state on the client, so swapping at runtime (e.g. re-binding a module-level singleton) is also safe.
Multi-tenant authentication
The SDK is designed for one key per Client instance. For multi-tenant
servers, build a small factory keyed by tenant:
from functools import lru_cache
from easylabs import Client
@lru_cache(maxsize=512)
def client_for(tenant_id: str) -> Client:
api_key = lookup_api_key(tenant_id) # your own resolver
return Client(api_key=api_key)Client is cheap to keep around (one httpx.Client + a few resource
namespaces), but __init__ does perform a /validate-key round-trip,
so caching avoids the network hop on every request.