Skip to main content

Pipecat Quickstart

Building a voice or multimodal AI agent? Pipecat is built on top of daily-python and is the faster path — see the Pipecat Quickstart guide.

Installation

See the installation guide for setup instructions.

Core concepts

Initializing

daily.Daily.init() Before using the SDK, initialize the Daily context:
from daily import *
Daily.init()

Creating a call client

daily.CallClient() Most SDK functionality lives in the CallClient class. Create one after initializing:
client = CallClient()

Releasing a call client

daily.CallClient.release() Once the client is no longer needed, explicitly release its internal resources. This is important when the client holds a circular reference (e.g. via an event handler — see Handling events below).
client.release()

Joining a meeting

daily.CallClient.join() Join a Daily meeting with its URL:
client.join("https://my.daily.co/meeting")
To join a private room or join as the meeting owner, pass a meeting token:
client.join("https://my.daily.co/meeting", meeting_token="MY_TOKEN")

Leaving a meeting

daily.CallClient.leave() Always leave the meeting when done to clean up network connections:
client.leave()

Setting the user name

daily.CallClient.set_user_name()
client.set_user_name("Jane Doe")

Setting client permissions

Meeting owners can control whether participants have presence in a meeting, can send media, or can manage other participants. Permissions can be set dynamically from within a meeting or configured in advance with a meeting token. A common use case for daily-python is to export media from a call as a hidden participant. To do this, use the REST API to create a meeting token with the permissions object’s hasPresence set to false, then pass the resulting JWT to join():
client.join(
    "https://my.daily.co/meeting",
    meeting_token="MY_TOKEN")

Completion callbacks

Some CallClient methods are asynchronous. Pass an optional callback to be notified when they complete:
def on_joined(join_data, error):
    if not error:
        print("We just joined the meeting!")

client.join("https://my.daily.co/meeting", completion=on_joined)

Handling events

To listen and react to various meeting events, such as participant updates, recordings started or ended, etc., simply subclass daily.EventHandler and implement the relevant event handler methods. For example:
class MyApp(EventHandler):

    def on_participant_joined(self, participant):
        print("New participant joined!")
Then, register the event handler when creating the call client:
class MyApp(EventHandler):

    def __init__(self):
        self.client = CallClient(event_handler=self)
Note the circular reference between MyApp and the client — make sure to call client.release() during cleanup.

Inputs and publishing

Inputs control devices — whether a camera is enabled and at what resolution, or which microphone is selected. Publishing settings control whether the input is being sent, and at what quality. A camera can be enabled via inputs but not published (i.e. not sent to other participants). See daily.CallClient.inputs and daily.CallClient.publishing for details.

Subscriptions and subscription profiles

Subscriptions define which media you receive, from which participants, and the quality you receive it at — for example, you may subscribe to audio from all participants but only subscribe to video from specific ones or you may only subscribe to low-quality video from certain participants. Subscription profiles give a named set of subscription settings. The built-in base profile is applied to all remote participants’ camera and microphone streams by default.

Updating subscription profiles

Update the base profile to change default subscriptions for all participants — for example, to subscribe to microphone only:
client.update_subscription_profiles({
    "base": {
        "camera": "unsubscribed",
        "microphone": "subscribed"
    }
})
Define additional profiles to handle different quality tiers:
client.update_subscription_profiles({
    "lower": {
        "camera": {
            "subscriptionState": "subscribed",
            "settings": { "maxQuality": "low" }
        },
        "microphone": "unsubscribed"
    },
    "higher": {
        "camera": {
            "subscriptionState": "subscribed",
            "settings": { "maxQuality": "high" }
        },
        "microphone": "unsubscribed"
    }
})

Updating subscriptions for participants

Update a specific participants’ subscriptions to use a custom or "base" profile:
client.update_subscriptions({
    "eb762a39-1850-410e-9b31-92d7b21d515c": {
        "profile": "higher",
    },
    "2aa78bc2-1a0b-4f2b-8447-baa6a95bca1a": {
        "profile": "base"
    }
})
Or override specific settings for a participant:
client.update_subscriptions({
    "eb762a39-1850-410e-9b31-92d7b21d515c": {
        "profile": "base",
        "media": {
            "camera": "unsubscribed"
        }
    },
})
You can update subscriptions profiles and participant subscriptions in a single call:
client.update_subscriptions({
    "eb762a39-1850-410e-9b31-92d7b21d515c": {
        "profile": "base",
        "media": {
            "camera": "subscribed"
        }
    }
}, {
    "base": {
        "camera": "unsubscribed",
        "microphone": "subscribed"
    },
})

Sending media

daily-python supports virtual video and audio devices for simulating cameras, speakers, and microphones.

Sending video

Cameras are used to send video into the meeting. A camera is a live stream, so it needs to generate images at a certain framerate. To start, we need to create a virtual camera with a certain width, height, and an optional color format (frames written to the camera should then be in this color format):
camera = Daily.create_camera_device("my-camera",
    width = 1024,
    height = 1024,
    color_format = "RGB")
Once the camera is selected, we need to choose it as our default camera input. This is done through the call client input settings:
client.update_inputs({
    "camera": {
        "isEnabled": True,
        "settings": {
            "deviceId": "my-camera"
        }
    }
})
Finally, we can just write frames to the camera which are then sent as the call client video stream. In the following example, we load a PNG file (using the Pillow library) in RGB format and we send it 30 times per second.
from PIL import Image
im = Image.open("image.png")
while True:
    camera.write_frame(im.tobytes())
    time.sleep(0.033)
See daily.Daily.create_camera_device and daily.CallClient.update_inputs for more details.

Sending audio

Create a virtual microphone and write audio frames to it, similar to the virtual camera:
microphone = Daily.create_microphone_device("my-mic", sample_rate=16000, channels=1)

client.update_inputs({
    "camera": False,
    "microphone": {
        "isEnabled": True,
        "settings": { "deviceId": "my-mic" }
    }
})

# After joining, write audio frames
microphone.write_frames(frames)
See daily.Daily.create_microphone_device and daily.CallClient.update_inputs for more details.
If you are using multiple call clients, where each need their own microphone, create separate processes. Multiple microphones can exist simultaneously, but only one can be active per client.

Receiving media

Receiving video

daily.CallClient.set_video_renderer() Register a callback to receive video frames from a specific participant:
client.set_video_renderer(PARTICIPANT_ID, on_video_frame)

def on_video_frame(participant_id, video_frame):
    print(f"New frame from {participant_id}")
video_frame is a daily.VideoFrame.

Receiving audio

Audio can be received from an individual participant or from all meeting participants in a single mixed track. For individual participant audio, register a callback using daily.CallClient.set_audio_renderer():
client.set_audio_renderer(PARTICIPANT_ID, on_audio_data)

def on_audio_data(participant_id, audio_data):
    print(f"New audio data from {participant_id}")
audio_data is a daily.AudioData.

Receiving mixed audio

Create a virtual speaker to receive mixed audio from all meeting participants:
speaker = Daily.create_speaker_device("my-speaker", sample_rate=16000, channels=1)
Daily.select_speaker_device("my-speaker")

# After joining, read audio frames (e.g. every 10ms)
while True:
    buffer = speaker.read_frames(160)
    time.sleep(0.01)
Audio is 16-bit linear PCM. See daily.Daily.create_speaker_device and daily.Daily.select_speaker_device for more details.

Transcription

start_transcription()
stop_transcription()
Room owners or participants with the 'transcription' canAdmin permission can start and stop transcription:
client.start_transcription()
Optional configuration settings:
NameTypeDescription
languagestrSee Deepgram language docs
modelstrSee Deepgram model docs
profanity_filterboolSee Deepgram profanity filter docs
redactbool or listSee Deepgram redaction docs
extradictAdditional Deepgram streaming options
includeRawResponseboolWhether to include Deepgram’s raw response in transcription messages
client.start_transcription({
    'language': 'fr-CA',
    'model': 'meeting',
})
Listen for transcription events via on_transcription_message, on_transcription_started, and on_transcription_stopped. Stop transcription with:
client.stop_transcription()

Recording

daily.CallClient.start_recording()
daily.CallClient.stop_recording()
With recording enabled on the room, you can start/stop a cloud recording from your Python client:
client.start_recording()
client.stop_recording()
Multiple recording sessions can run simultaneously by specifying a unique stream_id UUID. For layout options, see the recording layout reference. See also Daily’s recording guide for more details on recording features and capabilities.

Chatting with Prebuilt Clients

send_prebuilt_chat_message() When participants are in a Daily Prebuilt call, your Python client can send messages to Prebuilt’s chat like so:
client.send_prebuilt_chat_message("Hello, world", "Daily Python Bot")

Reference

Full API reference, types, and method signatures: reference-python.daily.co