Daily Client SDK for Python
Daily's Python SDK provides everything needed to build real-time video, audio, and AI-powered voice agents with WebRTC infrastructure. It allows headless bots to connect to Daily rooms and process media and event data streams, supporting various use cases from virtual devices to transcription. The current version is 0.27.0, and the library maintains an active release cadence with frequent updates and new features.
Common errors
-
ImportError: cannot import name 'CallClient' from 'daily'
cause Attempting to import `CallClient` (or other core classes) from a wrong path, or the `daily-python` package is not correctly installed or in the Python path.fixEnsure `daily-python` is installed (`pip install daily-python`) and import statements are `from daily import CallClient` (or other specific symbols). Verify Python environment activation. -
Failed to join meeting: Daily error: <specific error message>
cause Common causes include an invalid `meeting_url` or `meeting_token`, network connectivity issues, or domain configuration problems (e.g., meeting not enabled, permissions).fixDouble-check `DAILY_ROOM_URL` and `DAILY_TOKEN`. Ensure your Daily domain settings allow the client to join. Check network connectivity. Review the `error` message in the `on_joined_meeting` callback for specific diagnostics. -
TypeError: 'NoneType' object is not subscriptable
cause This typically occurs in a callback when you attempt to access a dictionary-like property (e.g., `participant['info']`) on an object that is `None`. This usually means an error occurred in the preceding asynchronous operation, and the data object was `None` instead of the expected payload.fixBefore accessing fields on callback parameters, add a check to ensure the object is not `None` and that no error was reported. For example, `if data and not error:`.
Warnings
- breaking The callback signatures for `CallClient.start_dialout()` and `CallClient.start_recording()` changed in `v0.21.0`. They now return `(session_id, error)` and `(stream_id, error)` respectively, instead of just `error`.
- gotcha The `CustomAudioSource` constructor's `auto_silence` parameter now defaults to `True` in `v0.27.0`. This means silence is automatically sent when no audio frames are being written. If you need precise control over when audio is sent (e.g., to send no audio when idle), explicitly set `auto_silence=False`.
- gotcha Asynchronous operations like `client.join()`, `start_recording()`, and `start_dialout()` use completion callbacks. Forgetting to handle the `error` parameter or assuming success can lead to silent failures or `AttributeError` if attempting to process `None` data.
Install
-
pip install daily-python
Imports
- CallClient
from daily import CallClient
- Daily
from daily import Daily
- EventHandler
from daily import EventHandler
- LogLevel
from daily import LogLevel
- CustomAudioSource
from daily import CustomAudioSource
- CustomVideoTrack
from daily import CustomVideoTrack
Quickstart
import os
import time
import threading
from daily import Daily, CallClient, EventHandler, LogLevel
# Replace with your Daily room URL and token (optional)
DAILY_ROOM_URL = os.environ.get('DAILY_ROOM_URL', 'https://your-domain.daily.co/your-room')
DAILY_TOKEN = os.environ.get('DAILY_TOKEN', None)
class MyEventHandler(EventHandler):
def on_participant_joined(self, participant):
print(f"Participant joined: {participant['info']['userName']} (ID: {participant['id']})")
def on_participant_left(self, participant, reason):
print(f"Participant left: {participant['info']['userName']} (ID: {participant['id']}) - Reason: {reason}")
def on_joined_meeting(self, data, error):
if error:
print(f"Failed to join meeting: {error}")
self.join_event.set() # Signal completion even on error
else:
print("Successfully joined the meeting!")
self.join_event.set()
def on_error(self, error):
print(f"An error occurred in the Daily SDK: {error}")
def set_join_event(self, event):
self.join_event = event
def main():
# Initialize the Daily SDK with optional logging
Daily.init(log_level=LogLevel.INFO)
print("Daily SDK initialized.")
event_handler = MyEventHandler()
client = CallClient(event_handler=event_handler)
join_completion_event = threading.Event()
event_handler.set_join_event(join_completion_event)
print(f"Attempting to join room: {DAILY_ROOM_URL}")
client.join(
meeting_url=DAILY_ROOM_URL,
meeting_token=DAILY_TOKEN,
completion=event_handler.on_joined_meeting
)
# Wait for the join operation to complete
join_completion_event.wait(timeout=30) # Wait up to 30 seconds
if join_completion_event.is_set() and client.is_joined():
print("Client is active in the meeting. Waiting for 10 seconds...")
time.sleep(10) # Stay in the meeting for a short period
print("Leaving meeting...")
leave_completion_event = threading.Event()
client.leave(completion=lambda data, error: leave_completion_event.set())
leave_completion_event.wait(timeout=10)
print("Left meeting.")
else:
print("Failed to join meeting or timed out.")
client.release()
Daily.deinit()
print("Daily SDK deinitialized.")
if __name__ == "__main__":
main()