Docs · Video comments
Video comments
A video's comments, top or newest first, with likes, reply counts and who wrote them.
https://api.lurkapi.com/v1/youtube/video/commentsWhen to use this
Use it for audience research and sentiment: what viewers say, which comments get the most likes, and what the creator pinned or hearted. About 20 comments per page; the first page also has the exact commentCount.
Pagination. Pass the response's continuationToken back as continuationToken (with the same url) for the next page; it's null at the end. YouTube stops at about 1,000 top comments, or a few thousand newest ones.
Replies. Comments with replies have a repliesContinuationToken; pass it to comment replies.
Videos with comments turned off, and unknown videos, return an empty comments list.
Parameters
All parameters go in the query string.
| Parameter | Description |
|---|---|
urlstringrequired | The video's URL (watch, youtu.be, /shorts/, /live/ or /embed/ links all work) or its 11-character id, e.g. https://www.youtube.com/watch?v=dQw4w9WgXcQ.
|
orderstringoptional | top (YouTube's ranking) or newest first.
|
continuationTokenstringoptional | The continuationToken from the previous response, to get the next page. |
Example request
Replace YOUR_API_KEY with your key, or set LURKAPI_KEY for the code. Get a free key.
curl "https://api.lurkapi.com/v1/youtube/video/comments?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ" \
-H "x-api-key: YOUR_API_KEY"const params = new URLSearchParams({
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
});
const res = await fetch(`https://api.lurkapi.com/v1/youtube/video/comments?${params}`, {
headers: { "x-api-key": process.env.LURKAPI_KEY },
});
const data = await res.json();
if (!data.success) throw new Error(`${data.code}: ${data.error}`);
console.log(data.comments);import os
import requests
res = requests.get(
"https://api.lurkapi.com/v1/youtube/video/comments",
params={
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
},
headers={"x-api-key": os.environ["LURKAPI_KEY"]},
timeout=60,
)
data = res.json()
if not data["success"]:
raise RuntimeError(f"{data['code']}: {data['error']}")
print(data["comments"])Example response
A real response from this endpoint, captured from the live API and trimmed to a couple of items. Strings over 96 characters (mostly signed media URLs) are cut short and end in ….
Show the example response (2 KB)Hide
{
"success": true,
"credits_remaining": 999985,
"credits_charged": 1,
"comments": [
{
"id": "Ugzge340dBgB75hWBm54AaABAg",
"content": "can confirm: he never gave us up",
"publishedTimeText": "1 year ago",
"publishedTime": "2025-09-30T14:33:52.979Z",
"replyLevel": 0,
"author": {
"name": "@YouTube",
"channelId": "UCBR8-60-B28hp2BmDPdntcQ",
"isVerified": true,
"isCreator": false,
"avatarUrl": "https://yt3.ggpht.com/3s6evpqAiDU9tQR4sC2siJippbH2RWVPnwHgyl4V0th2iuQz0V…",
"channelUrl": "https://www.youtube.com/@YouTube"
},
"engagement": {
"likes": 321000,
"replies": 963
},
"repliesContinuationToken": "Eg0SC2RRdzR3OVdnWGNRGAYygwEaUBIaVWd6Z2UzNDBkQmdCNzVoV0JtNTRBYUFCQWciAggA…",
"isPinned": true,
"isHearted": true
},
{
"id": "UgyEnXfdC-umwvTt8JF4AaABAg",
"content": "Gonna flag this for nudity so I can rick roll the YouTube staff",
"publishedTimeText": "6 years ago",
"publishedTime": "2020-10-01T14:33:52.979Z",
"replyLevel": 0,
"author": {
"name": "@Oatman69",
"channelId": "UCWf34D_s8K_m2vwBISIDWtg",
"isVerified": false,
"isCreator": false,
"avatarUrl": "https://yt3.ggpht.com/ghenbV7T5VMOA3iqp3PThC82exqcu7iVng_iWNx1Ujak72Ti4o…",
"channelUrl": "https://www.youtube.com/@Oatman69"
},
"engagement": {
"likes": 567000,
"replies": 707
},
"repliesContinuationToken": "Eg0SC2RRdzR3OVdnWGNRGAYygwEaUBIaVWd5RW5YZmRDLXVtd3ZUdDhKRjRBYUFCQWciAggA…",
"isPinned": false,
"isHearted": false
}
],
"continuationToken": "Eg0SC2RRdzR3OVdnWGNRGAYyggMK2AJnZXRfcmFua2VkX3N0cmVhbXMtLUNxY0JDSUFFRlJl…",
"commentCount": 2457944
}Response fields
Every field of a successful response. [] marks a list: a[].b is the b of each item in a. nullable fields can be null; optional fields can be missing.
Show all 24 fieldsHide
| Field | Type | Description |
|---|---|---|
| success | true | Always true here; errors have success: false. |
| credits_remaining | number | Your balance after this call. On anonymous playground calls: free tries left today. |
| credits_charged | number | Credits this call cost; 0 on free endpoints. On anonymous playground calls: tries used (1). |
| comments | object[] | One page of comments, in the requested order. |
| comments[].id | string | Comment id. Replies are <parent id>.<reply id>. |
| comments[].content | string | Comment text. |
| comments[].publishedTimeText | string | When it was posted, as YouTube shows it, e.g. "3 days ago" or "6 years ago (edited)". |
| comments[].publishedTime | stringnullable | Approximate post time, ISO 8601, worked out from publishedTimeText: only as precise as that text ("6 years ago" is ±6 months). |
| comments[].replyLevel | number | 0 for a comment, 1 for a reply. |
| comments[].author | object | Who wrote it. |
| comments[].author.name | string | Author's handle, e.g. @YouTube. |
| comments[].author.channelId | stringnullable | Author's channel id, UC…. |
| comments[].author.isVerified | boolean | The author has a verified badge. |
| comments[].author.isCreator | boolean | The author is the video's creator. |
| comments[].author.avatarUrl | stringnullable | Author's avatar URL. |
| comments[].author.channelUrl | stringnullable | Author's channel page. |
| comments[].engagement | object | Likes and replies. |
| comments[].engagement.likes | number | Likes; YouTube rounds above 1,000 (321K → 321000). |
| comments[].engagement.replies | number | Number of replies. |
| comments[].repliesContinuationToken | stringnullable | Pass as continuationToken to comment replies to read the replies. null when there are none (and always on replies). |
| comments[].isPinned | boolean | Pinned by the creator. |
| comments[].isHearted | boolean | Hearted by the creator. |
| continuationToken | stringnullable | Pass as continuationToken for the next page. null on the last page. |
| commentCount | numbernullable | Exact number of comments on the video (replies included). First page only; null on later pages. |
Caching and freshness
Responses are cached for up to 10 minutes, so a repeat call within that window can return the same data. It still costs 1 credit.
Errors
Errors return { success: false, error, code, docs } with the HTTP status below. Validation errors, 401/402/429 rejections and 5xx failures are free; not_found is charged (how charging works). All error codes.
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_api_key | No API key. Send it in the x-api-key header. |
| 401 | invalid_api_key | The key is unknown or was revoked. |
| 402 | insufficient_credits | Not enough credits for this call. Buy a pack or wait for tomorrow's top-up. |
| 405 | method_not_allowed | Endpoints take GET with query params. |
| 429 | rate_limited | Too many calls at once. Wait for the retry-after seconds, then retry. |
| 500 | internal_error | Something broke on our side. Retry; 5xx errors are free. |
| 502 | upstream_error | The platform didn't give a usable answer. Retry; 5xx errors are free. |
| 503 | upstream_busy | All our connections to the platform are busy. Retry in a few seconds; 5xx errors are free. |
| 504 | upstream_timeout | The platform took over 30 seconds to answer. Retry; timeouts are free. |
Use it in Claude
Once LurkAPI is connected to Claude, this endpoint is the youtube_comments tool. Ask in plain English, for example:
Use LurkAPI's youtube_comments with url "https://www.youtube.com/watch?v=dQw4w9WgXcQ" and summarize what you find.Claude calls youtube_comments with arguments like these, and each call costs 1 credit:
{
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
}Tool results skip nulls and empty lists to save tokens.