Private/Invoke-SpotifyRequest.ps1

function Invoke-SpotifyRequest {
    param (
        [Parameter(Mandatory=$true)]
        [string] $Uri,

        [Parameter(Mandatory=$true)]
        [hashtable] $Headers,

        [Parameter(Mandatory=$false)]
        [string] $Method = 'GET',

        [Parameter(Mandatory=$false)]
        [string] $ContentType,

        [Parameter(Mandatory=$false)]
        [string] $Body
    )

    $iwrParams = @{ Uri = $Uri; Headers = $Headers; Method = $Method }
    if ($PSBoundParameters.ContainsKey('ContentType')) { $iwrParams.ContentType = $ContentType }
    if ($PSBoundParameters.ContainsKey('Body'))        { $iwrParams.Body = $Body }

    for ($attempt = 1; $attempt -le 3; $attempt++) {
        try {
            return Invoke-WebRequest @iwrParams
        }
        catch {
            $status = $_.Exception.Response.StatusCode.value__
            if ($status -eq 429 -and $attempt -lt 3) {
                $retryAfter = $_.Exception.Response.Headers['Retry-After']
                $waitSecs   = if ($retryAfter) { [int]$retryAfter + 1 } else { 5 }
                Write-Warning "Spotify rate limit hit — waiting ${waitSecs}s (attempt $attempt/3)"
                [System.Threading.Thread]::Sleep($waitSecs * 1000)
                continue
            }
            throw
        }
    }
}