For the complete documentation index, see llms.txt. This page is also available as Markdown.

Python SDK

Drive Ojin's face models — Oris Presence and Oris Portrait — straight from Python. Feed in your TTS audio; get back a synchronized, lip-synced talking-avatar stream.

The ojin-client SDK is the fastest way to add a real-time talking avatar to a Python app or voice agent. You bring the words (any TTS); the SDK streams back a face that speaks them — perfectly in sync — handling buffering, the playback clock, resampling, and barge-in for you.

Just want a complete agent? If you'd rather not assemble a pipeline, the Human Agent gives you a full speech-in, speech-out avatar with a single line of embed code. Already on Pipecat? Jump to the Pipecat integration.

Requirements

  • Python 3.10+

  • An Ojin API keyget yours here

  • A face-model config ID — a Presence or Portrait configuration from your dashboard

New accounts start with $10 in free credits.

Install

With uv (recommended):

uv add "ojin-client[stv]"

Or with pip:

pip install "ojin-client[stv]"

The [stv] extra pulls in the speech-to-video helpers (numpy, opencv-python-headless, soxr) that handle resampling, decoding, and the sync math for you.

Authenticate

The client needs your API key and a configuration ID. Keep both out of source — read them from the environment:

Or let the SDK load and validate them for you — resolve_credentials() reads an optional .env file and raises a clear error if either value is missing:

By default the client connects to wss://models.ojin.ai/realtime.

Pick your model

The SDK is identical for both face models — the config_id you pass selects which one runs.

A config_id from…

You get

Choose it when

an Oris Presence config

the flagship, fully expressive generative presence — rich expressions and natural movement

quality and expressiveness matter most

an Oris Portrait config

a cost-effective, real-time lip-synced talking face

you want a great talking face at the best price

Create configurations in the dashboard. The code below is the same either way — only the config_id changes.

Two clients, one package

OjinSTVClient (high-level)

OjinClient (low-level)

Import

from ojin.stv import OjinSTVClient

from ojin.ojin_client import OjinClient

You give it

TTS audio at any sample rate

16 kHz mono int16 PCM

You get

synced STVAudioFrame + STVVideoFrame on a 25 fps clock

raw protocol messages (JPEG + PCM)

It handles

buffering, playback clock, resampling, barge-in re-sync

just the WebSocket + wire protocol

Most applications want OjinSTVClient. Reach for OjinClient only when you're writing your own playback/sync layer.

Quickstart

Feed the avatar one utterance of TTS audio and consume the synchronized stream it speaks back.

say() is a one-shot helper for start_turn() + send_tts_audio(). For a live agent, call start_turn() once per utterance and stream chunks with send_tts_audio() as your TTS produces them — the user hears the exact audio you sent, lip-synced to the avatar.

Events you'll use

Register handlers with @client.on(STVEvent.X) or client.add_listener(STVEvent.X, cb). Handlers may be sync or async; a failing handler never breaks the playback loop.

Event
Fires when

SESSION_READY

the session is live and ready for audio

BOT_STARTED_SPEAKING

the first speech frame of a turn is played

BOT_STOPPED_SPEAKING

a turn has finished playing

INTERRUPTED

a barge-in was accepted

ERROR

a transport or server error occurred

CLOSED

the session has been torn down

Where to go next

Runnable examples

Realtime streaming and render-to-MP4, ready to clone

View examples →

Build a voice agent

Drop the avatar into a Pipecat pipeline

Pipecat integration →

Last updated

Was this helpful?