Most tutorials that show you how to read YouTube comments stop at the
first page. Here is what actually happens when you try to read a thread
that has 40,000 of them.
The endpoint, and what it actually costs
Comments live behind commentThreads.list on the YouTube Data API v3:
GET https://www.googleapis.com/youtube/v3/commentThreads
?part=snippet,replies
&videoId=VIDEO_ID
&maxResults=100
&pageToken=NEXT_PAGE
&key=YOUR_API_KEY
Two things bite people here.
It is quota-metered, not rate-limited. commentThreads.list costs 1 unit
per call against a default 10,000-unit daily quota. At maxResults=100 that
is 100 calls — and 100 calls is 1% of your day. A busy video with 40,000
comments needs 400 calls. You will hit the ceiling faster than you expect,
and the error is a plain 403 quotaExceeded, not a retryable 429.
maxResults is capped at 100 and the cap is silent. Ask for 500 and you
get 100 with no warning. Loop until a page returns fewer items than you asked
for, or you will spin forever on a video whose comment count is a multiple
of 100.
Pagination that actually terminates
The naive loop breaks in three places: the last page, a video with comments
disabled, and a video that is age-restricted.
import requests
def read_thread(video_id: str, api_key: str):
url = "https://www.googleapis.com/youtube/v3/commentThreads"
token, seen = None, 0
while True:
params = {
"part": "snippet,replies",
"videoId": video_id,
"maxResults": 100,
"key": api_key,
}
if token:
params["pageToken"] = token
resp = requests.get(url, params=params, timeout=15)
# 403 covers both "comments disabled" and "quota exhausted".
# Read the reason, do not just retry.
if resp.status_code == 403:
reason = resp.json()["error"]["errors"][0]["reason"]
if reason == "commentsDisabled":
return seen
raise RuntimeError(f"quota or permission problem: {reason}")
payload = resp.json()
items = payload.get("items", [])
seen += len(items)
token = payload.get("nextPageToken")
if not token:
return seen
The nextPageToken is absent on the final page — that is your exit
condition. Do not compare against pageInfo.totalResults; it is an estimate
and it drifts on large threads.
Replies are a second request
commentThreads.list returns the top-level comment plus, by default, a
single reply. Getting the rest means calling comments.list with
parentId. On a thread with heavy back-and-forth this doubles or triples
your quota spend, and there is no way to know in advance how much.
If you only need the top-level conversation, do not request replies at all —
you get roughly 3x the coverage for the same quota.
When you do not want to write any of this
I built Apriocity because I kept writing this same
pagination loop for one-off research tasks. It does the paging, the quota
accounting and the reply expansion for you, and hands back CSV or JSON:
- paste a video link, read the whole thread, search it by keyword
- export the slice as CSV or JSON
- batch mode: up to ten videos, one ZIP back
It uses the same public API described above, so what you see is exactly
what is publicly visible — nothing is stored on the server side.
The short version
-
commentThreads.list= 1 quota unit per call,maxResultscapped at 100 -
nextPageTokenmissing = done; do not trusttotalResults -
403is overloaded: check thereasonfield before retrying - Replies cost extra calls — skip them unless you need them
If you are doing this once, the API is fine. If you are doing it weekly,
that loop is worth automating away.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.