YouTube Transcript API Rate Limit: What It Is and How to Handle 429s

Why APIs return 429 Too Many Requests, what RFC 6585 and the Retry-After header actually specify, how rate limits differ from daily quota, and how to implement correct retry and backoff logic.

00:08:00 · SEP 13, 2026

Trả lời nhanh

Một 429 nghĩa là bạn đang gửi request nhanh hơn trần requests-per-minute của gói bạn cho phép - nó không liên quan đến tín dụng hay quota hàng ngày. Nếu response có header Retry-After, hãy chờ đúng khoảng thời gian đó. Nếu không có, hãy dùng exponential backoff kèm jitter, và giới hạn riêng số request đang chạy song song cùng lúc.

Rate limit so với quota

Đây là hai ràng buộc khác nhau nhưng thường bị nhầm lẫn:

  • Quota - tổng số request được phép trong một khoảng thời gian dài hơn (ví dụ một ngày). Dùng hết nghĩa là phải chờ reset, đôi khi cách nhiều giờ.
  • Rate limit - số request được phép trong một khoảng thời gian ngắn (ví dụ một phút). Chạm giới hạn nghĩa là chậm lại vài giây đến một phút, không phải hàng giờ.

Bên dưới lớp vỏ, hầu hết API thực thi rate limit bằng một trong hai cách: token bucket (bạn tích lũy "token" request theo một tốc độ ổn định và tiêu một token mỗi request, cho phép các đợt bùng nổ ngắn) hoặc một bộ đếm cửa sổ cố định/trượt (một trần cứng cho mỗi phút lịch, reset ở ranh giới). Cách nào được một API sử dụng sẽ ảnh hưởng đến mức lưu lượng bùng nổ mà bạn có thể an toàn gửi đi - một token bucket chịu được một đợt tăng đột biến ngắn tốt hơn một cửa sổ cố định.

Chuẩn HTTP: RFC 6585 và Retry-After

429 Too Many Requests không phải là quy ước riêng của nhà cung cấp nào - nó được định nghĩa chính thức trong RFC 6585 (tháng 4 năm 2012) như một mã trạng thái HTTP chuẩn. Theo RFC 9110, một server tuân thủ có thể đính kèm header Retry-After vào một response 429 (hoặc 503), theo một trong hai dạng:

  • Dạng số giây trễ - một số nguyên, ví dụ Retry-After: 120, nghĩa là chờ 120 giây.
  • Dạng HTTP-date - một mốc thời gian tuyệt đối, ví dụ Retry-After: Wed, 21 Oct 2026 07:28:00 GMT.

Khi có mặt, header này là căn cứ chính thức - server đang cho bạn biết chính xác khi nào nó sẽ chấp nhận request trở lại. Đừng chồng thêm exponential backoff của riêng bạn lên trên nó; điều đó chỉ trì hoãn thêm việc phục hồi. Chỉ dùng backoff khi header này vắng mặt.

Rate limit của GetYouTubeTranscript

GóiGiới hạn tốc độ
Miễn phí60 req/phút
Hàng tháng ($5/tháng)200 req/phút
Hàng năm ($4.50/tháng)300 req/phút

Cách nó thực sự được thực thi

Đây là một cửa sổ cố định 1 phút cho mỗi API key, không phải cửa sổ trượt hay token bucket - bộ đếm reset ở ranh giới phút đồng hồ. Hiện tại nó không gửi header Retry-After, nên hãy coi bất kỳ 429 nào từ API này là "chờ tối đa khoảng 60 giây và thử lại kèm backoff", dùng mẫu code bên dưới thay vì cố phân tích một header sẽ không xuất hiện.

Xử lý 429 đúng cách

Thứ tự thao tác đúng: kiểm tra Retry-After trước, và chỉ chuyển sang exponential backoff kèm jitter nếu thiếu header đó. Jitter (ngẫu nhiên hóa nhẹ thời gian chờ) quan trọng vì khi nhiều client cùng chạm giới hạn một lúc và tất cả thử lại theo cùng một lịch trình, chúng đồng bộ hóa và tạo ra một "đàn thác" ngay lập tức chạm lại cùng giới hạn đó.

Node.js:

async function getTranscriptWithRetry(videoId, apiKey, maxRetries = 4) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch(
      `https://getyoutubetranscript.com/api/v1/transcript?v=${videoId}`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    );
    if (res.status !== 429) return res.json();

    const retryAfter = res.headers.get('retry-after');
    const waitMs = retryAfter
      ? Number(retryAfter) * 1000
      : 500 * 2 ** attempt + Math.random() * 250; // exponential backoff + jitter
    await new Promise((r) => setTimeout(r, waitMs));
  }
  throw new Error('Rate limited after retries');
}

Python:

import os, time, random, requests

def get_transcript_with_retry(video_id, api_key, max_retries=4):
    url = "https://getyoutubetranscript.com/api/v1/transcript"
    for attempt in range(max_retries + 1):
        res = requests.get(url, params={"v": video_id}, headers={"Authorization": f"Bearer {api_key}"})
        if res.status_code != 429:
            return res.json()

        retry_after = res.headers.get("Retry-After")
        wait = float(retry_after) if retry_after else (0.5 * 2 ** attempt + random.uniform(0, 0.25))
        time.sleep(wait)
    raise RuntimeError("Rate limited after retries")

Tốc độ và mức đồng thời - hai vấn đề khác nhau

Một trần request-per-minute và một trần đồng thời (in-flight) không phải là cùng một thứ. Bắn 50 request cùng lúc bằng Promise.all có thể gây ra một 429 dù tổng số của bạn trong phút vẫn còn trong ngân sách, vì server (hoặc một proxy phía trước nó) cũng có thể giới hạn số kết nối đồng thời. Khi xử lý hàng loạt nhiều video, hãy giới hạn mức đồng thời độc lập với backoff rate-limit của bạn:

import pLimit from 'p-limit';

const limit = pLimit(5); // at most 5 requests in flight at once
const results = await Promise.all(
  videoIds.map((id) => limit(() => getTranscriptWithRetry(id, apiKey)))
);

Với các lô khối lượng lớn kéo dài, nâng cấp gói sẽ trực tiếp nâng trần req/phút - xem bảng giá trong tài liệu API, hoặc bắt đầu với 100 tín dụng miễn phí từ dashboard.

Câu hỏi thường gặp về rate limit

Q01

Khác biệt giữa rate limit và quota là gì?

Một quota giới hạn tổng mức sử dụng trong một khoảng thời gian (ví dụ mỗi ngày). Một rate limit giới hạn số request bạn có thể thực hiện trong một đơn vị thời gian (ví dụ mỗi phút), độc lập với tổng sử dụng hàng ngày của bạn - bạn có thể chạm rate limit trong khi vẫn còn nhiều dưới quota hàng ngày. Xem hướng dẫn về quota YouTube API của chúng tôi để biết phần quota.

Q02

Một response 429 nghĩa là gì?

429 Too Many Requests được định nghĩa chính thức trong RFC 6585 năm 2012. Nó nghĩa là bạn đã vượt giới hạn requests-per-window của gói mình - đó là tín hiệu để chậm lại và thử lại, không phải request của bạn không hợp lệ hay bạn đã hết tín dụng.

Q03

Header Retry-After là gì, và tôi có nên luôn tin nó không?

Theo RFC 9110, một server có thể đính kèm header Retry-After vào một response 429 hoặc 503, theo một trong hai dạng: một số nguyên giây (Retry-After: 120) hoặc một HTTP-date tuyệt đối. Nếu có mặt, hãy tuân theo chính xác thay vì chồng thêm backoff riêng của bạn lên trên - server đã cho bạn một câu trả lời chính thức về thời điểm nên thử lại.

Q04

Rate limit của GetYouTubeTranscript là gì, và nó có gửi header Retry-After không?

60 request/phút ở gói miễn phí, 200 req/phút ở gói hàng tháng, và 300 req/phút ở gói hàng năm, thực thi theo cửa sổ cố định 1 phút cho mỗi API key (không phải cửa sổ trượt). Hiện tại nó không gửi header Retry-After trên các lỗi 429, nên hãy tự cài đặt backoff (xem đoạn code bên dưới) thay vì trông chờ vào header đó - vì cửa sổ là cố định thay vì trượt, thời gian chờ tối đa chỉ là phần còn lại của phút hiện tại.

Q05

Một 429 có tiêu tốn tín dụng không?

Không - một request bị rate-limit không bao giờ bị trừ phí. Chỉ request thành công mới tiêu tốn tín dụng.

Q06

Tôi có nên mở nhiều API key để có thêm thông lượng không?

Không - việc này thường vi phạm điều khoản sử dụng công bằng của nhà cung cấp, và riêng với GetYouTubeTranscript, rate limit đã được thực thi theo từng API key, nhưng tín dụng và trạng thái tài khoản được theo dõi ở cấp tài khoản bất kể bạn tạo bao nhiêu key. Nếu bạn cần thêm thông lượng, hãy nâng cấp gói thay vào đó, điều này trực tiếp nâng trần req/phút.

Q07

Vì sao request của tôi vẫn thất bại dù tôi đang dưới giới hạn requests-per-minute?

Đó thường là vấn đề đồng thời, không phải vấn đề tốc độ - bắn 50 request song song có thể làm quá tải server dù tổng số của bạn trong phút vẫn còn trong ngân sách. Hãy dùng một bộ giới hạn đồng thời (như p-limit trong Node hoặc một semaphore trong Python) để giới hạn số request đang chạy cùng lúc, độc lập với số request bạn gửi mỗi phút.

Liên quan