Skip to main content

MeetStream SDK for Python

Send AI bots into Zoom, Google Meet and Microsoft Teams. Record, transcribe, summarize and stream meetings, and run MIA voice agents that talk back.

PyPI Python Docs Typed License

pip install meetstream-sdk

Python 3.8+. Sync and async. Ships type information.

Quickstart

from meetstream import MeetStream

meetstream = MeetStream()  # reads MEETSTREAM_API_KEY

bot = meetstream.bots.create({
    "meeting_link": "https://meet.google.com/abc-defg-hij",
    "bot_name": "Notetaker",
    "recording_config": {
        "transcript": {"provider": {"deepgram": {"model": "nova-3", "language": "en"}}}
    },
})

# Bounded wait - never spins forever, even on a streaming-only bot.
transcript = meetstream.transcripts.wait_for(bot["transcript_id"])

Get a key at app.meetstream.ai/api-key.

Async

from meetstream import AsyncMeetStream

async with AsyncMeetStream() as meetstream:
    bot = await meetstream.bots.create({"meeting_link": "https://zoom.us/j/123"})
    status = await meetstream.bots.status(bot["bot_id"])

Why this SDK

The MeetStream API has a handful of behaviours that look like bugs until you know them. This SDK encodes all of them so you do not rediscover them in production.

The trap What the SDK does
202 sits inside the 2xx range and means "not ready yet", not success Raises NotReadyError instead of handing you an empty body
507 looks like a failure but is an idempotent replay Returns the original resource as success
Streaming-only providers return 202 forever - no post-call transcript ever exists wait_for() is always bounded and tells you exactly why it gave up
Transcripts are keyed by transcript_id, not bot_id transcripts.get() takes the right id
Segments use transcript, not text Documented at every call site
remove_bot is a GET bots.remove() handles it
REST uses Token, the MCP server uses Bearer Correct scheme sent automatically
MIA takes only agent_config_id Extra bridge fields are the usual cause of a silent agent

What you can do

Bots - lifecycle, media, live interaction
meetstream.bots.create(params, idempotency_key=uuid)
meetstream.bots.list()
meetstream.bots.status(bot_id)
meetstream.bots.detail(bot_id)        # includes transcript_id
meetstream.bots.summary(bot_id)       # AI summary
meetstream.bots.remove(bot_id)        # leave, keep the data
meetstream.bots.delete_data(bot_id)   # irreversible

meetstream.bots.audio(bot_id)
meetstream.bots.video(bot_id)
meetstream.bots.audio_streams(bot_id)       # per-participant audio
meetstream.bots.recording_streams(bot_id)   # per-participant video
meetstream.bots.screenshots(bot_id)
meetstream.bots.wait_for_audio(bot_id)      # bounded

meetstream.bots.participants(bot_id)
meetstream.bots.chats(bot_id)
meetstream.bots.speaker_timeline(bot_id)

meetstream.bots.send_message(bot_id, "Recording has started.")
meetstream.bots.send_image(bot_id, "https://…/slide.png", display_duration=5)
meetstream.bots.pause_recording(bot_id)     # privacy window
meetstream.bots.resume_recording(bot_id)
Transcripts
meetstream.transcripts.get(transcript_id)
meetstream.transcripts.wait_for(transcript_id, timeout=900)
meetstream.transcripts.list_for_bot(bot_id)
meetstream.transcripts.transcribe_bot_audio(bot_id)   # rescue a streaming-only bot
Calendar - auto-join and scheduling
meetstream.calendar.connect_google({
    "google_client_id": ..., "google_client_secret": ..., "google_refresh_token": ...,
})
meetstream.calendar.connect_outlook({...})
meetstream.calendar.events()
meetstream.calendar.schedule_event(event_id)
meetstream.calendar.list_scheduled_bots()
meetstream.calendar.reschedule_bot(bot_id, "2026-09-01T10:00:00Z")
meetstream.calendar.enable_auto_schedule()
MIA voice agents
agent = meetstream.mia.create({
    "agent_name": "Meeting Assistant",
    "mode": "pipeline",                    # or "realtime" for speech-to-speech
    "model": {"provider": "openai", "model": "gpt-4.1",
              "first_message": "Hi, I'm an AI assistant on this call."},
    "voice": {"provider": "openai", "voice_id": "nova"},
    "transcriber": {"provider": "deepgram", "model": "nova-3",
                    "boostwords": ["Acme", "MeetStream"]},
    "agent": {"tools": [], "mcp_servers": [], "enable_interruptions": True},
    "wake_word": {"enabled": True, "words": ["hey acme"], "timeout": 30},
})

# Attach it with ONE field. Adding socket_connection_url or
# live_audio_required alongside is the usual cause of a silent agent.
meetstream.bots.create({
    "meeting_link": meeting_link,
    "agent_config_id": agent["agent_config_id"],
})

boostwords fixes most "it mishears our company name" complaints, and mcp_servers is what turns a talking bot into one that can do work mid-call.

Integrations - Google signed-in bots, Zoom OAuth, your own S3
meetstream.google_logins.create_domain({...})
meetstream.google_logins.create({...})

meetstream.zoom.authorize_url()
meetstream.zoom.list_connections()

meetstream.storage.set({"provider": "aws", "bucket_name": ..., "region": ...})

Webhooks

Verify before you trust. Pass the raw body - re-serializing a parsed dict changes key order and the signature will never match.

from flask import Flask, request
from meetstream import parse_webhook, is_terminal, describe_stop

app = Flask(__name__)

@app.post("/webhook")
def webhook():
    try:
        event = parse_webhook(
            request.get_data(),                      # raw bytes, not request.json
            request.headers.get("X-MeetStream-Signature", ""),
            os.environ["WEBHOOK_SECRET"],
        )
    except ValueError:
        return "", 401

    if is_terminal(event):
        print(describe_stop(event))
    return "", 200   # ack fast, process async

bot.stopped is the single terminal event and always carries status_code: 200 - the reason lives in bot_status (Stopped, NotAllowed, Denied, Error). bot.error is not terminal; the bot keeps running. Streaming-only providers stop at audio.processed and never emit bot.done.

Errors

from meetstream import NotReadyError, RateLimitError, BadRequestError, MeetStreamError

try:
    meetstream.transcripts.get(transcript_id)
except NotReadyError:
    ...                                  # 202 - poll again
except RateLimitError as e:
    time.sleep(e.retry_after or 5)
except BadRequestError as e:
    print(e.message)                     # the API's own message
except MeetStreamError as e:
    print(e.status, e.request_id)

AuthenticationError (401) means the header never arrived. PermissionError (403) means the key itself was rejected. That distinction saves a lot of debugging.

Configuration

meetstream = MeetStream(
    api_key=os.environ["MEETSTREAM_API_KEY"],
    base_url="https://api.meetstream.ai/api/v1",
    timeout=60.0,
    max_retries=2,          # 429 and 5xx, exponential backoff, honours Retry-After
    default_headers={},
    http_client=my_httpx_client,
)
Variable Purpose
MEETSTREAM_API_KEY Your key. Required unless passed explicitly
MEETSTREAM_API_URL Override the API base

Anything not yet wrapped is reachable through the raw transport:

meetstream.http.get("/some/new/endpoint")

Typed

The package ships py.typed, so mypy and pyright pick up its annotations with no stub package.

Also available

What Where
🔌 MCP server - 19 tools for any MCP client @meetstream/mcp
📦 TypeScript SDK @meetstream/sdk
⌨️ CLI @meetstream/cli
🧪 Labs - runnable templates labs

Links

Documentation · API reference · Errors · Webhooks · MIA · support@meetstream.ai

MIT licensed.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

meetstream_sdk-1.0.0.tar.gz (20.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

meetstream_sdk-1.0.0-py3-none-any.whl (19.9 kB view details)

Uploaded Python 3

File details

Details for the file meetstream_sdk-1.0.0.tar.gz.

File metadata

  • Download URL: meetstream_sdk-1.0.0.tar.gz
  • Upload date:
  • Size: 20.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.14

File hashes

Hashes for meetstream_sdk-1.0.0.tar.gz
Algorithm Hash digest
SHA256 6ddb70f2a17162ddb30499b1ef1ac5ab195da08213101dedc778ad8f18a8bfa6
MD5 2abb43939f28964d21b44632141a6cf8
BLAKE2b-256 193e08083d00bbd1c60af3a2603ab19bf93738bfac4ca60a08f75ec1f5572bab

See more details on using hashes here.

File details

Details for the file meetstream_sdk-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: meetstream_sdk-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 19.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.11.14

File hashes

Hashes for meetstream_sdk-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3dd9bd0912ae104b9df91c62dcf144d6274751eed3b4058324fcb73e17dea122
MD5 add3dd36d2e5017720f5a5ef01d49832
BLAKE2b-256 11ea1412f17692f67e1e1888a1e920f1029d01423f46f6126e419b5ea695c2cb

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page