If you want a bot that joins a Zoom meeting and captures what people say, the first thing you will learn is that Zoom does not offer a simple API for it.
Zoom does have a Meeting SDK, but bots built on it require a feature review that can take four to six weeks before the bot can join external meetings.
The SDK also does not give you the transcript directly, you would need to capture raw audio and route it to a separate transcription service, which adds significant complexity.
The faster and more practical path is browser automation. You script a real Chromium browser to navigate to the Zoom web client, join the meeting as a participant, enable closed captions, and scrape the caption text from the page.
Zoom's closed captions use Zoom's own transcription engine and are completely free. This guide walks through building exactly that, using Node.js and Playwright.
Quick Recap
Extract the meeting ID and password from the Zoom URL
Build the web client join URL using zoom.us/wc/{meetingID}/join?pwd={password}
Launch Chromium with fake media device flags to satisfy Zoom's media checks
Fill in the display name on the pre-join screen and click Join
Enable captions through the toolbar using the Live Transcript button
Poll the captions container every two seconds and write new lines to a file with deduplication
Wrap each bot in a Docker container for isolated concurrent runs
Handle waiting rooms, selector changes, and caption accuracy as ongoing maintenance tasks
What You Need Before Building Zoom Meeting Bot?
Node.js version 18 or above
A Zoom meeting link you can test with
Basic familiarity with JavaScript and async/await
Docker installed (optional but recommended for running multiple bots cleanly)
How to Build a Zoom Meeting Bot?
Step 1: Set Up Your Project
Create a project folder and initialise it.
bash
mkdir zoom-bot
cd zoom-bot
npm init -y
Install Playwright and download the Chromium browser it will control.
bash
npminstall playwright
npx playwright install chromium
Create your main bot file and a simple Express server file.
bash
touch bot.js server.js
npminstall express
Step 2: Extract the Meeting ID and Password from the Zoom URL
Before the bot can navigate to the Zoom web client, it needs the meeting ID and the meeting password separately. A standard Zoom meeting link looks like this:
This URL takes the bot straight to the browser-based join flow without triggering the "Open in Zoom app" dialog.
Step 3: Launch the Browser and Navigate to the Meeting
Start a Chromium browser with flags that fake microphone and camera access. Without these, Zoom will block entry because it cannot detect media devices.
If the meeting has a waiting room enabled, the bot will be held there until the host admits it. The bot will wait on the waiting room screen until the host clicks Admit.
Step 5: Enable Closed Captions
Zoom includes free closed captions powered by its own transcription models. The bot enables them through the meeting toolbar.
javascript
asyncfunctionenableCaptions(page){
// Look for the "Live Transcript" or "Captions" button in the toolbar
// Zoom uses different button labels depending on the account settings
One important note: the host's Zoom account must have closed captions enabled in account settings. If the button does not appear, the account admin may need to turn on Live Transcription in the Zoom web portal under Account Settings.
Step 6: Scrape and Save the Transcript
Poll the captions container every two seconds. New text is written to a file and deduplicated so you do not save the same line twice.
If you need one bot per meeting, wrap each bot in its own Docker container so they run in isolation without sharing browser state or file handles. Create a simple Dockerfile:
dockerfile
FROM mcr.microsoft.com/playwright:v1.44.0-jammy
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "bot.js"]
Your Express server can then spin up a new container for each meeting URL it receives, and each container writes its transcript to a volume mounted on the host.
Common Problems and How to Fix Them
The Join button selector fails. Zoom updates its web client regularly. If button.preview-join-button stops working, open Zoom in a regular Chrome window, inspect the join button, and find the current class or aria-label. Role-based selectors like page.getByRole('button', { name: 'Join' }) are more stable than class names.
Captions do not appear. The most common reason is that the Zoom account hosting the meeting has not enabled Live Transcription in settings. The host needs to turn this on at zoom.us under Account Settings before the bot can use it.
The bot lands in the waiting room and never enters. Either admit the bot manually from the host controls, or create test meetings with the waiting room turned off by setting "Security" options when scheduling.
Caption text is incomplete. Zoom's caption engine paraphrases and sometimes drops words during fast speech. For verbatim accuracy, consider routing the meeting audio to a dedicated ASR service like Whisper or Deepgram instead of relying on DOM-scraped captions.
Conclusion
This bot works well as a prototype and for internal tooling. When you move to production and need to run dozens of bots concurrently, the workload shifts from writing code to managing infrastructure: scheduling containers, monitoring crashed bots, handling retries, and chasing Zoom web client updates that break your selectors.
If you want all of that handled for you, Meetstream.ai provides a clean API that sends bots to Zoom meetings, returns real-time speaker-attributed transcripts, and stores recordings without you maintaining any browser infrastructure yourself.