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.

Metadata

Release files for meetstream-sdk 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for meetstream-sdk 1.0.0
File Size Uploaded Provenance Actions
meetstream_sdk-1.0.0.tar.gz 20.3 kB Without provenance Details for meetstream_sdk-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for meetstream-sdk 1.0.0
File Interpreter ABI Platform Actions
meetstream_sdk-1.0.0-py3-none-any.whl Python 3 none any Details for meetstream_sdk-1.0.0-py3-none-any.whl

Total release size: 40.2 kB

Release files / meetstream_sdk-1.0.0.tar.gz

Download URL meetstream_sdk-1.0.0.tar.gz
Size 20.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6ddb70f2a17162ddb30499b1ef1ac5ab195da08213101dedc778ad8f18a8bfa6
BLAKE2b-256 checksum
How to use checksums
193e08083d00bbd1c60af3a2603ab19bf93738bfac4ca60a08f75ec1f5572bab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.14

Release files / meetstream_sdk-1.0.0-py3-none-any.whl

Download URL meetstream_sdk-1.0.0-py3-none-any.whl
Size 19.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3dd9bd0912ae104b9df91c62dcf144d6274751eed3b4058324fcb73e17dea122
BLAKE2b-256 checksum
How to use checksums
11ea1412f17692f67e1e1888a1e920f1029d01423f46f6126e419b5ea695c2cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.14

Security for meetstream-sdk 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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