Authentication
Get an API secret, set it for the right surface, and understand how it is exchanged for short-lived runtime tokens.
One credential, two names
bitHuman uses a single shared credential per account that authenticates every SDK and the REST API. There are two equivalent environment-variable names depending on which surface you’re using:
BITHUMAN_API_SECRET— Python SDK, REST API, LiveKit plugin, Kotlin SDK, and CLI.BITHUMAN_API_KEY— Swift SDK on Apple platforms. Same value, different name to match Apple convention.
Tip You only need a key when an avatar is rendering. Audio-only voice agents (Swift
VoiceChatwith noconfig.avatar) run fully offline without one — see pricing for what’s free vs. metered.
Get a key
- Sign in at bithuman.ai (free tier, no credit card).
- Go to Developer → API Keys.
- Click Create new key, name it (e.g.
production-mac), and copy the value. You won’t be able to view it again — store it somewhere durable.
Verify it works
curl -X POST https://api.bithuman.ai/v1/validate \
-H "api-secret: YOUR_KEY"
A 200 with {"valid": true} means you’re good. If you have the CLI installed,
bithuman doctor also checks the credential, brain selection, caches, and host
capabilities.
Set it for each surface
REST API — api-secret header on every request:
curl -X POST https://api.bithuman.ai/v1/agent/A78WKV4515/speak \
-H "api-secret: $BITHUMAN_API_SECRET" \
-H "content-type: application/json" \
-d '{"message": "Hello"}'
Python SDK — env var, or pass directly:
runtime = await AsyncBithuman.create(
model_path="avatar.imx",
api_secret="your_key",
)
Swift SDK — env var or config; never hardcode in source:
// development: env var
config.apiKey = ProcessInfo.processInfo.environment["BITHUMAN_API_KEY"]
// production: fetch from your backend via Keychain
config.apiKey = await fetchFromBackend()
For DMG distribution, bake the key into Info.plist via a build script. For App Store, fetch from your own backend via Keychain on first launch — don’t bundle.
api-secret vs. runtime tokens
The long-lived api-secret is never sent to the avatar engine or any third
party. It only ever travels to api.bithuman.ai over TLS. The streaming runtime
is authorized by a separate, short-lived runtime token:
- Your code provides the API secret to the SDK or REST request.
- The SDK exchanges it for a short-lived runtime token at
POST /v1/runtime-tokens/request. - That token authorizes the avatar engine (heartbeat + frame production) for a single session.
- Tokens auto-renew roughly every 60 seconds via the heartbeat.
- Bad keys fail at step 2 — fast — before any user-visible work.
The runtime token is not an api-secret. It can’t mint other tokens; it just
authorizes the runtime to compute frames for one specific session. It is HS256-signed
with a short TTL and carries iss=bitHuman, sub=<user_id>, and aud=<fingerprint>
claims. The SDKs and LiveKit plugin handle this loop for you — you rarely call
/v1/runtime-tokens/request directly. For browser embeds, use the more constrained
embed token flow instead.
Audio-only Swift mode is unmetered
If you only want on-device voice chat (no lip-synced avatar), skip the API key entirely:
var config = VoiceChatConfig()
config.systemPrompt = "You are a helpful assistant."
config.voice = .preset("Aiden")
// no config.avatar = ...
let chat = VoiceChat(config: config)
try await chat.start() // does not authenticate
This mode runs fully offline (after first-launch weight downloads), bills nothing, and doesn’t require a key.
Rotating keys
Rotate from the Developer dashboard. Rotation invalidates the old key immediately — there’s no overlap window. Live sessions using the old key fail their next heartbeat (within ~60 s) and pause; restart with the new key to resume. Rotate during a maintenance window if you have production sessions running.
Common errors
| Error | Cause | Fix |
|---|---|---|
401 from REST API | Missing or invalid api-secret header | Add the header on every request; re-verify with /v1/validate. |
Authentication failed (Python) | Wrong/missing BITHUMAN_API_SECRET | Verify with the curl /v1/validate recipe. |
VoiceChatError.missingAPIKey (Swift) | Avatar mode without apiKey set | Set config.apiKey or export BITHUMAN_API_KEY. |
| Heartbeat silent after 5 min | Network dropped on-device | Reconnect; the SDK pauses the avatar after the grace window and resumes when heartbeats succeed. |
See the full error reference.