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

YouTube下载应用调用youtubei/v1/search API出现403错误求助

问题

开发YouTube下载应用时,调用YouTube的youtubei/v1/search API获取视频数据,出现403错误:Liste.js:184 POST https://www.youtube.com/youtubei/v1/search?prettyPrint=false net::ERR_ABORTED 403。网站已上线,急需解决该问题。

相关前端代码:

import React, { useContext } from 'react'
import { Input, Grid, StatLabel, Stat, Image, StatHelpText, Button, useDisclosure, Spinner, } from '@chakra-ui/react'
import { useState, useEffect } from 'react'
import styles from './styles.module.css'
import DataTypes from './DataTypes'
import { useAllContext } from '../../context/AllContextes';
import axios from 'axios'

const Liste = () => {

    const message = {
        'tr': {
            share: "Indirmeyi paylaş",
            search: "Ara",
            videos: "tr"
        },
        'de': {
            share: "Download teilen",
            search: "Suchen",
            videos: "de"
        },
        'en': {
            share: "Share Download",
            search: "Search",
            videos: "en"
        },
        'fr':{
            share: "Partager Télécharger",
            search: "Chercher",
            videos: "fr"
        },
        'ar':{
            share: "مشاركة التنزيل",
            search: "يبحث",
            videos: "ar"
        }
    }

    const { lang, setLang } = useAllContext()

    const [data, setData] = useState(undefined);
    const [query, setQuery] = useState("");
    const [isLoading, setIsLoading] = useState(false)
    let nothing;

    const payload = {
        context: {
            client: {
                hl: lang,
                gl: "TR",
                remoteHost: "5.176.84.155",
                deviceMake: "Apple",
                deviceModel: "",
                visitorData: "CgtURlU1OFpEcm1uSSiF2YGWBg%3D%3D",
                userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36,gzip(gfe)",
                clientName:"WEB",
                clientVersion: "2.20220701.01.00",
                osName: "Macintosh",
                osVersion: "10_15_7",
                originalUrl: "https://www.youtube.com/results?search_query=halil+ibrahim+g%C3%B6ker",
                screenPixelDensity: 2,
                platform: "DESKTOP",
                clientFormFactor: "UNKNOWN_FORM_FACTOR",
                configInfo: {
                    appInstallData: "CIXZgZYGELfLrQUQuIuuBRDK7P0SEJje_RIQ1IOuBRDP8v0SEK6ergUQ_6muBRDV8f0SEJH4_BIQ2L6tBQ%3D%3D"
                },
                screenDensityFloat: 2,
                timeZone: "Europe/Istanbul",
                browserName: "Chrome",
                browserVersion: "103.0.0.0",
                screenWidthPoints: 1047,
                screenHeightPoints: 824,
                utcOffsetMinutes: 180,
                userInterfaceTheme: "USER_INTERFACE_THEME_LIGHT",
                memoryTotalKbytes: "8000000",
                mainAppWebInfo: {
                    graftUrl: "/results?search_query=halil+ibrahim+g%C3%B6ker",
                    pwaInstallabilityStatus: "PWA_INSTALLABILITY_STATUS_CAN_BE_INSTALLED",
                    webDisplayMode: "WEB_DISPLAY_MODE_BROWSER",
                    isWebNativeShareAvailable: false
                }
            },
            user: {
                lockedSafetyMode: false
            },
            request: {
                useSsl: true,
                internalExperimentFlags: [],
                consistencyTokenJars: []
            },
            clickTracking: {
                clickTrackingParams: "CB4Q7VAiEwieyd-rzNr4AhUJyxEIHdXVAzc="
            },
            adSignalsInfo: {
                params: [
                    {
                        key: "dt",
                        value: "1656777861522"
                    },
                    {
                        key: "flash",
                        value: "0"
                    },
                    {
                        key: "frm",
                        value: "0"
                    },
                    {
                        key: "u_tz",
                        value: "180"
                    },
                    {
                        key: "u_his",
                        value: "4"
                    },
                    {
                        key: "u_h",
                        value: "1050"
                    },
                    {
                        key: "u_w",
                        value: "1680"
                    },
                    {
                        key: "u_ah",
                        value: "940"
                    },
                    {
                        key: "u_aw",
                        value: "1680"
                    },
                    {
                        key: "u_cd",
                        value: "30"
                    },
                    {
                        key: "bc",
                        value: "31"
                    },
                    {
                        key: "bih",
                        value: "824"
                    },
                    {
                        key: "biw",
                        value: "1031"
                    },
                    {
                        key: "brdim",
                        value: "0,25,0,25,1680,25,1695,935,1047,824"
                    },
                    {
                        key: "vis",
                        value: "1"
                    },
                    {
                        key: "wgl",
                        value: "true"
                    },
                    {
                        key: "ca_type",
                        value: "image"
                    }
                ],
                bid: "ANyPxKqPl9hE_EFhqYFyXkFTsyGxNzPJ3zmZHYk5oPMRScurkXA0sUGIBXgLlNzSF64Eyyo1E87B"
            }
        },
        query: "halil ibrahim göker",
        webSearchboxStatsUrl: "/search?oq=halil ibrahim göker&gs_l=youtube.3..0i71k1l6.0.0.1.498002.0.0.0.0.0.0.0.0..0.0.uqaswc2,ytpso-bo-me=0,ytpsoso-bo-me=0,ytpso-rzp=0,ytpso-bo-bro-mi=24212243,ytpsoso-bo-bro-mi=24212243,cfro=1,ytpso-bo-me=1,ytpsoso-bo-me=1,ytpso-rzp=0,ytpso-bo-bro-mi=24237494,ytpsoso-bo-bro-mi=24237494...0...1ac..64.youtube..0.0.0....0.ndfBkFdQXUg"
    };

    const getSearchList = async (value) => {
        setIsLoading(true)
        setData(undefined);
        payload.query = value;
        const searchResponse = await fetch('https://www.youtube.com/youtubei/v1/search?prettyPrint=false',{
            method: "POST",
            mode:"no-cors",
            body: JSON.stringify(payload),
            headers: {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*',
            }
        });
        setIsLoading(false)
    };

    useEffect(() => { if (data) console.log(data.contents?.twoColumnSearchResultsRenderer?.primaryContents?.sectionListRenderer?.contents[0]?.itemSectionRenderer?.contents); }, [data]);

    return (
        <div className={styles.div}>
            <div className={styles.searchContainer}>
                <Input height="2.5rem" className={styles.input} variant='unstyled' onChange={(e) => { setQuery(e.target.value); }} placeholder={message[lang].search} value={query} pl="6px"/>
                <Button ml="6px" mr="5px" onClick={() => getSearchList(query)} bgGradient="linear(to-r, #f3360e, #fb4b18)" color="#fff" variant='solid'>{message[lang].search}</Button>
            </div>

            {isLoading ? <Spinner className={styles.spinner}
                thickness='10px'
                speed='0.65s'
                emptyColor='gray.200'
                color='#f3360e'
                width="2cm"
                height="2cm"
            />
                : nothing}

            <Grid className={styles.grid} templateColumns={{ sm: 'repeat(1, 5fr)', md: 'repeat(2, 5fr)', lg: 'repeat(2, 5fr)', xl: 'repeat(4, 5fr)', "2xl": 'repeat(4, 5fr)' }} gap={0}>
                {data && data.contents?.twoColumnSearchResultsRenderer?.primaryContents?.sectionListRenderer?.contents[0].itemSectionRenderer?.contents.map((item, index) => {
                    if (item.videoRenderer) return (
                        <Stat mt="20px" mb="20px" className={styles.stat}>
                            <Image src={(item.videoRenderer.thumbnail.thumbnails[0].url)} className={styles.image} onClick={() => window.open(`https://youtube.com${item.videoRenderer.navigationEndpoint.commandMetadata.webCommandMetadata.url}`, '_blank').focus()} />
                            <StatLabel >{(item.videoRenderer.title.runs[0].text)}</StatLabel>
                            <StatLabel fontFamily="candal" color="grey">{(item.videoRenderer.ownerText.runs[0].text)}</StatLabel>
                            <StatHelpText>{(item.videoRenderer.viewCountText.simpleText)}</StatHelpText>
                            <DataTypes data = {data} index ={index}/>
                            <Button className={styles.share} mt="5px" colorScheme='gray'>{message[lang].share}</Button>
                        </Stat>
                    )
                })}
            </Grid>
        </div >
    )
}

export default Liste

相关HTML依赖:

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

解决方案

错误原因分析

  1. 非公开API与跨域限制:youtubei/v1/search是YouTube内部使用的非公开接口,浏览器端直接调用会触发跨域规则,且YouTube会校验请求来源的合法性,拒绝非官方请求。
  2. no-cors模式无效:代码中设置mode:"no-cors"会导致浏览器屏蔽响应内容,同时这种请求模式不符合YouTube的校验逻辑,直接触发403。
  3. 硬编码参数失效:请求payload中的remoteHost、visitorData等参数为固定值,并非从合法会话中实时获取,YouTube会识别为伪造请求并拦截。

具体解决步骤

  1. 搭建后端代理接口
    在自有服务器上实现代理,由后端发起对YouTube API的请求,前端调用代理接口,绕过跨域限制。
    示例Node.js+Express代理:

    const express = require('express');
    const axios = require('axios');
    const app = express();
    app.use(express.json());
    
    app.post('/api/youtube-search', async (req, res) => {
      try {
        const response = await axios.post('https://www.youtube.com/youtubei/v1/search?prettyPrint=false', req.body, {
          headers: {
            'Content-Type': 'application/json',
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
          }
        });
        res.json(response.data);
      } catch (error) {
        res.status(error.response?.status || 500).send(error.message);
      }
    });
    
    app.listen(3001, () => console.log('Proxy server running on port 3001'));
    

    前端修改调用逻辑:

    const getSearchList = async (value) => {
      setIsLoading(true);
      setData(undefined);
      payload.query = value;
      try {
        const searchResponse = await fetch('/api/youtube-search', {
          method: "POST",
          body: JSON.stringify(payload),
          headers: {
            'Content-Type': 'application/json',
          }
        });
        const result = await searchResponse.json();
        setData(result);
      } catch (err) {
        console.error(err);
      }
      setIsLoading(false);
    };
    
  2. 切换到YouTube官方公开API
    使用YouTube Data API的search.list接口,这是官方支持的合法途径,需先在Google Cloud控制台申请API密钥。
    示例调用:

    const API_KEY = '你的Google API密钥';
    const getSearchList = async (value) => {
      setIsLoading(true);
      setData(undefined);
      try {
        const response = await fetch(`https://www.googleapis.com/youtube/v3/search?part=snippet&q=${encodeURIComponent(value)}&type=video&key=${API_KEY}`);
        const result = await response.json();
        setData(result);
      } catch (err) {
        console.error(err);
      }
      setIsLoading(false);
    };
    
  3. 动态提取合法参数(非公开API适配)
    若坚持使用youtubei/v1/search,需从当前YouTube页面会话中动态提取visitorData、clientVersion等参数,同时处理请求签名,但该方法不稳定,随时可能被YouTube封禁。

内容的提问来源于stack exchange,提问作者Salih Tekin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 04:48:29