You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.06 18:07:00