Skip to main content

msteams-transcripts

Download Microsoft Teams meeting transcripts from the command line, including the meetings where the Download button is greyed out.

It drives a browser you are already signed in to, so it needs no admin consent, no app registration and no Graph permissions, and it reaches exactly the meetings you can open yourself. Anything you can read in the Recap tab, this can save to a file.

uvx msteams-transcripts browser
uvx msteams-transcripts list --from 2026-09-01 --to today
uvx msteams-transcripts get 3 --details

Why the Download button being disabled does not matter

  • Listing meetings. The Teams web client loads your calendar from its middle-tier API. Each online meeting carries a thread identifier.
  • Finding the transcript. The Recap tab asks the meeting-content service about that thread. The reply lists resources; a transcript resource holds the OneDrive drive, item and transcript identifiers.
  • Reading the transcript. The recap page renders it from SharePoint, at /_api/v2.1/drives/<driveId>/items/<itemId>/media/transcripts/<id>/content, in JSON or WebVTT. That call is authenticated by your SharePoint cookies alone. The whole transcript is already delivered to your browser in one request, so no scrolling or scraping is involved. The disabled button is a user-interface policy, not an access control.
  • Tokens. The two Teams APIs want bearer tokens that the web client keeps encrypted in local storage. The tool captures them from the requests the Teams tab makes as it loads.

Install

With uv, nothing to install:

uvx msteams-transcripts --help

Or install it permanently:

uv tool install msteams-transcripts     # or: pipx install msteams-transcripts
pip install msteams-transcripts         # into the current environment

Both msteams-transcripts and the shorter teams-transcripts are installed as commands. Python 3.10 or newer is required. Playwright comes as a dependency, but do not run playwright install: the tool attaches to the Edge or Chrome already on your machine and downloads no browsers of its own.

Walkthrough

A first session from a clean machine, start to finish.

1. Start the browser.

uvx msteams-transcripts browser

An Edge window opens on Teams using a profile of its own. On a corporate, Entra-joined PC it usually signs in by itself. If a sign-in page appears, complete it there once. Leave the window open; you can minimise it. Recent Edge and Chrome refuse remote debugging on your everyday profile, which is why a separate one is used.

2. See what is available.

uvx msteams-transcripts list --from 2026-09-01 --to today
  #  Start (UTC)       Subject                                            Organizer
  1  2026-09-15 05:41  Weekly one to one                                  Priya Raman
  2  2026-09-15 06:09  Platform team sync                                 Tomas Lindqvist
  3  2026-09-16 09:02  Architecture review: storage tiering               Maya Okonkwo
12 transcript(s). Use:  msteams-transcripts get <#>

The first run takes 30 to 60 seconds while Teams loads and issues its tokens; later runs in the same browser session are quicker. Rows are transcripts rather than meetings, so a recurring meeting recorded twice appears twice.

3. Download one transcript with the meeting details.

uvx msteams-transcripts get 3 --details

This writes transcripts\2026-09-16 0902 - Architecture review storage tiering.txt:

# Architecture review: storage tiering
# Date: 2026-09-16 09:02
# Source: https://contoso-my.sharepoint.com/personal/.../Recordings/Architecture-review....mp4
# Organizer: Maya Okonkwo <maya.okonkwo@example.com>
# Location: Microsoft Teams Meeting
# Invited (13):
#   Priya Raman <priya.raman@example.com> (Required, Accepted)
#   Tomas Lindqvist <tomas.lindqvist@example.com> (Required, Tentative)
#   ...
# Speakers in transcript (5):
#   Priya Raman (84 segments)
#   Maya Okonkwo (72 segments)
#   ...
# Files shared in meeting chat (1):
#   capacity-plan.xlsx  https://contoso.sharepoint.com/sites/...
# Invitation text:
#   Agenda: ...

[00:00:03] Priya Raman:
    Good morning everyone, let's start with the architecture review.

A sidecar ...meta.json holds the same details as structured data: attendees with response status, speakers with segment counts, shared files, invitation text and the thread identifier.

4. Download from a link instead. Open the meeting in Teams, go to the Recap tab, copy the address bar, and pass it:

uvx msteams-transcripts get "https://teams.cloud.microsoft/l/meetingrecap?driveId=...&driveItemId=...&sitePath=..." --details -f all

This path does not touch the calendar and finishes in a few seconds.

5. Archive a whole period.

uvx msteams-transcripts batch --from 2026-09-01 --to 2026-09-30 --details -o C:\Archive\teams
uvx msteams-transcripts batch --from 2026-09-01 --to 2026-09-30 --only 3,7,12

Files with the same name are overwritten. Batch ends with a count of what was downloaded and what failed.

6. Skipping the "Stay better connected with the Teams desktop app" popup when you open recap links yourself: change /l/meetingrecap? in the address to /_#/l/meetingrecap?. That form opens the web client directly. The tool already uses it internally.

Commands

msteams-transcripts browser
msteams-transcripts list  --from YYYY-MM-DD --to YYYY-MM-DD|today [--json]
msteams-transcripts get   <row#|recap-url|thread-id> [-d] [-f txt|json|vtt|all] [-o DIR]
msteams-transcripts batch --from D --to D [-d] [-f FMT] [-o DIR] [--only 3,7,12]
Option Meaning
-d, --details organizer, location, invited attendees with response status, speakers, shared files, invitation text, plus a .meta.json sidecar
-f, --format txt (default), json for the raw Stream document, vtt for WebVTT, or all
-o, --out output directory, ./transcripts by default
--json on list, print the rows as JSON for scripting
--only on batch, restrict to given row numbers
--port, --profile, --browser override the debugging port, profile directory or browser executable

The text format is:

[00:00:03] Speaker Name:
    what they said
    the next sentence by the same speaker

Timestamps are offsets from the start of the recording, not wall-clock time. Row numbers used by get N come from the most recent list, cached in ~/.teams_transcripts/last_list.json.

Using it from an agent

.claude/skills/teams-transcripts/SKILL.md describes the tool for Claude Code or another agent: preconditions, commands, output format, typical flows and error handling. Claude Code picks it up automatically when working in this project.

Library use

import asyncio
from pathlib import Path
from teams_transcripts import TeamsSession, download_ref, refs_from_recap_url

async def main():
    async with TeamsSession() as s:
        ref = refs_from_recap_url("https://teams.cloud.microsoft/l/meetingrecap?...")
        await download_ref(s, ref, Path("out"), "txt", details=True)

asyncio.run(main())

Anything the user can act on raises TranscriptError.

Environment variables

Variable Default Purpose
TT_CDP_PORT 9222 browser debugging port
TT_PROFILE_DIR platform application-data directory browser profile
TT_BROWSER_EXE Edge, then Chrome, from the usual locations browser executable
TT_STATE_DIR ~/.teams_transcripts where the last listing is cached

Troubleshooting

  • "The browser did not expose its debugging port": another window is already using the dedicated profile without the port. Close it, then run browser again.
  • "Could not capture a Teams token": the Teams tab in that window is not signed in. Sign in there, then retry.
  • "Could not capture the meeting-content token": open any meeting's Recap tab in that window, then retry.
  • HTTP 403 when listing or downloading: the recording belongs to a tenant or site you cannot read. The Recap tab would be empty for you too.
  • Not available through this tool: the attendance report of who actually joined and when, collaborative Loop notes, and Copilot AI notes. --details gives the invited list from the calendar and who actually spoke per the transcript.
  • Recurring meetings share one thread identifier. Each occurrence's transcript is listed separately with its own start time.

Development

git clone https://github.com/divyavanmahajan/msteams-transcripts
cd msteams-transcripts
pip install -e ".[dev]"
python -m pytest

The tests cover the pure functions in model.py and formats.py and need neither a browser nor the network.

Hatchling owns the version, read from __version__ in src/teams_transcripts/__init__.py. To release:

hatch version patch            # or minor, major, or an explicit 1.2.3
git commit -am "Release 0.1.1"
git tag v0.1.1
git push --follow-tags

Pushing the tag runs .github/workflows/publish.yml, which refuses to continue if the tag and the package version disagree, then builds, publishes to PyPI through trusted publishing and creates a GitHub release. Trusted publishing is configured once on PyPI under Publishing, naming this repository, the publish.yml workflow and the pypi environment; no API token is stored. Running the workflow by hand offers TestPyPI as the target.

Licence

MIT. Transcripts are meeting content belonging to the people in them; this tool only fetches what you already have access to, and what you do with it afterwards is your responsibility.

Release files for msteams-transcripts 1.0.1

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

Source distribution (sdist)

Source distribution for msteams-transcripts 1.0.1
File Size Uploaded
msteams_transcripts-1.0.1.tar.gz 22.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for msteams-transcripts 1.0.1
File Interpreter ABI Platform
msteams_transcripts-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 47.3 kB

Release files / msteams_transcripts-1.0.1.tar.gz

Download URL msteams_transcripts-1.0.1.tar.gz
Size 22.8 kB
Tags Source
SHA-256 checksum
How to use checksums
cb14952eee03cf686879b981a2e702702896212e6e35675e4a2040021231f785
BLAKE2b-256 checksum
How to use checksums
eb15bfd142ed1bf066670ae167acab5c8a32b8d803aff2288bcfe6230263a750
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release files / msteams_transcripts-1.0.1-py3-none-any.whl

Download URL msteams_transcripts-1.0.1-py3-none-any.whl
Size 24.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3bbf07ab7008a14b940afc051f5c5f09e21bb18881b37b2e019ad270b84befd5
BLAKE2b-256 checksum
How to use checksums
71545713ec3540f98ef9ec775573d53cc4e97ecb894fbf7914d370317a5bd36f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release 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