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

Google Photos API认证权限不足问题求助及官方支持咨询

调用Google Photos API遇403权限错误及官方客服联系方法

问题描述

调用Google Photos API获取媒体资源时触发403权限错误,具体日志如下:

Fetching first 10 media items...
Traceback (most recent call last):
  File "gphotos_connectfetchtest.py", line 43, in <module>
    list_media_items()
  File "gphotos_connectfetchtest.py", line 31, in list_media_items
    results = service.mediaItems().list(pageSize=10).execute()
  File "D:\Program Files\Python\Python38\lib\site-packages\googleapiclient\_helpers.py", line 130, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "D:\Program Files\Python\Python38\lib\site-packages\googleapiclient\http.py", line 938, in execute
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://photoslibrary.googleapis.com/v1/mediaItems?pageSize=10&alt=json returned "Request had insufficient authentication scopes.". Details: "Request had insufficient authentication scopes.">

已多次尝试常规排查步骤(如确认权限范围、重新生成Token等)但未解决,怀疑账号存在特殊限制设置,需联系谷歌官方客服获取针对性帮助。

补充信息

使用的代码

import os
import json
import csv
from datetime import datetime
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

SCOPES = ['https://www.googleapis.com/auth/photoslibrary.readonly']
CLIENT_SECRET_FILE = 'credentials.json'
TOKEN_FILE = 'token.json'

def authenticate():
    creds = None
    # Load token if it exists
    if os.path.exists(TOKEN_FILE):
        creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES)
    # If no valid credentials available, let user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
            creds = flow.run_local_server(port=8080)
        # Save credentials for next run
        with open(TOKEN_FILE, 'w') as token:
            token.write(creds.to_json())
    return creds

def estimate_size(media_type, megapixels):
    try:
        if media_type == "Photo" and megapixels:
            return round(megapixels * 0.3, 2)  # MB estimate
        else:
            return ""
    except:
        return ""

def get_photos():
    creds = authenticate()
    service = build('photoslibrary', 'v1', credentials=creds, static_discovery=False)

    media_items = []
    next_page_token = None

    print("Fetching media items...")

    while True:
        results = service.mediaItems().list(pageSize=100, pageToken=next_page_token).execute()
        items = results.get('mediaItems', [])
        media_items.extend(items)
        next_page_token = results.get('nextPageToken')
        if not next_page_token:
            break

    timestamp = datetime.now().strftime('%d.%m.%Y_%H%M')
    output_file = f'google_photos_{timestamp}.csv'

    with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
        fieldnames = ['Name', 'Date Taken', 'Type', 'Extension', 'Resolution', 'Megapixels', 'Est. File Size (MB)', 'Link', 'mediaItemId']
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter='\t')
        writer.writeheader()

        for item in media_items:
            metadata = item.get('mediaMetadata', {})
            mime_type = item.get('mimeType', '')
            media_type = 'Video' if mime_type.startswith('video') else 'Photo'
            width = metadata.get('width')
            height = metadata.get('height')

            resolution = f"{width} x {height}" if width and height else ''
            megapixels = round((int(width) * int(height)) / 1_000_000, 2) if width and height else ''
            estimated_size = estimate_size(media_type, megapixels)
            filename = item.get('filename', '')
            extension = os.path.splitext(filename)[1].lower().lstrip('.')

            writer.writerow({
                'Name': filename,
                'Date Taken': metadata.get('creationTime', ''),
                'Type': media_type,
                'Extension': extension,
                'Resolution': resolution,
                'Megapixels': f"{megapixels} MP" if megapixels else '',
                'File Size': get_file_size(base_url),
                'Device Model': device_model,
                'Link': base_url + '=d',
                'mediaItemId': item.get('id'),
            })

    print(f"Saved {len(media_items)} media items to '{output_file}'.")

if __name__ == '__main__':
    get_photos()

生成的Token信息

{"token": "tokenTOKENtokenToKeNTOKENtokentokenTOKENtokenToKeNTOKENtokentokenTOKENtokenToKeNTOKENtokentokenTOKENtokenToKeNTOKENtokentokenTOKENtokenToKeNTOKEN_tokentokenTOKEN_tokenToKeNTOKENtokentokenTOKENtokenToKeNTOKENtokentokenTOKENtoken",
"refresh_token": "1//0refresh_tokenrefresh_tokenAkSNwF-refresh_token-refresh_tokenrefresh_tokenrefresh_tokenrefresh_token",
"token_uri": "https://oauth2.googleapis.com/token",
"client_id": "client_id-CLIENT_IDclient.idClIeNt_Id.apps.googleusercontent.com",
"client_secret": "SECRET-SECRETsecretSECRET-SeCrEtsecret-SECRET",
"scopes": ["https://www.googleapis.com/auth/photoslibrary.readonly"],
"universe_domain": "googleapis.com",
"account": "",
"expiry": "3485-06-25T00:39:18.310504Z"}

排查与客服联系方法

最后排查步骤(若未尝试)

  1. 删除旧Token重新授权:删除本地token.json文件,重新运行代码完成授权流程,确保新Token的权限范围与代码中定义的一致。
  2. 确认Cloud项目API启用状态:登录Google Cloud控制台,进入对应项目,搜索并确认「Photos Library API」已启用。
  3. 检查账号权限限制:若使用Google Workspace账号,联系管理员确认是否存在API调用的全局限制。

联系谷歌官方客服的途径

  • Google Workspace账号:直接联系企业管理员,管理员可通过Workspace管理控制台提交专属支持工单,获取官方技术支持。
  • 个人账号:
    1. 登录Google Cloud控制台,进入对应项目,点击右上角问号图标,选择「提交支持请求」。若未开通付费支持计划,可先选择社区支持,或申请临时付费支持以获取官方工单通道。
    2. 在提交工单时,需提供完整的错误日志、代码片段、Token权限截图及账号相关信息,便于官方定位问题。
    3. 也可通过Google账号帮助中心,搜索「Google Photos API 权限不足」,找到对应分类的反馈入口提交问题。

内容的提问来源于stack exchange,提问作者Bogdan Nicolescu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 20:28:13