meetbot / docs
SDKs

Python SDK

Official meetbot-sdk on PyPI. Sync + async clients, pydantic v2 models, webhook signature verification. Python 3.10+.

The official Python SDK ships as meetbot-sdk on PyPI (the bare meetbot name was already taken by an unrelated 2024 project; the Python import is meetbot). Source is MIT at github.com/meetbot-dev/meetbot/tree/main/packages/sdk-python.

Install

bash pip install meetbot-sdk
bash uv add meetbot-sdk
bash poetry add meetbot-sdk

Requires Python 3.10+. Sync (MeetBot) and async (AsyncMeetBot) clients ship in the same package; HTTP transport is httpx.

Auth

from meetbot import MeetBot

mb = MeetBot(api_key="mb_live_…")        # explicit
mb = MeetBot()                            # picks up $MEETBOT_API_KEY
env vardefaultpurpose
MEETBOT_API_KEYBearer token. Required if not passed.
MEETBOT_API_BASE_URLhttps://api.meetbot.devOverride for self-hosted / staging.

Method reference

All methods on MeetBot have an identical async twin on AsyncMeetBot (same name, same kwargs, same return type, plus await).

MethodHTTPReturns
dispatch_bot(...)POST /api/v1/jobsJob
get_bot(job_id)GET /api/v1/jobs/{id}Job or None (404 → None)
list_bots(...)M1.1list[Job]
cancel_bot(job_id)DELETE /api/v1/jobs/{id}None
request_recording_permission(job_id)POST /api/v1/jobs/{id}/request-recording-permissionRequestRecordingPermissionResult
pause_recording(job_id)POST /api/v1/jobs/{id}/pause-recordingRequestRecordingPermissionResult
resume_recording(job_id)POST /api/v1/jobs/{id}/resume-recordingRequestRecordingPermissionResult
stop_recording(job_id)POST /api/v1/jobs/{id}/stop-recordingRequestRecordingPermissionResult
leave_bot(job_id)POST /api/v1/jobs/{id}/leave-callRequestRecordingPermissionResult
send_chat_message(job_id, message)POST /api/v1/jobs/{id}/send-chat-messageRequestRecordingPermissionResult
verify_webhook_signature(...) (module)n/a — localbool

Example 1 — dispatch + poll until completion

import os
import time
from meetbot import MeetBot

with MeetBot(api_key=os.environ["MEETBOT_API_KEY"]) as mb:
    job = mb.dispatch_bot(
        external_id="lesson-2026-05-09-pavel",
        meeting_url="https://meet.google.com/abc-defg-hij",
        display_name="Acme Notetaker",
    )
    print(f"dispatched {job.id}{job.status}")

    while True:
        snapshot = mb.get_bot(job.id)
        assert snapshot is not None
        print(snapshot.status)
        if snapshot.status in ("completed", "failed", "cancelled"):
            print("manifest:", snapshot.manifest_uri)
            break
        time.sleep(5)

Example 2 — async dispatch with auto-leave + metadata

import asyncio
from datetime import datetime, timezone
from meetbot import AsyncMeetBot, AutoLeaveConfig

async def main() -> None:
    async with AsyncMeetBot() as mb:
        job = await mb.dispatch_bot(
            external_id="q2-board-meeting-acme",
            meeting_url="https://meet.google.com/xyz-abcd-efg",
            join_at=datetime(2026, 5, 15, 14, 0, tzinfo=timezone.utc),
            display_name="Acme Notetaker",
            on_finalize_url="https://api.acme.example/meetbot/finalize",
            metadata={"customer_id": "cust_4711"},
            auto_leave=AutoLeaveConfig(
                after_entry_delay_seconds=300,
                after_max_seconds=7200,
                on_bot_detected=True,
            ),
        )
        print(job.id, job.status, job.join_at)

asyncio.run(main())

Example 3 — verify an inbound webhook (Flask)

The signed webhook is the recommended completion signal. Verify before parsing — anyone with your webhook URL could otherwise POST garbage at it.

import os
from flask import Flask, request, abort
from meetbot import verify_webhook_signature

app = Flask(__name__)
WEBHOOK_SECRET = os.environ["MEETBOT_WEBHOOK_SECRET"]  # whsec_…

@app.post("/webhook/meetbot")
def meetbot_hook():
    raw = request.get_data()  # bytes — do NOT use request.json
    if not verify_webhook_signature(
        body=raw,
        header=request.headers.get("X-Meetbot-Signature"),
        secret=WEBHOOK_SECRET,
    ):
        abort(401)

    event = request.get_json()
    if event["data"]["event"] == "job.finalized":
        manifest_uri = event["data"]["manifestUri"]
        process_recording_async(event["data"]["jobId"], manifest_uri)
    return "ok"

The same algorithm in JavaScript and Go is at Webhooks: Signature Verification.

Errors

Every SDK exception subclasses MeetbotError. HTTP errors carry both a numeric status and the orchestrator's machine-readable code:

from meetbot import (
    MeetBot,
    MeetbotNotFoundError,
    MeetbotRateLimitError,
    MeetbotUnauthorizedError,
    MeetbotValidationError,
)

try:
    job = mb.dispatch_bot(external_id="bad", meeting_url="not-a-url")
except MeetbotValidationError as e:
    print("validation:", e.body)  # {"error": "invalid_input", "issues": [...]}
except MeetbotRateLimitError as e:
    print("over quota / rate-limited:", e.status, e.code)
except MeetbotUnauthorizedError:
    print("rotate the key at /account/keys")

Retry + idempotency

  • Transport-level retry: 3x with exponential backoff + jitter on HTTP 429 / 500 / 502 / 503 / 504 and httpx.TransportError. Honours Retry-After. Override via MeetBot(max_retries=…).
  • Idempotency: every POST sends an auto-generated Idempotency-Key header. Pass idempotency_key= to fix it yourself.
  • Server-side dedupe: dispatch_bot()'s external_id doubles as a server-side idempotency key — reposting the same external_id returns the existing job.

See also

On this page