Java调用YouTube API遇403 Forbidden错误(accountDelegationForbidden)求助
问题:检测YouTube频道直播时遭遇403权限错误
我正在开发Java应用,用来查看朋友YouTube频道的最新视频和直播。获取普通视频没问题,但检测直播时失败了。试过用YouTube.LiveBroadcasts.List传入频道ID,也试过YouTube.Search.List接口,都不行。
错误信息
Exception in thread "Timer-0" java.lang.RuntimeException: com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden { "code" : 403, "errors" : [ { "domain" : "youtube.common", "message" : "The authenticated user cannot act on behalf of the specified Google account.", "reason" : "accountDelegationForbidden" } ], "message" : "The authenticated user cannot act on behalf of the specified Google account." } at pl.kimor.smoothdcbot.Main$1.lambda$run$0(Main.java:37) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at pl.kimor.smoothdcbot.Main$1.run(Main.java:33) at java.base/java.util.TimerThread.mainLoop(Timer.java:566) at java.base/java.util.TimerThread.run(Timer.java:516) Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden { "code" : 403, "errors" : [ { "domain" : "youtube.common", "message" : "The authenticated user cannot act on behalf of the specified Google account.", "reason" : "accountDelegationForbidden" } ], "message" : "The authenticated user cannot act on behalf of the specified Google account." }
另一份错误栈:
Exception in thread "Timer-0" java.lang.RuntimeException: com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden { "code" : 403, "errors" : [ { "domain" : "youtube.common", "message" : "The authenticated user cannot act on behalf of the specified Google account.", "reason" : "accountDelegationForbidden" } ], "message" : "The authenticated user cannot act on behalf of the specified Google account." } at pl.kimor.smoothdcbot.Main$1.lambda$run$0(Main.java:37) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at pl.kimor.smoothdcbot.Main$1.run(Main.java:33) at java.base/java.util.TimerThread.mainLoop(Timer.java:566) at java.base/java.util.TimerThread.run(Timer.java:516) Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden { "code" : 403, "errors" : [ { "domain" : "youtube.common", "message" : "The authenticated user cannot act on behalf of the specified Google account.", "reason" : "accountDelegationForbidden" } ], "message" : "The authenticated user cannot act on behalf of the specified Google account." } at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146) at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113) at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40) at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321) at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1067) at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419) at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352) at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469) at pl.kimor.smoothdcbot.youtube.YouTubeChannel.checkForNewLivestreams(YouTubeChannel.java:60) at pl.kimor.smoothdcbot.youtube.YouTubeChannel.checkForNewContents(YouTubeChannel.java:36) at pl.kimor.smoothdcbot.Main$1.lambda$run$0(Main.java:35) ... 4 more
相关代码
YouTubeChannel.java
package pl.kimor.smoothdcbot.youtube; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.services.youtube.YouTube; import com.google.api.services.youtube.model.*; import pl.kimor.smoothdcbot.discord.announcer.StreamAnnouncer; import pl.kimor.smoothdcbot.discord.announcer.VideoAnnouncer; import java.awt.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; import static pl.kimor.smoothdcbot.youtube.YouTubeApi.API_KEY; public class YouTubeChannel { private String name; private ThumbnailDetails channelThumbnails; private String id; private String uploadsPlaylistId; private List<YouTubeLivestream> liveBroadcasts = new ArrayList<>(); public ThumbnailDetails getProfileImageThumbnail() { return channelThumbnails; } public String getName() { return name; } public YouTubeChannel(String name, String id) throws IOException { this.name=name; this.id=id; loadChannelData(); } public void checkForNewContents() throws IOException { checkForNewLivestreams(); } protected void loadChannelData() throws IOException { YouTube.Channels.List channelRequest = YouTubeApi.getYtInstance().channels() .list("snippet,contentDetails") .setId(id) .setKey(API_KEY); ChannelListResponse channelResponse = channelRequest.execute(); this.channelThumbnails = channelResponse.getItems().get(0).getSnippet().getThumbnails(); this.uploadsPlaylistId= channelResponse.getItems() .get(0) .getContentDetails() .getRelatedPlaylists() .getUploads(); } public void checkForNewLivestreams() throws IOException { YouTube.Search.List livestreamsRequest = YouTubeApi.getYtInstance() .search() .list("snippet") .setKey(API_KEY) .setChannelId(id) .setEventType("live") .setType("video") .setMaxResults(1L); SearchListResponse liveBroadcastListResponse = livestreamsRequest.execute(); if (liveBroadcastListResponse.getItems() == null || liveBroadcastListResponse.getItems().isEmpty()){ System.out.println("No livestreams found!"); } else { System.out.println("Livestreams count: " + liveBroadcastListResponse.getItems().size()); liveBroadcastListResponse.getItems().forEach(item -> { System.out.println(item.getId().getVideoId()); }); } List<YouTubeLivestream> livestreams = new ArrayList<>(); for (SearchResult searchResult : liveBroadcastListResponse.getItems()){ if (searchResult.getSnippet().getLiveBroadcastContent() == null){ continue; } VideoLiveStreamingDetails details = YouTubeApi.findVideoById(searchResult.getId().getVideoId()).getLiveStreamingDetails(); livestreams.add(new YouTubeLivestream( searchResult.getId().getVideoId(), this, details.getScheduledStartTime() == null ? YouTubeVideoType.LIVE_STREAM : YouTubeVideoType.INCOMING_STREAM, searchResult.getSnippet().getTitle(), searchResult.getSnippet().getDescription(), searchResult.getSnippet().getThumbnails(), details.getScheduledStartTime() )); } if (liveBroadcasts.isEmpty()){ liveBroadcasts.addAll(livestreams); } if(livestreams.isEmpty()) return; if (livestreams.get(0).getTransmissionScheduleDate().getValue()>liveBroadcasts.get(0).getTransmissionScheduleDate().getValue()){ liveBroadcasts.clear(); liveBroadcasts.addAll(livestreams); StreamAnnouncer.announceNewLivestream(liveBroadcasts.get(0)); } } }
YouTubeApi.java
package pl.kimor.smoothdcbot.youtube; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.services.youtube.YouTube; import com.google.api.services.youtube.model.Video; import java.io.IOException; import java.security.GeneralSecurityException; public class YouTubeApi { public static String API_KEY = "api key is here"; private static YouTube ytInstance; public static YouTube getYtInstance(){ return ytInstance; } public static void initialize() throws GeneralSecurityException, IOException { ytInstance=new YouTube.Builder( GoogleNetHttpTransport.newTrustedTransport(), JacksonFactory.getDefaultInstance(), null ).setApplicationName("app-name").build(); } public static Video findVideoById(String id) throws IOException { YouTube.Videos.List videoList = ytInstance.videos() .list("liveStreamingDetails") .setId(id) .setKey(API_KEY) .setMaxResults(1L); if (videoList.execute().getItems().isEmpty()){ return null; } return videoList.execute().getItems().get(0); } }
解决方案
1. 检查API密钥的权限配置
当前错误提示accountDelegationForbidden,说明API密钥的权限配置有问题:
- 登录Google Cloud控制台,找到对应项目,确保YouTube Data API v3已启用。
- 检查API密钥的限制规则:如果设置了IP限制,确认服务器IP在允许列表内;如果设置了API限制,确保包含YouTube Data API v3。
2. 切换到OAuth2认证(若需访问受限内容)
部分直播接口需要用户级OAuth2认证,而非单纯的API密钥,比如访问私有频道直播:
- 在Google Cloud控制台创建OAuth2客户端ID,下载
client_secrets.json文件。 - 修改
YouTubeApi.initialize()方法,添加OAuth2凭证:
public static void initialize() throws GeneralSecurityException, IOException { GoogleClientSecrets clientSecrets = GoogleClientSecrets.load( JacksonFactory.getDefaultInstance(), new FileReader("client_secrets.json") ); GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( GoogleNetHttpTransport.newTrustedTransport(), JacksonFactory.getDefaultInstance(), clientSecrets, Collections.singletonList(YouTubeScopes.YOUTUBE_READONLY) ).build(); Credential credential = new AuthorizationCodeInstalledApp( flow, new LocalServerReceiver() ).authorize("user"); ytInstance=new YouTube.Builder( GoogleNetHttpTransport.newTrustedTransport(), JacksonFactory.getDefaultInstance(), credential ).setApplicationName("app-name").build(); }
3. 修正Search.List接口参数
确保调用参数正确:
eventType可同时设置为live,upcoming,覆盖当前直播和即将到来的直播。- 确认
channelId是正确的频道ID(可从频道页面URL获取,不是用户名)。
4. 检查频道隐私设置
如果朋友的频道是私有/未公开状态,需要频道所有者授权你的账号访问其内容;若为公开频道,可跳过此步骤。
内容的提问来源于stack exchange,提问作者kimor
相关产品推荐
相关产品推荐

