Python · YouTube
Monitor YouTube video stats with Python
YouTube shows a video's current view count, but not how it got there. If you want to know how fast a video grows – your own, a competitor's or a campaign you paid for – you need to write the numbers down regularly. This guide builds a small script that does that for you.
Once a day it asks everydata.io for the current views, likes and comments of each video on your list, adds a line to a CSV file and prints how many views each video gained since the last run. Optionally it also picks up the newest uploads of a channel, so new videos are tracked from their first day.
Updated · by everydata.io
What you will have at the end
- A CSV file with one row per video and day: views, likes, comments.
- The view growth since the previous check, printed on every run.
- Automatic tracking of a channel's new uploads.
What you need
- Python 3.10 or newer and
pip install requests. - A free everydata.io API key – create one here – in the
EVERYDATA_API_KEYenvironment variable. - The videos you want to watch: the ID after
v=in the link, or the full link.
01See what the API returns for one video
The video-details endpoint takes a video ID or link in id and returns the public numbers of the video page. Shortened documented example:
{
"responseStatus": "PAGE_FOUND",
"responseMessage": "Page successfully found!",
"videoId": "kJQP7kiw5Fk",
"title": "Luis Fonsi - Despacito ft. Daddy Yankee",
"channelName": "LuisFonsiVEVO",
"viewCount": 9125159593,
"likeCount": 56673581,
"likeCountText": "56M",
"commentCount": 4300000,
"publishedAt": "2017-01-12T21:00:02-08:00",
"duration": 282
}viewCount, likeCount and commentCount are plain numbers, ready to store and compare. likeCountText is the rounded label YouTube shows on the page.
02Record the numbers on every run
The script reads the last view count of each video from the CSV file, fetches the current numbers, appends a new row and prints the difference.
"""Record views, likes and comments of YouTube videos and print the growth since the last check."""
import csv
import os
import re
import time
from datetime import datetime, timezone
from pathlib import Path
import requests
API = "https://api.everydata.io"
KEY = os.environ["EVERYDATA_API_KEY"]
CSV_FILE = Path("video_stats.csv")
VIDEOS = ["kJQP7kiw5Fk"] # video IDs or full YouTube links
def api_get(path: str, params: dict) -> dict:
"""GET an everydata.io endpoint. Waits when rate limited (429) and retries a failed page load (5xx) once."""
retried = False
for _ in range(4):
resp = requests.get(f"{API}{path}", params=params, headers={"x-api-key": KEY}, timeout=60)
if resp.status_code == 429:
# Too many requests at once. The answer says how long to wait: "... Try again in 12 seconds."
wait = re.search(r"in (\d+) seconds", resp.text)
time.sleep(int(wait.group(1)) + 1 if wait else 60)
continue
if resp.status_code >= 500 and not retried:
retried = True # 5xx answers are not counted against your quota
time.sleep(10)
continue
resp.raise_for_status() # 4xx (wrong key, quota used up, bad parameter) or a second 5xx
return resp.json()
raise RuntimeError("still rate limited after several waits – try again later")
def video_details(video: str) -> dict:
return api_get("/ytb/youtube-video-details", {"id": video})
def last_views() -> dict[str, int]:
"""Most recent view count per video from earlier runs."""
if not CSV_FILE.exists():
return {}
with CSV_FILE.open(newline="") as f:
return {r["video_id"]: int(r["views"]) for r in csv.DictReader(f) if r["views"]}
def main() -> None:
before = last_views()
now = datetime.now(timezone.utc).isoformat(timespec="seconds")
new_file = not CSV_FILE.exists()
with CSV_FILE.open("a", newline="") as f:
writer = csv.writer(f)
if new_file:
writer.writerow(["checked_at", "video_id", "title", "views", "likes", "comments"])
for video in VIDEOS:
try:
v = video_details(video)
except (requests.RequestException, RuntimeError) as exc:
print(f"{video}: could not check this time ({exc}) – the next run tries again")
continue # one failed video does not stop the others
video_id = v.get("videoId") or video
views = v.get("viewCount")
writer.writerow([now, video_id, v.get("title"), views or "",
v.get("likeCount") or "", v.get("commentCount") or ""])
if views is not None and video_id in before:
print(f"{v.get('title')}: +{views - before[video_id]:,} views since the last check")
if __name__ == "__main__":
main()Run it with python3 monitor.py. From the second run on, you see lines like “+12,345 views since the last check”. api_get waits and continues when you send too many requests at once (429) and tries a failed page load (5xx) once more; a video that still fails is reported and picked up again on the next run.
03Pick up a channel's new videos
Instead of adding every new video by hand, ask for a channel's latest uploads and include them in the list. The channel-videos endpoint accepts a channel ID or a channel link.
def latest_uploads(channel: str) -> list[str]:
"""IDs of the newest regular videos of a channel (channel ID or URL such as https://www.youtube.com/@WWE)."""
data = api_get("/ytb/youtube-channel-videos", {"id": channel, "filter": "videos_latest"})
return [item["videoId"] for item in data.get("items", []) if item.get("type") == "video"]
# In main(): watch the five newest uploads of a channel as well as your fixed list
# for video in VIDEOS + latest_uploads("https://www.youtube.com/@WWE")[:5]:{
"responseStatus": "PAGE_FOUND",
"responseMessage": "Page successfully found!",
"channelId": "UCJ5v_MCY6GNUBTO8-D3XoAg",
"filter": "videos_latest",
"items": [
{
"type": "video",
"videoId": "b7kl_pcHa0A",
"title": "Blake Monroe vows to get payback against Giulia: SmackDown exclusive, Sept. 18, 2026",
"viewCount": 6800,
"publishedTimeText": "47 minutes ago"
}
]
}04Run it once a day
Schedule the script with cron (Mac, Linux), the Windows Task Scheduler or a scheduled CI job:
0 6 * * * cd /home/you/yt-monitor && EVERYDATA_API_KEY=ds_your_key /usr/bin/python3 monitor.py >> monitor.log 2>&1Each video lookup and each channel lookup is one request. As an example, three videos checked once a day are about 90 requests a month, within the free plan of 100. For daily numbers on many videos, see pricing.
Open video_stats.csv in a spreadsheet and plot views over checked_at to see the growth curve.
Endpoints used in this guide
Video Details · YouTube API
Channel Videos · YouTube API
What else you can do with this data
YouTube metadata as JSON – title, views, likes, comments, duration, tags, thumbnails, captions – by video ID or URL. No OAuth, no Data API quota.
Public YouTube channel stats via REST: subscriber and video counts, verification, latest videos, streams, Shorts and playlists by channel ID or @handle.
Scrape YouTube comments by video ID or URL: text, author, likes, reply count, pinned and hearted flags, with cursor paging into replies. No OAuth.
Try it with your own data
The free plan includes 100 requests a month, usable on every platform – enough to follow this guide end to end.