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

使用Spotipy调用Spotify API反复出现HTTP 429错误求助

Spotify API速率限制问题排查与代码修复建议

问题概述

  • 18小时前因请求量过大触发Spotify API速率限制,原代码执行到API调用时无限挂起
  • 12小时前重写代码,设置retries = 0以抛出错误而非挂起,同时添加请求间隔控制,但重写后仅能成功发起1次API请求,之后每次均返回HTTP 429错误,间隔数小时重试仍无法解决
  • 已确保所有请求间隔至少1秒,收到429错误时采用指数递增时间重试(最长等待2分钟),尝试更换开发者账号下两个应用的Client ID/Secret均无效
  • 疑问:当前处理方式是否正确?代码存在哪些问题?如何获取429响应头的Retry-After字段?该字段能否解决问题?

代码问题分析

1. 请求时间更新逻辑缺失

成功发起API请求后,未更新self.recent_request为当前时间,导致后续请求的间隔计算完全错误,可能短时间内连续发起请求触发速率限制。

2. 递归分支无返回值

当请求间隔不足1秒时,递归调用get_playlist_tracks但未返回结果,会导致上层调用拿到None,进而引发后续代码报错,同时也会导致请求时间无法正确更新。

3. Retry-After字段获取方式错误

Spotipy的SpotifyException中,响应头信息存储在e.response.headers而非e.headers中,直接访问e.headers无法获取到Retry-After字段。

4. 重试逻辑未结合请求间隔

重试时直接发起请求,未检查当前时间与上一次请求的间隔,可能刚完成等待就再次触发速率限制。

5. 未更新offset导致无限重复请求

原代码中循环获取 playlist 数据时,未递增offset值,会无限请求同一页数据,进一步加剧速率限制问题。

修复建议与解决方案

1. 修复请求间隔与时间更新逻辑

确保每次发起请求前检查间隔,且成功请求后更新请求时间,避免短时间内连续请求。

2. 正确获取Retry-After字段

使用e.response.headers.get('Retry-After')获取官方建议的等待时间,优先遵循该时间而非自定义的指数退避时间,这能更精准地匹配Spotify的限制规则。

3. 完善递归重试的返回逻辑

所有递归分支必须返回结果,避免出现None值导致后续流程中断。

4. 排查账号/应用状态

若间隔数小时仍返回429,可能是Spotify对账号或应用进行了临时的严格限制,需检查开发者控制台的应用状态,确认是否有额外限制通知。

修复后的代码示例

import spotipy # type: ignore
from spotipy.oauth2 import SpotifyOAuth # type: ignore
import time

def create_client() -> spotipy.Spotify:
    sp: spotipy.Spotify = spotipy.Spotify(
        retries=0,
        auth_manager=SpotifyOAuth(
            client_id='my_id',
            client_secret='_my_secret',
            scope='playlist-read-private playlist-modify-public playlist-modify-private',
            redirect_uri='http://localhost:8888/callback'
        )
    )
    return sp

class APIRequests:
    def __init__(self, user_id: str) -> None:
        self.recent_request: float = time.time()
        self.sp: spotipy.Spotify = create_client()
        self.user_id: str = user_id

    def get_playlist_tracks(self, playlist: str, offset: int, retries: int=0):
        # 确保两次请求间隔至少1秒
        delta_time = time.time() - self.recent_request
        if delta_time < 1:
            time.sleep(1 - delta_time)
        
        try:
            # 更新请求时间为当前发起请求的时间
            self.recent_request = time.time()
            response = self.sp.user_playlist_tracks(self.user_id, playlist, offset=offset, limit=100)
            return response
        except spotipy.exceptions.SpotifyException as e:
            if e.http_status == 429:
                # 正确获取Retry-After字段
                retry_after = e.response.headers.get('Retry-After')
                # 优先使用官方建议的等待时间,否则用指数退避
                wait_time = int(retry_after) if retry_after else (2 ** retries) * 4
                print(f"收到HTTP 429错误,{'官方建议等待'+retry_after+'秒' if retry_after else f'等待{wait_time}秒'}")
                time.sleep(wait_time)
                retries += 1
                return self.get_playlist_tracks(playlist, offset, retries)
            else:
                raise e

api_client: APIRequests = APIRequests('my_user_id')

def get_playlist_tracks(playlist: str) -> list:
    offset: int = 0
    tracks: list = []
    while True:
        print('发送请求...')
        request_batch = api_client.get_playlist_tracks(playlist, offset)
        print('请求完成')
        for track in request_batch['items']:
            tracks.append(track['track'])
        
        if not request_batch["next"]:
                break
        offset += 100  # 递增offset获取下一页数据
    
    return tracks

if __name__ == '__main__':
    tracks = get_playlist_tracks('my_playlist')
    print(len(tracks))

关于Retry-After字段的作用

Spotify返回的Retry-After字段会明确告知你需要等待的秒数,遵循该时间等待能确保你在合规的时间点重新发起请求,比自定义的指数退避更精准,能有效避免重复触发429错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 01:11:11