Search code examples
pythonauthorizationspotify

Keep getting 403 Forbidden Error, when I try to create a playlist with Spotify API


I have tried everything that I could find on the internet, but I am unable to fix this error. My spotify_search and access_token works just fine. I do not understand why I am being given access to create a playlist with my code. Especially considering that create a playlist works when I use Spotify testing website for creating a playlist.

Here is my code (parts with Secret are obvious sensitive info hiding):

import json
import requests
from bs4 import BeautifulSoup

URL = 'https://www.billboard.com/charts/hot-100/1998-02-28/'

# Obtaining the Top 100 Songs, based on the Year submitted
response = requests.get(URL)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')

song_list = [song.getText().strip() for song in 
             soup.select(selector='li h3', class_='c-title')[:100]]
artist_list = [song.getText().strip() for song in 
               soup.select(selector='li ul li span.c-label.a-no-trucate')]

spotify_EP = 'https://api.spotify.com/v1/'

client_ID = '***SECRET***'

#Spotify Documentation - Phase 0: Access Token
def access_token(): 
    token_URL = 'https://accounts.spotify.com/api/token'
    token_secret = '***SECRET***'
    
    
    
    header = {
        'Content-Type': 'application/x-www-form-urlencoded',
    }
    
    params = {
        'grant_type': 'client_credentials',
        'client_id': client_ID,
        'client_secret': token_secret
   }
    response = requests.post(token_URL, params=params, headers=header)
    response.raise_for_status()
    
    return response.json()['access_token']
    
access_token = access_token()

#Spotify Documentation - Phase 1: SEARCH for the Spotify Track and grab their urls
def spotify_search(access_token): 
    print("Searching for Songs...")
    song_urls = []
    for song in range(100): 
        search_sEP = f'{spotify_EP}search'
        search_params = {
            'q': f'{song_list[song]} {artist_list[song]}',
            'type': 'track',
            'limit': 1,
            }
        
        header = {
            'Authorization': f'Bearer {access_token}' 
            }
        
        response = requests.get(search_sEP, params=search_params, headers=header)
        response.raise_for_status()
        song_urls.append(response.json()['tracks']['items'][0]['uri'])
    print('\nSongs Successfully Collected.')
    return song_urls
    
# top100_tracks = spotify_search(access_token) 


#Spotify Documentation - Phase 2: Creating the Spotify Playlist 
def create_playlist():
   
    playlist_sEP = f'{spotify_EP}users/shakuryonsteward/playlists'
    data = {
        'name': "Top 100 Tracks from Birthday",
        'description': "Generated API Playlist as part of Coding Project xP",
        'public': True,
        'grant_type': 'client_credentials'
        }
    header = {
        'Authorization': f'Bearer {access_token}',
        'Content-Type': 'application/json',

        }
    
    response = requests.post(playlist_sEP, data=data, headers=header)
    response.raise_for_status()
    print(response.json())
    

create_playlist()

EDIT: Here is the Full Error code:

Traceback (most recent call last):
  File "C:\Users\shaku\OneDrive\Docs\Spyder Projects\Spyder Projects\Spotify Playlist Top 100 Creator\main.py", line 90, in <module>
    create_playlist()
  File "C:\Users\shaku\OneDrive\Docs\Spyder Projects\Spyder Projects\Spotify Playlist Top 100 Creator\main.py", line 86, in create_playlist
    response.raise_for_status()
  File "C:\Users\shaku\AppData\Local\Programs\Python\Python311\Lib\site-packages\requests\models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://api.spotify.com/v1/users/shakuryonsteward/playlists

I have tried as listed in my code, scavenging through Spotify's dreadful API documentation for what is going on, but I am unable to find any solution to create a new Spotify playlist in my username.


Solution

  • This is my approach for adding songs to playlist from Billboard. I got 50 songs even if parsing 100 hot songs due to some song is not exist in Spotify and some queries not work well (rank 9 and rank 10) but overall the concept works.

    Overview

    enter image description here

    Error Reason

    403: Forbidden The client_credentials grant_type can search song but can't add(or create) playlist due to it is user private activity. So you can't create playlist by access token which get by "client_credentials"

    if you want to do that have to get an access token by "Authorization Code Flow" It needs your login step and redirect code to get access token.

    more detailed information in here

    It means you running local server by Flask (Django) OR using "spotipy" This is example of using Flask and here

    The spotipy can add songs into existing playlist. That is why I made a playlist by manually.

    Demo code

    import requests
    from bs4 import BeautifulSoup
    import json
    from urllib.parse import quote
    import spotipy
    from spotipy.oauth2 import SpotifyOAuth
    
    # Spotify authentication details
    SCOPE = ['playlist-modify-public', 'playlist-modify-private']
    USER_ID = '[your USER_ID]'                  # Copy from your Spotify Edit Profile
    REDIRECT_URI = '[your REDIRECT_URI]'        # Copy from your  Spotify for Developers's Dashboard
    CLIENT_ID = '[your CLIENT_ID]'              # Copy from your  Spotify for Developers's Dashboard
    CLIENT_SECRET = '[your CLIENT_SECRET]'      # https://developer.spotify.com/dashboard
    PLAYLIST_ID = '[your PLAYLIST_ID]'          # copy from Spotify UI after create new playlist
    
    auth_manager = SpotifyOAuth(
        scope=SCOPE,
        username=USER_ID,
        redirect_uri=REDIRECT_URI,
        client_id=CLIENT_ID,
        client_secret=CLIENT_SECRET)
    
    def add_songs_to_playlist(playlist_id, track_ids):
        try:
            sp = spotipy.Spotify(auth_manager=auth_manager)
            sp.playlist_add_items(playlist_id, track_ids)
            print("Songs added to the playlist successfully.")
        except Exception as e:
            print('Failed to add songs to the playlist: ' + str(e))
    
    def get_song_ids_from_json(file_path):
        try:
            with open(file_path, 'r') as file:
                data = json.load(file)
                track_ids = []
                for item in data:
                    # Check if 'items' list is not empty
                    if item['tracks']['items']:
                        track_id = item['tracks']['items'][0]['id']
                        track_ids.append(track_id)
                return track_ids
        except Exception as e:
            print('Failed to read from JSON file: ' + str(e))
            return []
    
    # Function to get Spotify access token
    def get_spotify_token(client_id, client_secret):
        auth_url = 'https://accounts.spotify.com/api/token'
        data = {
            'grant_type': 'client_credentials',
            'client_id': client_id,
            'client_secret': client_secret,
        }
        auth_response = requests.post(auth_url, data=data)
        access_token = auth_response.json().get('access_token')
        return access_token
    
    # Function to search Spotify for a track and artist, and remove 'available_markets'
    def search_spotify(access_token, rank, artist, track):
        headers = {"Authorization": "Bearer " + access_token}
        
        # # Encode the artist and track to handle special characters
        encoded_artist = quote(artist)
        encoded_track = quote(artist)
    
        query = f'artist:{encoded_artist} track:{encoded_track}'
        search_url = f'https://api.spotify.com/v1/search?q={query}&type=track&limit=1'
        result = requests.get(url=search_url, headers=headers).json()
    
        # Remove 'available_markets' from the result
        items = result.get('tracks', {}).get('items', [])
        if items:
            item = items[0]
            item.pop('available_markets', None)
            album = item.get('album', {})
            album.pop('available_markets', None)
    
        # Add rank to the result
        result['rank'] = rank
        return result
    
    def save_songs_to_json(spotify_results, file_path):
        filtered_results = []
    
        for result in spotify_results:
            # Check if 'tracks' and 'items' are in the result and 'items' is not empty
            if 'tracks' in result and 'items' in result['tracks'] and result['tracks']['items']:
                filtered_results.append(result)
    
        try:
            with open(file_path, 'w') as file:
                json.dump(filtered_results, file, indent=4)
                print(f"Results saved to {file_path}")
    
        except Exception as e:
            print(f'Failed to save results to JSON: {str(e)}')
    
    # Billboard Hot 100 scraping
    def get_billboard_songs(url):
        response = requests.get(URL)
        soup = BeautifulSoup(response.text, 'html.parser')
    
        # Prepare lists to hold songs and artists
        rank = 1
        rank_list = []
        song_list = []
        artist_list = []
        songs = []
    
        # Find all list items that contain song and artist information
        entries = soup.find_all('li', class_='o-chart-results-list__item')
    
        for entry in entries:
            song_title_element = entry.find('h3', class_='c-title')
            if song_title_element:
                song_title = song_title_element.get_text(strip=True)
    
                artist_name_element = song_title_element.find_next_sibling('span')
                if artist_name_element:
                    artist_name = artist_name_element.get_text(strip=True)
                    rank_list.append(rank)
                    song_list.append(song_title)
                    artist_list.append(artist_name)
                    songs.append({"rank" : rank, "song": song_title, "artist": artist_name})
                    rank = rank + 1
    
        # Save the Spotify search results to a JSON file
        file_path = 'billboard_songs.json'
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(songs, f, ensure_ascii=False, indent=4)
    
        return rank_list, artist_list, song_list
    
    # Search each song on Spotify
    def get_spotify_songs(rank_list, artist_list, song_list):
        spotify_results = []
        # Get Spotify access token
        spotify_token = get_spotify_token(CLIENT_ID, CLIENT_SECRET)
        for rank, artist, song in zip(rank_list, artist_list, song_list):
            result = search_spotify(spotify_token, rank, artist, song)
            spotify_results.append(result)
    
        return spotify_results
    
    # Step 1  Get billboard songs and save 'billboard_songs.json'
    URL = 'https://www.billboard.com/charts/hot-100/1998-02-28/'
    rank_list, artist_list, song_list = get_billboard_songs(URL)
    
    # Step 2  Get get_spotify songs
    spotify_results = get_spotify_songs(rank_list, artist_list, song_list)
    
    # Step 3  Get get_spotify songs
    save_songs_to_json(spotify_results, 'spotify_songs.json')
    
    # Step 4  Add songs to playlist
    song_ids = get_song_ids_from_json('spotify_songs.json')
    print(len(song_ids))
    add_songs_to_playlist(PLAYLIST_ID, song_ids)
    

    Saved 'billboard_songs.json'

    [
        {
            "rank": 1,
            "song": "My Heart Will Go On",
            "artist": "Celine Dion"
        },
        {
            "rank": 2,
            "song": "Nice & Slow",
            "artist": "Usher"
        },
        {
            "rank": 3,
            "song": "Gettin' Jiggy Wit It",
            "artist": "Will Smith"
        },
        {
            "rank": 4,
            "song": "Together Again",
            "artist": "Janet"
        },
        {
            "rank": 5,
            "song": "Truly Madly Deeply",
            "artist": "Savage Garden"
        },
        {
            "rank": 6,
            "song": "How Do I Live",
            "artist": "LeAnn Rimes"
        },
        {
            "rank": 7,
            "song": "I Don't Ever Want To See You Again",
            "artist": "Uncle Sam"
        },
        {
            "rank": 8,
            "song": "A Song For Mama",
            "artist": "Boyz II Men"
        },
        {
            "rank": 9,
            "song": "No, No, No",
            "artist": "Destiny's Child"
        },
        {
            "rank": 10,
            "song": "Been Around The World",
            "artist": "Puff Daddy & The Family (Feat. The Notorious B.I.G. & Mase)"
        },
        {
            "rank": 11,
            "song": "Too Much",
            "artist": "Spice Girls"
        },
        {
            "rank": 12,
            "song": "Swing My Way",
            "artist": "K.P. & Envyi"
        },
        {
            "rank": 13,
            "song": "What You Want",
            "artist": "Mase Featuring Total"
        },
        {
            "rank": 14,
            "song": "Gone Till November",
            "artist": "Wyclef Jean"
        },
        {
            "rank": 15,
            "song": "Deja Vu [Uptown Baby]",
            "artist": "Lord Tariq & Peter Gunz"
        },
        {
            "rank": 16,
            "song": "How's It Going To Be",
            "artist": "Third Eye Blind"
        },
        {
            "rank": 17,
            "song": "I Don't Want To Wait",
            "artist": "Paula Cole"
        },
        {
            "rank": 18,
            "song": "Kiss The Rain",
            "artist": "Billie Myers"
        },
        {
            "rank": 19,
            "song": "You Make Me Wanna...",
            "artist": "Usher"
        },
        {
            "rank": 20,
            "song": "Are You Jimmy Ray?",
            "artist": "Jimmy Ray"
        },
        {
            "rank": 21,
            "song": "Father",
            "artist": "LL Cool J"
        },
        {
            "rank": 22,
            "song": "Dangerous",
            "artist": "Busta Rhymes"
        },
        {
            "rank": 23,
            "song": "Candle In The Wind 1997/Something About The Way You Look Tonight",
            "artist": "Elton John"
        },
        {
            "rank": 24,
            "song": "Too Close",
            "artist": "Next"
        },
        {
            "rank": 25,
            "song": "Show Me Love",
            "artist": "Robyn"
        },
        {
            "rank": 26,
            "song": "Love You Down",
            "artist": "INOJ/LATHUN"
        },
        {
            "rank": 27,
            "song": "You're Still The One",
            "artist": "Shania Twain"
        },
        {
            "rank": 28,
            "song": "Tubthumping",
            "artist": "Chumbawamba"
        },
        {
            "rank": 29,
            "song": "Body Bumpin' Yippie-Yi-Yo",
            "artist": "Public Announcement"
        },
        {
            "rank": 30,
            "song": "Make Em' Say Uhh!",
            "artist": "Master P Feat. Fiend, Silkk The Shocker, Mia X & Mystikal"
        },
        {
            "rank": 31,
            "song": "If You Think I'm Jiggy",
            "artist": "The Lox"
        },
        {
            "rank": 32,
            "song": "All My Love",
            "artist": "Queen Pen Featuring Eric Williams"
        },
        {
            "rank": 33,
            "song": "We're Not Making Love No More",
            "artist": "Dru Hill"
        },
        {
            "rank": 34,
            "song": "I Do",
            "artist": "Lisa Loeb"
        },
        {
            "rank": 35,
            "song": "Am I Dreaming",
            "artist": "Ol Skool [Featuring Keith Sweat & Xscape]"
        },
        {
            "rank": 36,
            "song": "Pink",
            "artist": "Aerosmith"
        },
        {
            "rank": 37,
            "song": "My Body",
            "artist": "LSG"
        },
        {
            "rank": 38,
            "song": "Heaven",
            "artist": "Nu Flavor"
        },
        {
            "rank": 39,
            "song": "Sweet Surrender",
            "artist": "Sarah McLachlan"
        },
        {
            "rank": 40,
            "song": "Semi-Charmed Life",
            "artist": "Third Eye Blind"
        },
        {
            "rank": 41,
            "song": "Feel So Good",
            "artist": "Mase"
        },
        {
            "rank": 42,
            "song": "I Know Where It's At",
            "artist": "All Saints"
        },
        {
            "rank": 43,
            "song": "They Don't Know",
            "artist": "Jon B"
        },
        {
            "rank": 44,
            "song": "Quit Playing Games (With My Heart)",
            "artist": "Backstreet Boys"
        },
        {
            "rank": 45,
            "song": "All For You",
            "artist": "Sister Hazel"
        },
        {
            "rank": 46,
            "song": "What Would Happen",
            "artist": "Meredith Brooks"
        },
        {
            "rank": 47,
            "song": "I Will Come To You",
            "artist": "Hanson"
        },
        {
            "rank": 48,
            "song": "Light In Your Eyes",
            "artist": "Blessid Union Of Souls"
        },
        {
            "rank": 49,
            "song": "No Tengo Dinero",
            "artist": "Los Umbrellos"
        },
        {
            "rank": 50,
            "song": "Given To Fly",
            "artist": "Pearl Jam"
        },
        {
            "rank": 51,
            "song": "Spice Up Your Life",
            "artist": "Spice Girls"
        },
        {
            "rank": 52,
            "song": "Get At Me Dog",
            "artist": "DMX Featuring Sheek Of The Lox"
        },
        {
            "rank": 53,
            "song": "All I Do",
            "artist": "Somethin' For The People"
        },
        {
            "rank": 54,
            "song": "32 Flavors",
            "artist": "Alana Davis"
        },
        {
            "rank": 55,
            "song": "Romeo And Juliet",
            "artist": "Sylk-E. Fyne Featuring Chill"
        },
        {
            "rank": 56,
            "song": "Burn",
            "artist": "Militia"
        },
        {
            "rank": 57,
            "song": "Breaking All The Rules",
            "artist": "She Moves"
        },
        {
            "rank": 58,
            "song": "Going Back To Cali",
            "artist": "The Notorious B.I.G."
        },
        {
            "rank": 59,
            "song": "What If I Said",
            "artist": "Anita Cochran (Duet With Steve Wariner)"
        },
        {
            "rank": 60,
            "song": "At The Beginning",
            "artist": "Richard Marx & Donna Lewis"
        },
        {
            "rank": 61,
            "song": "Sock It 2 Me",
            "artist": "Missy \"Misdemeanor\" Elliott Featuring Da Brat"
        },
        {
            "rank": 62,
            "song": "Just Clownin'",
            "artist": "WC From Westside Connection"
        },
        {
            "rank": 63,
            "song": "Say You'll Stay",
            "artist": "KAI"
        },
        {
            "rank": 64,
            "song": "The Memory Remains",
            "artist": "Metallica"
        },
        {
            "rank": 65,
            "song": "If I Could Teach The World",
            "artist": "Bone Thugs-N-Harmony"
        },
        {
            "rank": 66,
            "song": "Strawberries",
            "artist": "Smooth"
        },
        {
            "rank": 67,
            "song": "One More Night",
            "artist": "Amber"
        },
        {
            "rank": 68,
            "song": "Don't Be Stupid (You Know I Love You)",
            "artist": "Shania Twain"
        },
        {
            "rank": 69,
            "song": "If I Never Stop Loving You",
            "artist": "David Kersh"
        },
        {
            "rank": 70,
            "song": "Nothin' Move But The Money",
            "artist": "Mic Geronimo Featuring DMX & Black Rob"
        },
        {
            "rank": 71,
            "song": "Life In Mono",
            "artist": "Mono"
        },
        {
            "rank": 72,
            "song": "Ain't That Just The Way",
            "artist": "Lutricia McNeal"
        },
        {
            "rank": 73,
            "song": "Lollipop (Candyman)",
            "artist": "Aqua"
        },
        {
            "rank": 74,
            "song": "I'm Not A Player",
            "artist": "Big Punisher"
        },
        {
            "rank": 75,
            "song": "Brian Wilson",
            "artist": "Barenaked Ladies"
        },
        {
            "rank": 76,
            "song": "Send My Love/Send One Your Love",
            "artist": "Born Jamericans"
        },
        {
            "rank": 77,
            "song": "Off The Hook",
            "artist": "Jody Watley"
        },
        {
            "rank": 78,
            "song": "So Help Me Girl",
            "artist": "Gary Barlow"
        },
        {
            "rank": 79,
            "song": "What If",
            "artist": "Reba McEntire"
        },
        {
            "rank": 80,
            "song": "Then What?",
            "artist": "Clay Walker"
        },
        {
            "rank": 81,
            "song": "I Can Love You Better",
            "artist": "Dixie Chicks"
        },
        {
            "rank": 82,
            "song": "Roxanne `97 - Puff Daddy Remix",
            "artist": "Sting & The Police"
        },
        {
            "rank": 83,
            "song": "4, 3, 2, 1",
            "artist": "LL Cool J Featuring Method Man, Redman, DMX, Canibus And Master P"
        },
        {
            "rank": 84,
            "song": "You're Not Alone",
            "artist": "Olive"
        },
        {
            "rank": 85,
            "song": "Love Of My Life",
            "artist": "Sammy Kershaw"
        },
        {
            "rank": 86,
            "song": "The Day That She Left Tulsa (In A Chevy)",
            "artist": "Wade Hayes"
        },
        {
            "rank": 87,
            "song": "Tuck Me In",
            "artist": "Kimberly Scott"
        },
        {
            "rank": 88,
            "song": "Young, Sad And Blue",
            "artist": "Lysette"
        },
        {
            "rank": 89,
            "song": "Something That We Do",
            "artist": "Clint Black"
        },
        {
            "rank": 90,
            "song": "The Note",
            "artist": "Daryle Singletary"
        },
        {
            "rank": 91,
            "song": "So Long (Well, Well, Well)",
            "artist": "Phajja"
        },
        {
            "rank": 92,
            "song": "I'm Afraid Of Americans",
            "artist": "David Bowie"
        },
        {
            "rank": 93,
            "song": "Tha Hop",
            "artist": "Kinsu"
        },
        {
            "rank": 94,
            "song": "The City Is Mine",
            "artist": "Jay-Z Featuring BLACKstreet"
        },
        {
            "rank": 95,
            "song": "You Know My Steez",
            "artist": "Gang Starr"
        },
        {
            "rank": 96,
            "song": "Tubthumping",
            "artist": "Chucklebutt"
        },
        {
            "rank": 97,
            "song": "So Good",
            "artist": "Davina"
        },
        {
            "rank": 98,
            "song": "Tic Tic Tac",
            "artist": "Fruit De La Passion"
        },
        {
            "rank": 99,
            "song": "In A Dream",
            "artist": "Rockell"
        },
        {
            "rank": 100,
            "song": "L-L-Lies",
            "artist": "Diana King"
        }
    ]
    

    Run result

    enter image description here

    enter image description here

    Search Example by Postman

    enter image description here

    Spotipy API for add song to playlist

    https://spotipy.readthedocs.io/en/2.16.1/?highlight=playlist_add_items#spotipy.client.Spotify.playlist_add_items