Generated banner for Build Zoom Rest API App
Generated logo for Build Zoom Rest API App

Build Zoom Rest API App

OpenAIOfficialCreated by ZoomDistributed by OpenAI

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Medium risk

From the author

Use when calling REST APIs.
— the description by Zoom

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to interacting with the Zoom REST API. It offers guidance on selecting appropriate authentication flows, managing resources such as meetings and users, and implementing robust API interaction patterns like pagination and rate-limit handling. It includes reference documentation for various Zoom product areas, including account management and AI Companion services, to assist developers in building API-integrated applications.

What it does

  • Provides endpoint inventories for Zoom API product areas.
  • Outlines authentication requirements for user-level, account-level, and server-to-server workflows.
  • Defines patterns for implementing API wrappers with pagination and retry logic.
  • Offers guidance on webhook processing, including signature verification.
  • Assists in troubleshooting common API issues like scope mismatches and rate limits.

When to use it

  • When developing applications that need to programmatically manage Zoom meetings, users, or recordings.
  • When integrating Zoom account settings or dashboard metrics into custom software.
  • When setting up event-driven workflows using Zoom webhooks.
  • When debugging authentication or scope-related errors during Zoom API integration.

Advantages

  • Centralizes access to official Zoom API endpoint documentation.
  • Standardizes the implementation of API interaction patterns.
  • Clarifies the selection of correct authentication scopes for different API operations.
  • Provides a clear methodology for handling API rate limits and resource management.

Usage examples

“How do I list meeting participants using the Zoom API?”
The agent provides the correct endpoint path from the dashboard references and explains the necessary OAuth scopes and pagination requirements.
“What authentication should I use for an account-level Zoom integration?”
The agent guides you through the account-level OAuth flow requirements as defined in the authentication documentation.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/openai/build-zoom-rest-api-app/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip build-zoom-rest-api-app.zip -d .agents/skills

Documentation by Zoom

Build Zoom REST API App

Use this skill when the task needs deterministic Zoom API calls or resource management from application code.

Workflow

  1. Define the resource and actor: user, meeting, webinar, recording, docs, chat, phone, account, or admin-level workflow.

  2. Select the endpoint and required scopes from the reference files before coding.

  3. Confirm auth fit: user-level OAuth for user-owned resources, account-level OAuth for admin workflows, and only use server-to-server OAuth where the target API documents support for it.

  4. Implement narrow API wrappers with explicit pagination, retry, idempotency, and rate-limit handling.

  5. Treat webhook processing as a separate event-ingestion path with signature verification and replay protection.

  6. Debug by checking token audience, missing scopes, resource ownership, account settings, API enablement, and rate-limit headers.

References

Files

concepts/rate-limiting-strategy.md

Rate Limiting Strategy

Zoom API rate limits by plan, category, and strategies for handling them in production.

Rate Limits by Account Plan

Rate limits are per-account (shared by all users and all apps on the account):

Main REST API
CategoryFreeProBusiness+
Light4/sec, 6,000/day30/sec80/sec
Medium2/sec, 2,000/day20/sec60/sec
Heavy1/sec, 1,000/day10/sec*40/sec*
Resource-Intensive10/min, 30,000/day10/min*20/min*

* Combined daily limits:

  • Pro: 30,000/day (Heavy + Resource-Intensive shared)

  • Business+: 60,000/day (Heavy + Resource-Intensive shared)

Business+ includes: Business, Education, Enterprise, and Partners.

Zoom Phone API
CategoryProBusiness+
Light20/sec40/sec
Medium10/sec20/sec
Heavy5/sec, 15,000/day*10/sec, 30,000/day*
Resource-Intensive5/min, 15,000/day*10/min, 30,000/day*

* Daily limit shared between Heavy and Resource-Intensive.

Zoom Contact Center API
CategoryProBusiness+
Light20/sec40/sec
Medium10/sec20/sec
Heavy5/sec, 15,000/day*10/sec, 30,000/day*

* Daily limit shared with Resource-Intensive APIs.

Video SDK Account Rate Limits
PlanUses Limits
Pay As You Go (Deprecated)Pro
Annual Prepay Monthly UsagePro
All other plansBusiness+

Endpoint Category Examples

LightMediumHeavy
Get A MeetingCreate MeetingGet Daily Usage Report
Get Meeting RecordingsList All RecordingsList Devices
Add Meeting RegistrantGet Past Meeting Participants—
Update A MeetingList Meetings—

Per-User Daily Limits

These are separate from account-level rate limits:

OperationLimitReset
Meeting/Webinar Create/Update100/day per user00:00 UTC
Registrant Addition3/day per registrant00:00 UTC
Registrant Status Updates10/day per registrant00:00 UTC

The 100/day limit applies to all Meeting/Webinar IDs hosted by a specific user. To bulk-create meetings, distribute across multiple host users.

Concurrent Request Limits (Lock-Key)

Zoom enforces single-concurrency on certain resource operations:

ScenarioBehavior
Multiple DELETE on same userIdOnly 1 concurrent DELETE allowed
POST to /v2/usersBlocks GET/PATCH/PUT/DELETE until complete

Error:

{
  "code": 429,
  "message": "Too many concurrent requests. A request to disassociate this user has already been made."
}

Response Headers

Every API response includes rate limit information:

HeaderDescription
X-RateLimit-CategoryLight, Medium, Heavy, or Resource-intensive
X-RateLimit-TypeQPS (per-second) or Daily-limit
X-RateLimit-LimitMax requests in current window
X-RateLimit-RemainingRequests remaining
X-RateLimit-ResetUnix timestamp when per-second limit resets
Retry-AfterISO 8601 datetime when daily limit resets
Example — Normal Response
X-RateLimit-Category: Medium
X-RateLimit-Type: QPS
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 55
Example — Per-Second Rate Limited
HTTP/1.1 429 Too Many Requests
X-RateLimit-Category: Light
X-RateLimit-Type: QPS
X-RateLimit-Limit: 80
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705312800
Example — Daily Rate Limited
HTTP/1.1 429 Too Many Requests
X-RateLimit-Category: Heavy
X-RateLimit-Type: Daily-limit
X-RateLimit-Limit: 60000
X-RateLimit-Remaining: 0
Retry-After: 2025-01-20T00:00:00Z

Strategy 1: Exponential Backoff with Jitter

The simplest retry strategy for handling 429 responses:

async function callZoomAPI(url, options, maxRetries = 5) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, options);

    if (response.status === 429) {
      // Check for daily limit (Retry-After header)
      const retryAfter = response.headers.get('Retry-After');
      if (retryAfter) {
        const waitMs = new Date(retryAfter) - Date.now();
        console.warn(`Daily limit hit. Retry after: ${retryAfter}`);
        if (waitMs > 0 && waitMs < 86400000) {
          await sleep(waitMs);
          continue;
        }
        throw new Error(`Daily rate limit hit. Retry after ${retryAfter}`);
      }

      // Per-second limit — exponential backoff with jitter
      const baseDelay = Math.pow(2, attempt) * 1000;
      const jitter = baseDelay * 0.2 * Math.random();
      const delay = baseDelay + jitter;
      console.warn(`Rate limited. Retrying in ${Math.round(delay)}ms (attempt ${attempt + 1})`);
      await sleep(delay);
      continue;
    }

    return response;
  }
  throw new Error('Max retries exceeded for Zoom API');
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Strategy 2: Proactive Throttling

Monitor remaining quota and slow down before hitting limits:

async function throttledRequest(url, options) {
  const response = await fetch(url, options);

  const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '999');
  const limit = parseInt(response.headers.get('X-RateLimit-Limit') || '999');
  const category = response.headers.get('X-RateLimit-Category');

  // Proactive throttling when under 10% quota
  if (remaining < limit * 0.1) {
    const resetTs = response.headers.get('X-RateLimit-Reset');
    if (resetTs) {
      const waitMs = (parseInt(resetTs) * 1000) - Date.now();
      if (waitMs > 0 && waitMs < 10000) {
        console.warn(`[${category}] ${remaining}/${limit} remaining — throttling ${waitMs}ms`);
        await sleep(waitMs);
      }
    } else {
      await sleep(1000);
    }
  }

  return response;
}

Strategy 3: Request Queue (High-Volume)

For applications making many concurrent requests:

class ZoomRateLimitedQueue {
  constructor(requestsPerSecond = 10, minDelayMs = 100) {
    this.queue = [];
    this.running = 0;
    this.maxConcurrent = requestsPerSecond;
    this.minDelayMs = minDelayMs;
    this.processing = false;
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.process();
    });
  }

  async process() {
    if (this.processing) return;
    this.processing = true;

    while (this.queue.length > 0) {
      if (this.running >= this.maxConcurrent) {
        await sleep(this.minDelayMs);
        continue;
      }

      const { requestFn, resolve, reject } = this.queue.shift();
      this.running++;

      requestFn()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.running--;
        });

      await sleep(this.minDelayMs);
    }

    this.processing = false;
  }
}

// Usage — process 10 requests/sec max
const queue = new ZoomRateLimitedQueue(10, 100);

const userIds = ['user1', 'user2', 'user3', /* ... */];
const results = await Promise.all(
  userIds.map(id =>
    queue.add(() => zoom.request('GET', `/users/${id}`))
  )
);

Best Practices

1. Cache GET Responses
const cache = new Map();

async function cachedGet(path, ttlMs = 60000) {
  const cached = cache.get(path);
  if (cached && Date.now() - cached.time < ttlMs) {
    return cached.data;
  }
  const data = await zoom.request('GET', path);
  cache.set(path, { data, time: Date.now() });
  return data;
}
2. Use Webhooks Instead of Polling
// DON'T: Poll for meeting status changes
setInterval(async () => {
  const meetings = await zoom.request('GET', `/users/${userId}/meetings`);
}, 60000);

// DO: Receive webhook events
app.post('/webhook', (req, res) => {
  handleEvent(req.body);
  res.status(200).send();
});

See zoom-webhooks for webhook implementation.

3. Use List Endpoints with Pagination
// DON'T: Fetch users one by one (N API calls)
for (const id of userIds) {
  const user = await zoom.request('GET', `/users/${id}`);
}

// DO: Fetch in bulk (1 API call per page)
const allUsers = await zoom.request('GET', '/users?page_size=300');
4. Distribute Bulk Creates Across Users
// Avoid hitting the 100/day per-user limit
const hosts = ['[email protected]', '[email protected]', '[email protected]'];
let hostIndex = 0;

for (const meeting of meetingsToCreate) {
  const host = hosts[hostIndex % hosts.length];
  await zoom.request('POST', `/users/${host}/meetings`, meeting);
  hostIndex++;
  await sleep(100); // Prevent per-second burst
}
5. Use QSS for Quality Data

For Quality of Service data, use QSS (push-based) instead of polling Reports API:

  • Streams telemetry via webhooks/WebSocket

  • Pushes data 4-6 times per minute

  • Drastically reduces API call volume

Common Gotchas

IssueSolution
429 on first request of the dayAnother app on account used quota
Different limits than documentedCheck account type (Free/Pro/Business+)
Meeting create fails at 100/dayPer-user limit — distribute across hosts
Concurrent DELETE errorsSerialize DELETE operations on same user
Daily limit hit unexpectedlyHeavy + Resource-Intensive share quota

Resources

Requirements and compatibility

Written for: OpenAI Codex

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

What our scanner noticed· Medium risk

  • Possible data-exfiltration endpoints
  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Zoom
Publisher
OpenAI (Official)
Distributed by
OpenAI
Source
openai/plugins · plugins/zoom/skills/rest-api · commit d420d13
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from plugins/zoom/LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Zoom or OpenAI.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download build-zoom-rest-api-app.zip