As per docs:
contentDetails.contentRating.ytRating
A rating that YouTube uses to identify age-restricted content.
But that doesn't seem to work as documented, here's the example: https://www.youtube.com/watch?v=IUc0wyae-WI
API response:
{
"items": [
{
"id": "U9x_WdDwATA",
"contentDetails": {
"contentRating": {},
},
}
]
}
Notice that contentRating.ytRating isn't set which means that the video doesn't have age-restriction according to API.
But actually it's not the case: https://www.youtube.com/embed/IUc0wyae-WI?hl=en
This video is age-restricted and only available on YouTube. Learn more
Watch on YouTube
Where's my mistake? Or is it the bug in YouTube API v3?
try to find playabilityStatus:
see for more details:
Use the YouTube API to check if a video is embeddable
This is undocumented API existing for long time, so exploring it is up
to developer. I am aware of "status" (ok/fail), "errorcode" (100 and
150 in my practice), "reason" (string description of error). I am
getting duration ("length_seconds") this way because oEmbed does not
provide this information (strange, but true) and I can hardly motivate
every employer to get keys from youTube to use official API
I am aware of similar questions being asked before, but not this exact one, so please bear with me...
I want to reproduce a channel's uploaded videos list as they appear on YouTube's web page (broken into pages and sorted by publish date).
To do this, I am trying to get a list of VideoIDs from a YouTube channel that's sorted by publish date (by YouTube, not my code since there could be 1000's of videos in a playlist and YouTube limits to 50 results per query which can add up when I only want to show the user the first 25 entries).
Initially, I was using this YouTube Data API v3 Search query:
https://www.googleapis.com/youtube/v3/search?key=[APIKey]&channelId=[ChannelID]&part=snippet,id&order=date&type=video&maxResults=25
However, as some of the previous posts on stackoverflow mentioned (YouTube API v3 Search not returning all videos), this method does not guarantee to return all videos and indeed, some videos are missing from the result, making use of this query problematic.
I then saw this google video in some of the posts:
https://www.youtube.com/watch?v=RjUlmco7v2M
In the video, it is explained that you must first get the 'uploads' playlist for a channel (I'm also grabbing the channel's title and thumbnail in this query), which I do using:
https://www.googleapis.com/youtube/v3/channels?key=[APIKEY]&part=snippet,contentDetails&id=[ChannelID]
And once I have the 'uploads' playlist ID, I query:
https://www.googleapis.com/youtube/v3/playlistItems?key=[APIKey]&playlistId=[PlaylistID]&part=snippet,id&order=date&type=video&maxResults=25
However, the returned entries are not sorted by the publish date and according to the documentation (https://developers.google.com/youtube/v3/docs/playlistItems/list), there is no optional "order" parameter associated with this query.
With all these issues in mind, how do I get the first 25 entries of the 'uploads' playlist sorted by publish date without downloading the entire playlist so I can faithfully recreate how the YouTube website is listing videos.
After making some tests and thanks to this answer (and the next answers too) I was able to retrieve the information you need using the YouTube Data API v3 and here is how I made it:
First, in your question you're using the "search" API - since I don't know which criteria you're using in the search request, I omitted it for get direct to get the "upload" playlist id from a given channel_id.
Using the channel_id = UCT2rZIAL-zNqeK1OmLLUa6g (which belongs to "Microsoft HoloLens"), I use the "channels" API for retrieve the uploads playlist id.
Here is the URL request for retrieve the "upload" playlist id from the channel_id previously mentioned:
https://www.googleapis.com/youtube/v3/channels?part=id%2Csnippet%2CcontentDetails&fields=items(contentDetails%2FrelatedPlaylists%2Fuploads%2Csnippet%2Flocalized)&id=UCT2rZIAL-zNqeK1OmLLUa6g&key=<YOUR_API_KEY>
Explanation:
part: set the snippet and contentDetails parts for retrieve the following:
fields: from the snippet part: (localized, description and title) and from the contentDetails part: (relatedPlayLists and uploads).
id: channel_id used in this request.
Here are the results from this request:
{
"items": [
{
"snippet": {
"localized": {
"title": "Microsoft HoloLens",
"description": "The official YouTube channel of Microsoft HoloLens. Transform your world with holograms. Visit HoloLens.com for more info."
}
},
"contentDetails": {
"relatedPlaylists": {
"uploads": "UUT2rZIAL-zNqeK1OmLLUa6g"
}
}
}
]
}
Check the value of the uploads property in the
contentDetails section. This value will be used in the next API request.
You can also check these results in the Google API Explorer demo I prepared for make this request.
Once retrieved the uploads value (as specified in previous lines), now it's time to use the "playlistItems" API for build the following URL:
https://www.googleapis.com/youtube/v3/playlistItems?part=snippet%2CcontentDetails&playlistId=UUT2rZIAL-zNqeK1OmLLUa6g&fields=items(contentDetails(videoId%2CvideoPublishedAt)%2Csnippet%2Ftitle%2Cstatus)&maxResults=25&key=<YOUR_API_KEY>
Explanation:
part: set the snippet and contentDetails parts for retrieve the following:
fields: from the snippet part: (title and status) and from the contentDetails part: (videoId and videoPublishedAt).
playlistId: is the playlistId used in this request - (that is, the uploads value).
maxResults: set to 25.
Here are the results from this request:
{
"items": [
{
"snippet": {
"title": "Microsoft Windows Mixed Reality update | October 2018"
},
"contentDetails": {
"videoId": "00vnln25HBg",
"videoPublishedAt": "2019-01-04T17:43:47.000Z"
}
},
{
"snippet": {
"title": "How to use Spectator View for mobile devices"
},
"contentDetails": {
"videoId": "3fXlPw_FGLg",
"videoPublishedAt": "2018-10-15T17:13:42.000Z"
}
},
{
"snippet": {
"title": "Microsoft HoloLens: Visualizing the next mission to Mars."
},
"contentDetails": {
"videoId": "XVBbJ4EtAQY",
"videoPublishedAt": "2018-07-02T16:30:26.000Z"
}
},
{
"snippet": {
"title": "Microsoft HoloLens: Making mixed reality plug and play."
},
"contentDetails": {
"videoId": "QwXcSekZKWE",
"videoPublishedAt": "2018-06-25T23:25:55.000Z"
}
},
{
"snippet": {
"title": "Microsoft HoloLens | Windows Mixed Reality HMD Exerciser"
},
"contentDetails": {
"videoId": "RU3OMjq_Yic",
"videoPublishedAt": "2018-05-14T16:58:43.000Z"
}
}
]
}
I check the order of the items and they are in sorted by videoPublishedAt value (new to old).
You can also check these results in the try-it funcionality found in the YouTube Data API v3 - official documentation.1
1 For this case, I was unable to use the Google API Explorer (as I used in the first request) because I always got a "backend Error".
{
"error": {
"errors": [
{
"domain": "global",
"reason": "backendError",
"message": "Backend Error"
}
],
"code": 500,
"message": "Backend Error"
}
}
I think this is because the Google API explorer is outdated.
If anyone want to check it out, here is the demo.
I'm also using the playlist ID to get a list of videos, however mine do seem to be sorted by date from newest to oldest. Note that Youtube returns a page token that you can use to get the next 25 (or in my case 50) videos. I'm querying the API this way:
https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken= not_used_for_first_query&fields=nextPageToken,items(snippet(publishedAt,title,desc ription,thumbnails(default(url)),resourceId(videoId)))&playlistId=uploads_playlist_id&maxResults=50&order=date&key=your_api_key
This gets back in JSON response:
The video title
Publish date
Video description
Youtube URL for video
Video unique ID
Video thumbnail
You can see my working example at https://www.scriptbarrel.com
When you upload a video to YouTube you may get a Includes copyrighted content flag in the video manager. Then when you go and check the video you see a message under the "copyright" section:
You have x copyright claims on your video. and below this is a Details section displaying interesting information like:
"CONTENT","CLAIMANT","POLICY" and it shows the time region where this claimed content is located in the video. What i would like to know is if Youtube's or Google's API can give us this same information?
Try using Videos: list.
Send HTTP request using this format:
GET https://www.googleapis.com/youtube/v3/videos
If successful, this method returns a response body with this sample video resource:
"contentDetails": {
"licensedContent": boolean,
"regionRestriction": {
"allowed": [
string
],
"blocked": [
string
]
},
"status": {
"privacyStatus": string,
"publishAt": datetime,
"license": string,
"publicStatsViewable": boolean
}
I'm trying get the currently active live streams for list of channels. I know, that there is search/list method, but it costs 100 units of api quota and only applicable for one channel per request. It's impossible to update stream avaibility frequently for large number of channels with total quota of 1M api units per day.
So, is there any other way, i can get active streams for channel or list of channels, avoiding "heavy" search method?
You can directly use Live Streaming API - LiveStreams:list, LiveStreams:list returns a list of video streams that match the API request parameters.
HTTP request:
GET https://www.googleapis.com/youtube/v3/liveStreams
Note that in every request needs authorizing access to provate user data. You need to implement OAuth 2.0.
Include onBehalfOfContentOwner parameter, this parameter is intended for YouTube content partners that own and manage many different YouTube channels. It allows content owners to authenticate once and get access to all their video and channel data, without having to provide authentication credentials for each individual channel.
HTTP response:
{
"kind": "youtube#liveStreamListResponse",
"etag": etag,
"nextPageToken": string,
"prevPageToken": string,
"pageInfo": {
"totalResults": integer,
"resultsPerPage": integer
},
"items": [
liveStream Resource
]
}
Here's a sample code snippet how to request liveStream resources:
// This object is used to make YouTube Data API requests.
youtube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, credential)
.setApplicationName("youtube-cmdline-liststreams-sample")
.build();
// Create a request to list liveStream resources.
YouTube.LiveStreams.List livestreamRequest = youtube.liveStreams().list("id,snippet");
// Modify results to only return the user's streams.
livestreamRequest.setMine(true);
// Execute the API request and return the list of streams.
LiveStreamListResponse returnedListResponse = livestreamRequest.execute();
List<LiveStream> returnedList = returnedListResponse.getItems();
// Print information from the API response.
System.out.println("\n================== Returned Streams ==================\n");
for (LiveStream stream : returnedList) {
System.out.println(" - Id: " + stream.getId());
System.out.println(" - Title: " + stream.getSnippet().getTitle());
System.out.println(" - Description: " + stream.getSnippet().getDescription());
System.out.println(" - Published At: " + stream.getSnippet().getPublishedAt());
System.out.println("\n-------------------------------------------------------------\n");
}
I can't find any informations to check if a YouTube channel is actually streaming or not.
With Twitch you just need the channel name, and with the API you can check if there is a live or not.
I don't want to use OAuth, normally a public API key is enough. Like checking the videos of a channel I want to know if the channel is streaming.
You can do this by using search.list and specifying the channel ID, setting the type to video, and setting eventType to live.
For example, when I searched for:
https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=UCXswCcAMb5bvEUIDEzXFGYg&type=video&eventType=live&key=[API_KEY]
I got the following:
{
"kind": "youtube#searchListResponse",
"etag": "\"sGDdEsjSJ_SnACpEvVQ6MtTzkrI/gE5P_aKHWIIc6YSpRcOE57lf9oE\"",
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 5
},
"items": [
{
"kind": "youtube#searchResult",
"etag": "\"sGDdEsjSJ_SnACpEvVQ6MtTzkrI/H-6Tm7-JewZC0-CW4ALwOiq9wjs\"",
"id": {
"kind": "youtube#video",
"videoId": "W4HL6h-ZSws"
},
"snippet": {
"publishedAt": "2015-09-08T11:46:23.000Z",
"channelId": "UCXswCcAMb5bvEUIDEzXFGYg",
"title": "Borussia Dortmund vs St. Pauli 1-0 Live Stream",
"description": "Borussia Dortmund vs St. Pauli Live Stream Friendly Match.",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/W4HL6h-ZSws/default.jpg"
},
"medium": {
"url": "https://i.ytimg.com/vi/W4HL6h-ZSws/mqdefault.jpg"
},
"high": {
"url": "https://i.ytimg.com/vi/W4HL6h-ZSws/hqdefault.jpg"
}
},
"channelTitle": "",
"liveBroadcastContent": "live"
}
}
]
}
The search-method (https://www.googleapis.com/youtube/v3/search) is awfully expensive to use though. It costs 100 quota units (https://developers.google.com/youtube/v3/determine_quota_cost) out of the 10,000 you have by default.
This means you only get 100 requests per day which is terrible.
You could request an increase in the quota but that seems like brute forcing the the problem.
Is there really no other simpler method?
I know this is old, but I figured it out myself with PHP.
$API_KEY = 'your api3 key';
$ChannelID = 'the users channel id';
$channelInfo = 'https://www.googleapis.com/youtube/v3/search?part=snippet&channelId='.$ChannelID.'&type=video&eventType=live&key='.$API_KEY;
$extractInfo = file_get_contents($channelInfo);
$extractInfo = str_replace('},]',"}]",$extractInfo);
$showInfo = json_decode($extractInfo, true);
if($showInfo['pageInfo']['totalResults'] === 0){
echo 'Users channel is Offline';
} else {
echo 'Users channel is LIVE!';
}
Guys I found better way to do this. Yes, it requires you to make GET requests to a YouTube page and parse HTML, but it will work with newer versions + works with consent + works with captcha (most likely, 90%)
All you need to do is make a request to https://youtube.com/channel/[CHANNELID]/live and check the href attribute of the <link rel="canonical" /> tag.
For example,
<link rel="canonical" href="https://www.youtube.com/channel/UC4cueEAH9Oq94E1ynBiVJhw">
means there is no livestream, while
<link rel="canonical" href="https://www.youtube.com/watch?v=SR9w_ofpqkU">
means there is a stream, and you can even fetch its data by videoid.
Since canonical URL is very important for SEO and redirect does not work in GET or HEAD requests anymore, I recommend using my method.
Also here is the simple script I use:
import { parse } from 'node-html-parser'
import fetch from 'node-fetch'
const channelID = process.argv[2] // process.argv is array of arguments passed in console
const response = await fetch(`https://youtube.com/channel/${channelID}/live`)
const text = await response.text()
const html = parse(text)
const canonicalURLTag = html.querySelector('link[rel=canonical]')
const canonicalURL = canonicalURLTag.getAttribute('href')
const isStreaming = canonicalURL.includes('/watch?v=')
console.log(isStreaming)
Then run npm init -y && npm i node-html-parser node-fetch to create project in working directory and install dependencies
Then run node isStreaming.js UC4cueEAH9Oq94E1ynBiVJhw and it will print true/false (400-600 ms per one execution)
It does require you to depend on node-html-parser and node-fetch, but you can make requests with the built-in HTTP library (which sucks) and rewrite this to use regex. (Do not parse HTML with regex.)
I was also struggling with API limits. The most reliable and cheapest way I've found was simply a HEAD request to https://www.youtube.com/channel/CHANNEL_ID/live. If the channel is live then it will auto load the stream. If not then it will load the channels videos feed. You can simply check the Content-Length header size to determine which. If live the size is almost 2x when NOT live.
And depending on your region you might need to accept the cookies consent page. Just send your request with cookies={ "CONSENT": "YES+cb.20210420-15-p1.en-GB+FX+634" }.
if you point streamlink at a https://www.youtube.com/channel/CHANNEL_ID/live link, it will tell you if it is live or not
e.g. lofi beats is usually live,
$ streamlink "https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow/live"
[cli][info] Found matching plugin youtube for URL https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow/live
Available streams: 144p (worst), 240p, 360p, 480p, 720p, 1080p (best)
whereas MKBHD is not
$ streamlink "https://www.youtube.com/c/mkbhd/live"
[cli][info] Found matching plugin youtube for URL https://www.youtube.com/c/mkbhd/live
error: Could not find a video on this page
The easisest way that I have found to this has been scraping the site. This can be done by finding this:
<link rel="canonical" href="linkToActualYTLiveVideoPage">
as in Vitya's answer.
This is my simple Python code using bs4:
import requests
from bs4 import BeautifulSoup
def is_liveYT():
channel_url = "https://www.youtube.com/c/LofiGirl/live"
page = requests.get(channel_url, cookies={'CONSENT': 'YES+42'})
soup = BeautifulSoup(page.content, "html.parser")
live = soup.find("link", {"rel": "canonical"})
if live:
print("Streaming")
else:
print("Not Streaming")
if __name__ == "__main__":
is_liveYT()
It is pretty weird, honestly, that YouTube doesn't have a simple way to do this through the API, although this is probably easier.
I found the answer by #VityaSchel to be quite useful, but it doesn't distinguish between channels which have a live broadcast scheduled, and those which are broadcasting live now.
To distinguish between scheduled and live, I have extended his code to access the YouTube Data API to find the live streaming details:
import { parse } from 'node-html-parser'
import fetch from 'node-fetch'
const youtubeAPIkey = 'YOUR_YOUTUBE_API_KEY'
const youtubeURLbase = 'https://www.googleapis.com/youtube/v3/videos?key=' + youtubeAPIkey + '&part=liveStreamingDetails,snippet&id='
const c = {cid: process.argv[2]} // process.argv is array of arguments passed in console
const response = await fetch(`https://youtube.com/channel/${c.cid}/live`)
const text = await response.text()
const html = parse(text)
const canonicalURLTag = html.querySelector('link[rel=canonical]')
const canonicalURL = canonicalURLTag.getAttribute('href')
c.live = false
c.configured = canonicalURL.includes('/watch?v=')
if (!c.configured) process.exit()
c.vid = canonicalURL.match(/(?<==).*/)[0]
const data = await fetch(youtubeURLbase + c.vid).then(response => response.json())
if (data.error) {
console.error(data)
process.exit(1)
}
const i = data.items.pop() // pop() grabs the last item
c.title = i.snippet.title
c.thumbnail = i.snippet.thumbnails.standard.url
c.scheduledStartTime = i.liveStreamingDetails.scheduledStartTime
c.live = i.liveStreamingDetails.hasOwnProperty('actualStartTime')
if (c.live) {
c.actualStartTime = i.liveStreamingDetails.actualStartTime
}
console.log(c)
Sample output from the above:
% node index.js UCNlfGuzOAKM1sycPuM_QTHg
{
cid: 'UCNlfGuzOAKM1sycPuM_QTHg',
live: true,
configured: true,
vid: '8yRgYiNH39E',
title: '🔴 Deep Focus 24/7 - Ambient Music For Studying, Concentration, Work And Meditation',
thumbnail: 'https://i.ytimg.com/vi/8yRgYiNH39E/sddefault_live.jpg',
scheduledStartTime: '2022-05-23T01:25:00Z',
actualStartTime: '2022-05-23T01:30:22Z'
}
Every YouTube channel as a permanent livestream, even if the channel is currently not actively livestreaming. In the liveStream resource, you can find a boolean named isDefaultStream.
But where can we get this video (livestream) id? Go to https://www.youtube.com/user/CHANNEL_ID/live, right click on the stream and copy the video URL.
You can now make a GET request to
https://youtube.googleapis.com/youtube/v3/videos?part=liveStreamingDetails&id=[VIDEO_ID]&key=[API_KEY] (this request has a quota cost of 1 unit, see here)
This will be the result if the stream is currently active/online.
{
"kind": "",
"etag": "",
"items": [
{
"kind": "",
"etag": "",
"id": "",
"liveStreamingDetails": {
"actualStartTime": "",
"scheduledStartTime": "",
"concurrentViewers": "",
"activeLiveChatId": ""
}
}
],
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 1
}
}
If the stream is currently offline, the property concurrentViewers will not exist. In other words, the only difference between an online and offline livestream is that concurrentViewers is present or not present. With this information, you can check, if the channel is currently streaming or not (at least for his default stream).
I found youtube API to be very restrictive given the cost of search operation. Web scraping with aiohttp and beautifulsoup was not an option since the better indicators required javascript support. Hence I turned to selenium. I looked for the css selector
#info-text
and then search for the string Started streaming or with watching now in it.
You can run a small API on heroku with flask as well.