diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..31ad145 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.env +__pycache__/ +.ipynb_checkpoints/ +.cache \ No newline at end of file diff --git a/lab-apis.ipynb b/lab-apis.ipynb index e923554..b8e3e96 100644 --- a/lab-apis.ipynb +++ b/lab-apis.ipynb @@ -34,7 +34,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "ea44c82a-5c07-4dbc-beb2-bba2601bb75e", "metadata": {}, "outputs": [], @@ -45,7 +45,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "319cfd4e-f6fb-4419-80e0-e3983cd25e43", "metadata": {}, "outputs": [], @@ -56,15 +56,61 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "04e12954-fd70-4311-88a5-fb7e2c29799c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: spotipy in /home/ricci-preto/anaconda3/lib/python3.13/site-packages (2.26.0)\n", + "Requirement already satisfied: redis>=3.5.3 in /home/ricci-preto/anaconda3/lib/python3.13/site-packages (from spotipy) (8.0.0)\n", + "Requirement already satisfied: requests>=2.25.0 in /home/ricci-preto/anaconda3/lib/python3.13/site-packages (from spotipy) (2.34.2)\n", + "Requirement already satisfied: urllib3>=1.26.0 in /home/ricci-preto/anaconda3/lib/python3.13/site-packages (from spotipy) (2.7.0)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /home/ricci-preto/anaconda3/lib/python3.13/site-packages (from requests>=2.25.0->spotipy) (3.4.4)\n", + "Requirement already satisfied: idna<4,>=2.5 in /home/ricci-preto/anaconda3/lib/python3.13/site-packages (from requests>=2.25.0->spotipy) (3.18)\n", + "Requirement already satisfied: certifi>=2023.5.7 in /home/ricci-preto/anaconda3/lib/python3.13/site-packages (from requests>=2.25.0->spotipy) (2026.5.20)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m26.1.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.2.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" + ] + } + ], "source": [ "# If you havent done so, install the spotipy wrapper\n", "!pip install spotipy" ] }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b66fdc50", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: pip in /home/ricci-preto/anaconda3/lib/python3.13/site-packages (26.1.2)\n", + "Collecting pip\n", + " Downloading pip-26.2.1-py3-none-any.whl.metadata (4.6 kB)\n", + "Downloading pip-26.2.1-py3-none-any.whl (1.8 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.8/1.8 MB\u001b[0m \u001b[31m3.5 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m eta \u001b[36m0:00:01\u001b[0m\n", + "\u001b[?25hInstalling collected packages: pip\n", + " Attempting uninstall: pip\n", + " Found existing installation: pip 26.1.2\n", + " Uninstalling pip-26.1.2:\n", + " Successfully uninstalled pip-26.1.2\n", + "Successfully installed pip-26.2.1\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install --upgrade pip" + ] + }, { "cell_type": "markdown", "id": "dc0e86da-8846-4207-84c3-cd20b9e01d0e", @@ -75,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "03034bc6-9858-412a-83b7-18abdc345d7e", "metadata": {}, "outputs": [], @@ -106,12 +152,39 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "7772a1e0-9692-4d04-a590-76111a272d8d", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "HTTP Error for GET to https://api.spotify.com/v1/search with Params: {'q': 'Lady Gaga', 'limit': 50, 'offset': 0, 'type': 'track', 'market': None} returned 403 due to Active premium subscription required for the owner of the app. When the subscription status changes, it can take a few hours before requests are allowed again.\n" + ] + }, + { + "ename": "SpotifyException", + "evalue": "http status: 403, code: -1 - https://api.spotify.com/v1/search?q=Lady+Gaga&limit=50&offset=0&type=track:\n Active premium subscription required for the owner of the app. When the subscription status changes, it can take a few hours before requests are allowed again., reason: None", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mHTTPError\u001b[39m Traceback (most recent call last)", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:271\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 266\u001b[39m response = \u001b[38;5;28mself\u001b[39m._session.request(\n\u001b[32m 267\u001b[39m method, url, headers=headers, proxies=\u001b[38;5;28mself\u001b[39m.proxies,\n\u001b[32m 268\u001b[39m timeout=\u001b[38;5;28mself\u001b[39m.requests_timeout, **args\n\u001b[32m 269\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m271\u001b[39m \u001b[30;43mresponse\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mraise_for_status\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 272\u001b[39m results = response.json()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/requests/models.py:1167\u001b[39m, in \u001b[36mResponse.raise_for_status\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 1166\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m http_error_msg:\n\u001b[32m-> \u001b[39m\u001b[32m1167\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(http_error_msg, response=\u001b[38;5;28mself\u001b[39m)\n", + "\u001b[31mHTTPError\u001b[39m: 403 Client Error: Forbidden for url: https://api.spotify.com/v1/search?q=Lady+Gaga&limit=50&offset=0&type=track", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[31mSpotifyException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[6]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m results = sp.search(q=\u001b[33m'Lady Gaga'\u001b[39m, limit=\u001b[32m50\u001b[39m)\n\u001b[32m 2\u001b[39m results\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:613\u001b[39m, in \u001b[36mSpotify.search\u001b[39m\u001b[34m(self, q, limit, offset, type, market)\u001b[39m\n\u001b[32m 598\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34msearch\u001b[39m(\u001b[38;5;28mself\u001b[39m, q, limit=\u001b[32m10\u001b[39m, offset=\u001b[32m0\u001b[39m, \u001b[38;5;28mtype\u001b[39m=\u001b[33m\"\u001b[39m\u001b[33mtrack\u001b[39m\u001b[33m\"\u001b[39m, market=\u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m 599\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\" searches for an item\u001b[39;00m\n\u001b[32m 600\u001b[39m \n\u001b[32m 601\u001b[39m \u001b[33;03m Parameters:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 611\u001b[39m \u001b[33;03m from_token.\u001b[39;00m\n\u001b[32m 612\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m613\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_get\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m 614\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43msearch\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mq\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mq\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mtype\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mtype\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\n\u001b[32m 615\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:321\u001b[39m, in \u001b[36mSpotify._get\u001b[39m\u001b[34m(self, url, args, payload, **kwargs)\u001b[39m\n\u001b[32m 318\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m args:\n\u001b[32m 319\u001b[39m kwargs.update(args)\n\u001b[32m--> \u001b[39m\u001b[32m321\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_internal_call\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mGET\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43murl\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mpayload\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mkwargs\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:291\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 286\u001b[39m reason = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 288\u001b[39m logger.error(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mHTTP Error for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmethod\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00murl\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m with Params: \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 289\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00margs.get(\u001b[33m'\u001b[39m\u001b[33mparams\u001b[39m\u001b[33m'\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m returned \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.status_code\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m due to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m SpotifyException(\n\u001b[32m 292\u001b[39m response.status_code,\n\u001b[32m 293\u001b[39m -\u001b[32m1\u001b[39m,\n\u001b[32m 294\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.url\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 295\u001b[39m reason=reason,\n\u001b[32m 296\u001b[39m headers=response.headers,\n\u001b[32m 297\u001b[39m )\n\u001b[32m 298\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m requests.exceptions.RetryError \u001b[38;5;28;01mas\u001b[39;00m retry_error:\n\u001b[32m 299\u001b[39m request = retry_error.request\n", + "\u001b[31mSpotifyException\u001b[39m: http status: 403, code: -1 - https://api.spotify.com/v1/search?q=Lady+Gaga&limit=50&offset=0&type=track:\n Active premium subscription required for the owner of the app. When the subscription status changes, it can take a few hours before requests are allowed again., reason: None" + ] + } + ], "source": [ "results = sp.search(q='Lady Gaga', limit=50)\n", "results" @@ -382,12 +455,631 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "29694252-f217-454d-8881-681b2b6eeb1e", "metadata": {}, "outputs": [], "source": [ - "# Your answer here" + "import os\n", + "from dotenv import load_dotenv\n", + "import spotipy\n", + "from spotipy.oauth2 import SpotifyOAuth\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "fccdf295", + "metadata": {}, + "outputs": [], + "source": [ + "load_dotenv()\n", + "\n", + "client_id = os.getenv(\"SPOTIPY_CLIENT_ID\")\n", + "client_secret = os.getenv(\"SPOTIPY_CLIENT_SECRET\")\n", + "redirect_uri = os.getenv(\"SPOTIPY_REDIRECT_URI\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "42ac369e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "8466cf7198134d90be35c664d876f1d9\n", + "http://127.0.0.1:8080\n", + "True\n" + ] + } + ], + "source": [ + "print(client_id)\n", + "print(redirect_uri)\n", + "print(client_secret is not None)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2bb9ea73", + "metadata": {}, + "outputs": [], + "source": [ + "sp = spotipy.Spotify(\n", + " auth_manager=SpotifyOAuth(\n", + " client_id=client_id,\n", + " client_secret=client_secret,\n", + " redirect_uri=redirect_uri,\n", + " scope=\"user-library-read\"\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "dd158f3c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'artists': {'href': 'https://api.spotify.com/v1/search?offset=0&limit=1&query=artist%3AColdplay&type=artist',\n", + " 'limit': 1,\n", + " 'next': 'https://api.spotify.com/v1/search?offset=1&limit=1&query=artist%3AColdplay&type=artist',\n", + " 'offset': 0,\n", + " 'previous': None,\n", + " 'total': 11,\n", + " 'items': [{'external_urls': {'spotify': 'https://open.spotify.com/artist/4gzpq5DPGxSnKTe4SA8HAU'},\n", + " 'href': 'https://api.spotify.com/v1/artists/4gzpq5DPGxSnKTe4SA8HAU',\n", + " 'id': '4gzpq5DPGxSnKTe4SA8HAU',\n", + " 'images': [{'url': 'https://i.scdn.co/image/ab6761610000e5eb1ba8fc5f5c73e7e9313cc6eb',\n", + " 'height': 640,\n", + " 'width': 640},\n", + " {'url': 'https://i.scdn.co/image/ab676161000051741ba8fc5f5c73e7e9313cc6eb',\n", + " 'height': 320,\n", + " 'width': 320},\n", + " {'url': 'https://i.scdn.co/image/ab6761610000f1781ba8fc5f5c73e7e9313cc6eb',\n", + " 'height': 160,\n", + " 'width': 160}],\n", + " 'name': 'Coldplay',\n", + " 'type': 'artist',\n", + " 'uri': 'spotify:artist:4gzpq5DPGxSnKTe4SA8HAU'}]}}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = sp.search(q=\"artist:Coldplay\", type=\"artist\", limit=1)\n", + "\n", + "results" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "90fa5014", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Artist: Coldplay\n", + "ID: 4gzpq5DPGxSnKTe4SA8HAU\n" + ] + } + ], + "source": [ + "artist = results[\"artists\"][\"items\"][0]\n", + "\n", + "print(\"Artist:\", artist[\"name\"])\n", + "print(\"ID:\", artist[\"id\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "7d1790e3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Coldplay', 'Queen', 'The Beatles']" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "artists = [\"Coldplay\", \"Queen\", \"The Beatles\"]\n", + "artists" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "a79637d4", + "metadata": {}, + "outputs": [], + "source": [ + "def get_top_tracks(artist_name):\n", + " results = sp.search(\n", + " q=f\"artist:{artist_name}\",\n", + " type=\"track\",\n", + " limit=5\n", + " )\n", + " \n", + " return [track[\"name\"] for track in results[\"tracks\"][\"items\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "c4d824d2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['A Sky Full of Stars', 'Viva La Vida', 'Yellow']" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_top_tracks(artists[0])" + ] + }, + { + "cell_type": "markdown", + "id": "ae5e5162", + "metadata": {}, + "source": [ + "Note: Spotify deprecated and removed the GET /artists/{id}/top-tracks endpoint in February 2026. Therefore, the original sp.artist_top_tracks() method used in this exercise is no longer available for new applications. To complete the exercise with the current Spotify Web API, I used the Search endpoint to retrieve tracks associated with each artist instead. These results should therefore be considered Spotify search results rather than official artist top tracks." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "116c15f1", + "metadata": {}, + "outputs": [], + "source": [ + "top_tracks_list = [get_top_tracks(artist) for artist in artists]" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "b7d41e30", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[['A Sky Full of Stars', 'Viva La Vida', 'Yellow'],\n", + " ['Bohemian Rhapsody',\n", + " \"Don't Stop Me Now\",\n", + " 'Under Pressure (feat. David Bowie)'],\n", + " ['Blackbird - Remastered 2009',\n", + " 'Here Comes The Sun - Remastered 2009',\n", + " 'Let It Be - Remastered 2009']]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "top_tracks_list" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "f620c64b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4gzpq5DPGxSnKTe4SA8HAU\n" + ] + } + ], + "source": [ + "results = sp.search(\n", + " q=f\"artist:{artists[0]}\",\n", + " type=\"artist\",\n", + " limit=1\n", + ")\n", + "\n", + "artist_id = results[\"artists\"][\"items\"][0][\"id\"]\n", + "\n", + "print(artist_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "d8973d31", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "HTTP Error for GET to https://api.spotify.com/v1/artists/4gzpq5DPGxSnKTe4SA8HAU/related-artists with Params: {} returned 403 due to Forbidden\n" + ] + }, + { + "ename": "SpotifyException", + "evalue": "http status: 403, code: -1 - https://api.spotify.com/v1/artists/4gzpq5DPGxSnKTe4SA8HAU/related-artists:\n Forbidden, reason: None", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mHTTPError\u001b[39m Traceback (most recent call last)", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:271\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 266\u001b[39m response = \u001b[38;5;28mself\u001b[39m._session.request(\n\u001b[32m 267\u001b[39m method, url, headers=headers, proxies=\u001b[38;5;28mself\u001b[39m.proxies,\n\u001b[32m 268\u001b[39m timeout=\u001b[38;5;28mself\u001b[39m.requests_timeout, **args\n\u001b[32m 269\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m271\u001b[39m \u001b[30;43mresponse\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mraise_for_status\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 272\u001b[39m results = response.json()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/requests/models.py:1167\u001b[39m, in \u001b[36mResponse.raise_for_status\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 1166\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m http_error_msg:\n\u001b[32m-> \u001b[39m\u001b[32m1167\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(http_error_msg, response=\u001b[38;5;28mself\u001b[39m)\n", + "\u001b[31mHTTPError\u001b[39m: 403 Client Error: Forbidden for url: https://api.spotify.com/v1/artists/4gzpq5DPGxSnKTe4SA8HAU/related-artists", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[31mSpotifyException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[23]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m sp.artist_related_artists(artist_id)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:473\u001b[39m, in \u001b[36mSpotify.artist_related_artists\u001b[39m\u001b[34m(self, artist_id)\u001b[39m\n\u001b[32m 467\u001b[39m warnings.warn(\n\u001b[32m 468\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mYou\u001b[39m\u001b[33m'\u001b[39m\u001b[33mre using `artist_related_artists(...)`, \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 469\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mwhich is marked as deprecated by Spotify.\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 470\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m\n\u001b[32m 471\u001b[39m )\n\u001b[32m 472\u001b[39m trid = \u001b[38;5;28mself\u001b[39m._get_id(\u001b[33m\"\u001b[39m\u001b[33martist\u001b[39m\u001b[33m\"\u001b[39m, artist_id)\n\u001b[32m--> \u001b[39m\u001b[32m473\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_get\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43martists/\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m+\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mtrid\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m+\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m/related-artists\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:321\u001b[39m, in \u001b[36mSpotify._get\u001b[39m\u001b[34m(self, url, args, payload, **kwargs)\u001b[39m\n\u001b[32m 318\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m args:\n\u001b[32m 319\u001b[39m kwargs.update(args)\n\u001b[32m--> \u001b[39m\u001b[32m321\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_internal_call\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mGET\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43murl\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mpayload\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mkwargs\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:291\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 286\u001b[39m reason = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 288\u001b[39m logger.error(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mHTTP Error for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmethod\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00murl\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m with Params: \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 289\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00margs.get(\u001b[33m'\u001b[39m\u001b[33mparams\u001b[39m\u001b[33m'\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m returned \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.status_code\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m due to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m SpotifyException(\n\u001b[32m 292\u001b[39m response.status_code,\n\u001b[32m 293\u001b[39m -\u001b[32m1\u001b[39m,\n\u001b[32m 294\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.url\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 295\u001b[39m reason=reason,\n\u001b[32m 296\u001b[39m headers=response.headers,\n\u001b[32m 297\u001b[39m )\n\u001b[32m 298\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m requests.exceptions.RetryError \u001b[38;5;28;01mas\u001b[39;00m retry_error:\n\u001b[32m 299\u001b[39m request = retry_error.request\n", + "\u001b[31mSpotifyException\u001b[39m: http status: 403, code: -1 - https://api.spotify.com/v1/artists/4gzpq5DPGxSnKTe4SA8HAU/related-artists:\n Forbidden, reason: None" + ] + } + ], + "source": [ + "sp.artist_related_artists(artist_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "eabbc615", + "metadata": {}, + "outputs": [], + "source": [ + "def find_related_artists(artist_name):\n", + " # Search for artists related to the given artist name.\n", + " results = sp.search(\n", + " q=artist_name,\n", + " type=\"artist\",\n", + " limit=5\n", + " )\n", + " \n", + " return [artist[\"name\"] for artist in results[\"artists\"][\"items\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "f15e4c4e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Coldplay',\n", + " 'Imagine Dragons',\n", + " 'Pedro Abrunhosa',\n", + " 'Plutonio',\n", + " 'Coldplay Piano Covers']" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "find_related_artists(artists[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "820dc02e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[['Coldplay',\n", + " 'Imagine Dragons',\n", + " 'Pedro Abrunhosa',\n", + " 'Plutonio',\n", + " 'Coldplay Piano Covers'],\n", + " ['Queen',\n", + " 'Queens of the Stone Age',\n", + " 'Michael Jackson',\n", + " 'Freddie Mercury',\n", + " 'Beyoncé'],\n", + " ['The Beatles', 'Queen', 'Eagles', 'GNR', 'Xutos & Pontapés']]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "related_artists_list = [find_related_artists(artist) for artist in artists]\n", + "related_artists_list" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "5c07c47c", + "metadata": {}, + "outputs": [], + "source": [ + "all_related_artists = [\n", + " related_artist\n", + " for related_list in related_artists_list\n", + " for related_artist in related_list\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "a485c915", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Coldplay',\n", + " 'Imagine Dragons',\n", + " 'Pedro Abrunhosa',\n", + " 'Plutonio',\n", + " 'Coldplay Piano Covers',\n", + " 'Queen',\n", + " 'Queens of the Stone Age',\n", + " 'Michael Jackson',\n", + " 'Freddie Mercury',\n", + " 'Beyoncé',\n", + " 'The Beatles',\n", + " 'Queen',\n", + " 'Eagles',\n", + " 'GNR',\n", + " 'Xutos & Pontapés']" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all_related_artists" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "68dad89c", + "metadata": {}, + "outputs": [], + "source": [ + "related_tracks = [\n", + " get_top_tracks(artist)\n", + " for artist in all_related_artists\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "a1cecc73", + "metadata": {}, + "outputs": [], + "source": [ + "playlist_tracks = [\n", + " track\n", + " for track_list in top_tracks_list + related_tracks\n", + " for track in track_list\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "61869656", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['A Sky Full of Stars',\n", + " 'Viva La Vida',\n", + " 'Yellow',\n", + " 'Bohemian Rhapsody',\n", + " \"Don't Stop Me Now\",\n", + " 'Under Pressure (feat. David Bowie)',\n", + " 'Blackbird - Remastered 2009',\n", + " 'Here Comes The Sun - Remastered 2009',\n", + " 'Let It Be - Remastered 2009',\n", + " 'A Sky Full of Stars',\n", + " 'Viva La Vida',\n", + " 'Yellow',\n", + " 'Demons',\n", + " 'Believer',\n", + " 'Thunder',\n", + " 'Que O Amor Te Salve Nesta Noite Escura - Ao Vivo',\n", + " 'Para Os Braços Da Minha Mãe',\n", + " 'Tudo O Que Eu Te Dou',\n", + " \"C'est La Vie (feat. Plutonio)\",\n", + " 'Interestelar',\n", + " '25 DE ABRIL (feat. Plutonio)',\n", + " 'Coldplay - Clocks - Piano Cover',\n", + " 'Coldplay - Viva La Vida - Piano Cover',\n", + " 'Coldplay - Yellow - Piano Cover',\n", + " 'Coldplay - Paradise - Piano Cover',\n", + " 'Coldplay - A Sky Full Of Stars - Piano Cover',\n", + " 'Bohemian Rhapsody',\n", + " \"Don't Stop Me Now\",\n", + " 'Under Pressure (feat. David Bowie)',\n", + " 'Go With The Flow',\n", + " 'No One Knows',\n", + " 'Make It Wit Chu',\n", + " 'Billie Jean',\n", + " 'Beat It',\n", + " 'Chicago',\n", + " 'Living On My Own - No More Brothers Radio Mix',\n", + " 'Barcelona',\n", + " 'I Was Born to Love You',\n", + " 'MORNING DEW (DONK)',\n", + " 'Halo',\n", + " 'Crazy In Love (feat. JAY-Z)',\n", + " 'Blackbird - Remastered 2009',\n", + " 'Here Comes The Sun - Remastered 2009',\n", + " 'Let It Be - Remastered 2009',\n", + " 'Bohemian Rhapsody',\n", + " \"Don't Stop Me Now\",\n", + " 'Under Pressure (feat. David Bowie)',\n", + " 'Hotel California - 2013 Remaster',\n", + " 'Hotel California - Live On MTV, 1994',\n", + " 'One of These Nights - 2013 Remaster',\n", + " \"Sweet Child O' Mine\",\n", + " \"Knockin' On Heaven's Door\",\n", + " 'November Rain',\n", + " 'O Homem Do Leme',\n", + " 'Contentores',\n", + " 'A Minha Casinha']" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "playlist_tracks" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "41a2a7eb", + "metadata": {}, + "outputs": [], + "source": [ + "playlist_tracks = list(dict.fromkeys(playlist_tracks))" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "a0349965", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "44" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(playlist_tracks)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "f13c052c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['A Sky Full of Stars',\n", + " 'Viva La Vida',\n", + " 'Yellow',\n", + " 'Bohemian Rhapsody',\n", + " \"Don't Stop Me Now\",\n", + " 'Under Pressure (feat. David Bowie)',\n", + " 'Blackbird - Remastered 2009',\n", + " 'Here Comes The Sun - Remastered 2009',\n", + " 'Let It Be - Remastered 2009',\n", + " 'Demons',\n", + " 'Believer',\n", + " 'Thunder',\n", + " 'Que O Amor Te Salve Nesta Noite Escura - Ao Vivo',\n", + " 'Para Os Braços Da Minha Mãe',\n", + " 'Tudo O Que Eu Te Dou',\n", + " \"C'est La Vie (feat. Plutonio)\",\n", + " 'Interestelar',\n", + " '25 DE ABRIL (feat. Plutonio)',\n", + " 'Coldplay - Clocks - Piano Cover',\n", + " 'Coldplay - Viva La Vida - Piano Cover',\n", + " 'Coldplay - Yellow - Piano Cover',\n", + " 'Coldplay - Paradise - Piano Cover',\n", + " 'Coldplay - A Sky Full Of Stars - Piano Cover',\n", + " 'Go With The Flow',\n", + " 'No One Knows',\n", + " 'Make It Wit Chu',\n", + " 'Billie Jean',\n", + " 'Beat It',\n", + " 'Chicago',\n", + " 'Living On My Own - No More Brothers Radio Mix',\n", + " 'Barcelona',\n", + " 'I Was Born to Love You',\n", + " 'MORNING DEW (DONK)',\n", + " 'Halo',\n", + " 'Crazy In Love (feat. JAY-Z)',\n", + " 'Hotel California - 2013 Remaster',\n", + " 'Hotel California - Live On MTV, 1994',\n", + " 'One of These Nights - 2013 Remaster',\n", + " \"Sweet Child O' Mine\",\n", + " \"Knockin' On Heaven's Door\",\n", + " 'November Rain',\n", + " 'O Homem Do Leme',\n", + " 'Contentores',\n", + " 'A Minha Casinha']" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "playlist_tracks" + ] + }, + { + "cell_type": "markdown", + "id": "6b830d43", + "metadata": {}, + "source": [ + "API compatibility note: The original exercise uses Spotify endpoints for artist top tracks and related artists. These endpoints were removed from the Spotify Web API in February 2026. Therefore, the implementation was adapted using the currently available Search endpoint. The resulting tracks and artists should be interpreted as Spotify search results rather than Spotify's official top tracks or related-artists recommendations." ] }, { @@ -404,10 +1096,37 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "id": "25fb0cf8-c13a-41b0-b8f8-7e0700fd1e41", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "HTTP Error for GET to https://api.spotify.com/v1/browse/featured-playlists with Params: {'locale': None, 'country': None, 'timestamp': None, 'limit': 20, 'offset': 0} returned 403 due to Forbidden\n" + ] + }, + { + "ename": "SpotifyException", + "evalue": "http status: 403, code: -1 - https://api.spotify.com/v1/browse/featured-playlists?limit=20&offset=0:\n Forbidden, reason: None", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mHTTPError\u001b[39m Traceback (most recent call last)", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:271\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 266\u001b[39m response = \u001b[38;5;28mself\u001b[39m._session.request(\n\u001b[32m 267\u001b[39m method, url, headers=headers, proxies=\u001b[38;5;28mself\u001b[39m.proxies,\n\u001b[32m 268\u001b[39m timeout=\u001b[38;5;28mself\u001b[39m.requests_timeout, **args\n\u001b[32m 269\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m271\u001b[39m \u001b[30;43mresponse\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mraise_for_status\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 272\u001b[39m results = response.json()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/requests/models.py:1167\u001b[39m, in \u001b[36mResponse.raise_for_status\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 1166\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m http_error_msg:\n\u001b[32m-> \u001b[39m\u001b[32m1167\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(http_error_msg, response=\u001b[38;5;28mself\u001b[39m)\n", + "\u001b[31mHTTPError\u001b[39m: 403 Client Error: Forbidden for url: https://api.spotify.com/v1/browse/featured-playlists?limit=20&offset=0", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[31mSpotifyException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[35]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m sp.featured_playlists() \u001b[38;5;66;03m# We get a playlist id of a playlist we like\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:1721\u001b[39m, in \u001b[36mSpotify.featured_playlists\u001b[39m\u001b[34m(self, locale, country, timestamp, limit, offset)\u001b[39m\n\u001b[32m 1692\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\" Get a list of Spotify featured playlists\u001b[39;00m\n\u001b[32m 1693\u001b[39m \n\u001b[32m 1694\u001b[39m \u001b[33;03m .. deprecated::\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1714\u001b[39m \u001b[33;03m items.\u001b[39;00m\n\u001b[32m 1715\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 1716\u001b[39m warnings.warn(\n\u001b[32m 1717\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mYou\u001b[39m\u001b[33m'\u001b[39m\u001b[33mre using `featured_playlists(...)`, \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 1718\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mwhich is marked as deprecated by Spotify.\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 1719\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m,\n\u001b[32m 1720\u001b[39m )\n\u001b[32m-> \u001b[39m\u001b[32m1721\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_get\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m 1722\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mbrowse/featured-playlists\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1723\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mlocale\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mlocale\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1724\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mcountry\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mcountry\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1725\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mtimestamp\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mtimestamp\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1726\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1727\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 1728\u001b[39m \u001b[30;43m\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:321\u001b[39m, in \u001b[36mSpotify._get\u001b[39m\u001b[34m(self, url, args, payload, **kwargs)\u001b[39m\n\u001b[32m 318\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m args:\n\u001b[32m 319\u001b[39m kwargs.update(args)\n\u001b[32m--> \u001b[39m\u001b[32m321\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_internal_call\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mGET\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43murl\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mpayload\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mkwargs\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:291\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 286\u001b[39m reason = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 288\u001b[39m logger.error(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mHTTP Error for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmethod\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00murl\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m with Params: \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 289\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00margs.get(\u001b[33m'\u001b[39m\u001b[33mparams\u001b[39m\u001b[33m'\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m returned \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.status_code\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m due to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m SpotifyException(\n\u001b[32m 292\u001b[39m response.status_code,\n\u001b[32m 293\u001b[39m -\u001b[32m1\u001b[39m,\n\u001b[32m 294\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.url\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 295\u001b[39m reason=reason,\n\u001b[32m 296\u001b[39m headers=response.headers,\n\u001b[32m 297\u001b[39m )\n\u001b[32m 298\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m requests.exceptions.RetryError \u001b[38;5;28;01mas\u001b[39;00m retry_error:\n\u001b[32m 299\u001b[39m request = retry_error.request\n", + "\u001b[31mSpotifyException\u001b[39m: http status: 403, code: -1 - https://api.spotify.com/v1/browse/featured-playlists?limit=20&offset=0:\n Forbidden, reason: None" + ] + } + ], "source": [ "sp.featured_playlists() # We get a playlist id of a playlist we like" ] @@ -431,10 +1150,37 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "id": "46d35121-9256-4cf4-81f5-118b87f7af32", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "HTTP Error for GET to https://api.spotify.com/v1/playlists/37i9dQZF1DXd9zR7tdziuQ with Params: {'fields': None, 'market': None, 'additional_types': 'track'} returned 404 due to Resource not found\n" + ] + }, + { + "ename": "SpotifyException", + "evalue": "http status: 404, code: -1 - https://api.spotify.com/v1/playlists/37i9dQZF1DXd9zR7tdziuQ?additional_types=track:\n Resource not found, reason: None", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mHTTPError\u001b[39m Traceback (most recent call last)", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:271\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 266\u001b[39m response = \u001b[38;5;28mself\u001b[39m._session.request(\n\u001b[32m 267\u001b[39m method, url, headers=headers, proxies=\u001b[38;5;28mself\u001b[39m.proxies,\n\u001b[32m 268\u001b[39m timeout=\u001b[38;5;28mself\u001b[39m.requests_timeout, **args\n\u001b[32m 269\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m271\u001b[39m \u001b[30;43mresponse\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mraise_for_status\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 272\u001b[39m results = response.json()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/requests/models.py:1167\u001b[39m, in \u001b[36mResponse.raise_for_status\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 1166\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m http_error_msg:\n\u001b[32m-> \u001b[39m\u001b[32m1167\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(http_error_msg, response=\u001b[38;5;28mself\u001b[39m)\n", + "\u001b[31mHTTPError\u001b[39m: 404 Client Error: Not Found for url: https://api.spotify.com/v1/playlists/37i9dQZF1DXd9zR7tdziuQ?additional_types=track", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[31mSpotifyException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[36]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m playlist_id = \u001b[33m\"37i9dQZF1DXd9zR7tdziuQ\"\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m playlist = sp.playlist(playlist_id)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:682\u001b[39m, in \u001b[36mSpotify.playlist\u001b[39m\u001b[34m(self, playlist_id, fields, market, additional_types)\u001b[39m\n\u001b[32m 671\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\" Gets playlist by id.\u001b[39;00m\n\u001b[32m 672\u001b[39m \n\u001b[32m 673\u001b[39m \u001b[33;03m Parameters:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 679\u001b[39m \u001b[33;03m valid types are: track and episode\u001b[39;00m\n\u001b[32m 680\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 681\u001b[39m plid = \u001b[38;5;28mself\u001b[39m._get_id(\u001b[33m\"\u001b[39m\u001b[33mplaylist\u001b[39m\u001b[33m\"\u001b[39m, playlist_id)\n\u001b[32m--> \u001b[39m\u001b[32m682\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_get\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m 683\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mf\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mplaylists/\u001b[39;49m\u001b[30;43;01m{\u001b[39;49;00m\u001b[30;43mplid\u001b[39;49m\u001b[30;43;01m}\u001b[39;49;00m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 684\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mfields\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mfields\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 685\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 686\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43madditional_types\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mjoin\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43madditional_types\u001b[39;49m\u001b[30;43m)\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 687\u001b[39m \u001b[30;43m\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:321\u001b[39m, in \u001b[36mSpotify._get\u001b[39m\u001b[34m(self, url, args, payload, **kwargs)\u001b[39m\n\u001b[32m 318\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m args:\n\u001b[32m 319\u001b[39m kwargs.update(args)\n\u001b[32m--> \u001b[39m\u001b[32m321\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_internal_call\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mGET\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43murl\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mpayload\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mkwargs\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:291\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 286\u001b[39m reason = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 288\u001b[39m logger.error(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mHTTP Error for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmethod\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00murl\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m with Params: \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 289\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00margs.get(\u001b[33m'\u001b[39m\u001b[33mparams\u001b[39m\u001b[33m'\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m returned \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.status_code\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m due to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m SpotifyException(\n\u001b[32m 292\u001b[39m response.status_code,\n\u001b[32m 293\u001b[39m -\u001b[32m1\u001b[39m,\n\u001b[32m 294\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.url\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 295\u001b[39m reason=reason,\n\u001b[32m 296\u001b[39m headers=response.headers,\n\u001b[32m 297\u001b[39m )\n\u001b[32m 298\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m requests.exceptions.RetryError \u001b[38;5;28;01mas\u001b[39;00m retry_error:\n\u001b[32m 299\u001b[39m request = retry_error.request\n", + "\u001b[31mSpotifyException\u001b[39m: http status: 404, code: -1 - https://api.spotify.com/v1/playlists/37i9dQZF1DXd9zR7tdziuQ?additional_types=track:\n Resource not found, reason: None" + ] + } + ], "source": [ "playlist_id = \"37i9dQZF1DXd9zR7tdziuQ\"\n", "playlist = sp.playlist(playlist_id)" @@ -442,10 +1188,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "id": "5260f67f-6024-4fee-8449-30904f03bf76", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'playlist' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[37]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m print(playlist[\u001b[33m'name'\u001b[39m]) \u001b[38;5;66;03m# Print the playlist's name\u001b[39;00m\n\u001b[32m 2\u001b[39m print(playlist[\u001b[33m'description'\u001b[39m]) \u001b[38;5;66;03m# Print the playlist's description\u001b[39;00m\n", + "\u001b[31mNameError\u001b[39m: name 'playlist' is not defined" + ] + } + ], "source": [ "print(playlist['name']) # Print the playlist's name\n", "print(playlist['description']) # Print the playlist's description" @@ -462,10 +1220,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 38, "id": "69c78f8d-7e6a-4d15-bcbb-fc93edb82433", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "HTTP Error for GET to https://api.spotify.com/v1/playlists/37i9dQZF1DXd9zR7tdziuQ/items with Params: {'limit': 50, 'offset': 0, 'fields': None, 'market': None, 'additional_types': 'track'} returned 404 due to Resource not found\n" + ] + }, + { + "ename": "SpotifyException", + "evalue": "http status: 404, code: -1 - https://api.spotify.com/v1/playlists/37i9dQZF1DXd9zR7tdziuQ/items?limit=50&offset=0&additional_types=track:\n Resource not found, reason: None", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mHTTPError\u001b[39m Traceback (most recent call last)", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:271\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 266\u001b[39m response = \u001b[38;5;28mself\u001b[39m._session.request(\n\u001b[32m 267\u001b[39m method, url, headers=headers, proxies=\u001b[38;5;28mself\u001b[39m.proxies,\n\u001b[32m 268\u001b[39m timeout=\u001b[38;5;28mself\u001b[39m.requests_timeout, **args\n\u001b[32m 269\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m271\u001b[39m \u001b[30;43mresponse\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mraise_for_status\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 272\u001b[39m results = response.json()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/requests/models.py:1167\u001b[39m, in \u001b[36mResponse.raise_for_status\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 1166\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m http_error_msg:\n\u001b[32m-> \u001b[39m\u001b[32m1167\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(http_error_msg, response=\u001b[38;5;28mself\u001b[39m)\n", + "\u001b[31mHTTPError\u001b[39m: 404 Client Error: Not Found for url: https://api.spotify.com/v1/playlists/37i9dQZF1DXd9zR7tdziuQ/items?limit=50&offset=0&additional_types=track", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[31mSpotifyException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[38]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m tracks = sp.playlist_tracks(playlist_id)\n\u001b[32m 2\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m track \u001b[38;5;28;01min\u001b[39;00m tracks[\u001b[33m'items'\u001b[39m]:\n\u001b[32m 3\u001b[39m print(track[\u001b[33m'track'\u001b[39m][\u001b[33m'name'\u001b[39m]) \u001b[38;5;66;03m# Print each track's name\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:718\u001b[39m, in \u001b[36mSpotify.playlist_tracks\u001b[39m\u001b[34m(self, playlist_id, fields, limit, offset, market, additional_types)\u001b[39m\n\u001b[32m 698\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\" Get full details of the tracks of a playlist.\u001b[39;00m\n\u001b[32m 699\u001b[39m \n\u001b[32m 700\u001b[39m \u001b[33;03m .. deprecated::\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 711\u001b[39m \u001b[33;03m valid types are: track and episode\u001b[39;00m\n\u001b[32m 712\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 713\u001b[39m warnings.warn(\n\u001b[32m 714\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mYou should use `playlist_items(playlist_id, ...,\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 715\u001b[39m \u001b[33m\"\u001b[39m\u001b[33madditional_types=(\u001b[39m\u001b[33m'\u001b[39m\u001b[33mtrack\u001b[39m\u001b[33m'\u001b[39m\u001b[33m,))` instead\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 716\u001b[39m \u001b[38;5;167;01mDeprecationWarning\u001b[39;00m,\n\u001b[32m 717\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m718\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mplaylist_items\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43mplaylist_id\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mfields\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 719\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43madditional_types\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:742\u001b[39m, in \u001b[36mSpotify.playlist_items\u001b[39m\u001b[34m(self, playlist_id, fields, limit, offset, market, additional_types)\u001b[39m\n\u001b[32m 730\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\" Get full details of the tracks and episodes of a playlist.\u001b[39;00m\n\u001b[32m 731\u001b[39m \n\u001b[32m 732\u001b[39m \u001b[33;03m Parameters:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 739\u001b[39m \u001b[33;03m valid types are: track and episode\u001b[39;00m\n\u001b[32m 740\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 741\u001b[39m plid = \u001b[38;5;28mself\u001b[39m._get_id(\u001b[33m\"\u001b[39m\u001b[33mplaylist\u001b[39m\u001b[33m\"\u001b[39m, playlist_id)\n\u001b[32m--> \u001b[39m\u001b[32m742\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_get\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m 743\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mf\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mplaylists/\u001b[39;49m\u001b[30;43;01m{\u001b[39;49;00m\u001b[30;43mplid\u001b[39;49m\u001b[30;43;01m}\u001b[39;49;00m\u001b[30;43m/items\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 744\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 745\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 746\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mfields\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mfields\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 747\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 748\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43madditional_types\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mjoin\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43madditional_types\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 749\u001b[39m \u001b[30;43m\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:321\u001b[39m, in \u001b[36mSpotify._get\u001b[39m\u001b[34m(self, url, args, payload, **kwargs)\u001b[39m\n\u001b[32m 318\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m args:\n\u001b[32m 319\u001b[39m kwargs.update(args)\n\u001b[32m--> \u001b[39m\u001b[32m321\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_internal_call\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mGET\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43murl\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mpayload\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mkwargs\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:291\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 286\u001b[39m reason = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 288\u001b[39m logger.error(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mHTTP Error for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmethod\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00murl\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m with Params: \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 289\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00margs.get(\u001b[33m'\u001b[39m\u001b[33mparams\u001b[39m\u001b[33m'\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m returned \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.status_code\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m due to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m SpotifyException(\n\u001b[32m 292\u001b[39m response.status_code,\n\u001b[32m 293\u001b[39m -\u001b[32m1\u001b[39m,\n\u001b[32m 294\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.url\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 295\u001b[39m reason=reason,\n\u001b[32m 296\u001b[39m headers=response.headers,\n\u001b[32m 297\u001b[39m )\n\u001b[32m 298\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m requests.exceptions.RetryError \u001b[38;5;28;01mas\u001b[39;00m retry_error:\n\u001b[32m 299\u001b[39m request = retry_error.request\n", + "\u001b[31mSpotifyException\u001b[39m: http status: 404, code: -1 - https://api.spotify.com/v1/playlists/37i9dQZF1DXd9zR7tdziuQ/items?limit=50&offset=0&additional_types=track:\n Resource not found, reason: None" + ] + } + ], "source": [ "tracks = sp.playlist_tracks(playlist_id)\n", "for track in tracks['items']:\n", @@ -488,10 +1274,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "id": "65c5e5c4-f186-42c6-b136-4ef02b0b01ff", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'tracks' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mNameError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[39]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# List to store unique artist names\u001b[39;00m\n\u001b[32m 2\u001b[39m artists_list = []\n\u001b[32m 3\u001b[39m \n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m track_item \u001b[38;5;28;01min\u001b[39;00m tracks[\u001b[33m'items'\u001b[39m]:\n\u001b[32m 5\u001b[39m track = track_item[\u001b[33m'track'\u001b[39m]\n\u001b[32m 6\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m artist \u001b[38;5;28;01min\u001b[39;00m track[\u001b[33m'artists'\u001b[39m]:\n\u001b[32m 7\u001b[39m artist_name = artist[\u001b[33m'name'\u001b[39m]\n", + "\u001b[31mNameError\u001b[39m: name 'tracks' is not defined" + ] + } + ], "source": [ "# List to store unique artist names\n", "artists_list = []\n", @@ -533,18 +1331,247 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "id": "ed92d961-9646-4375-a386-ccc320a958f5", "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'playlists': {'href': 'https://api.spotify.com/v1/search?offset=0&limit=5&query=playlist%3AToday%27s%20Top%20Hits&type=playlist',\n", + " 'limit': 5,\n", + " 'next': 'https://api.spotify.com/v1/search?offset=5&limit=5&query=playlist%3AToday%27s%20Top%20Hits&type=playlist',\n", + " 'offset': 0,\n", + " 'previous': None,\n", + " 'total': 8,\n", + " 'items': [{'collaborative': False,\n", + " 'description': 'Best Hits 2026 | Popular Songs 2026 | Top Music Hits | Today Top Hits | Viral Hits | Radio Hits 2025/2026 | Summer Music Mix 2026 | Best Of 2026 | Summer Vibes | Deep House | Pop Vibes | Top Songs | Top Music | Global Hits | Best Songs| Clean | Pop Hits | Viral Pop - updated and active! :)',\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/playlist/5KJDMJe9EJ7QRz8FG2MIpI'},\n", + " 'href': 'https://api.spotify.com/v1/playlists/5KJDMJe9EJ7QRz8FG2MIpI',\n", + " 'id': '5KJDMJe9EJ7QRz8FG2MIpI',\n", + " 'images': [{'height': None,\n", + " 'url': 'https://image-cdn-ak.spotifycdn.com/image/ab67706c0000da84c9571da656091290878b1a3b',\n", + " 'width': None}],\n", + " 'name': 'BEST HITS 2026 🔥 Popular Songs 🎵 Top Summer Music Hits 🎵 ',\n", + " 'owner': {'display_name': 'Hot Vibes',\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/user/hotamazingnetwork'},\n", + " 'href': 'https://api.spotify.com/v1/users/hotamazingnetwork',\n", + " 'id': 'hotamazingnetwork',\n", + " 'type': 'user',\n", + " 'uri': 'spotify:user:hotamazingnetwork'},\n", + " 'primary_color': None,\n", + " 'public': True,\n", + " 'snapshot_id': 'AAAvJKlg9upIYbYtNyEt3lmMURYWZenG',\n", + " 'items': {'href': 'https://api.spotify.com/v1/playlists/5KJDMJe9EJ7QRz8FG2MIpI/items',\n", + " 'total': 149},\n", + " 'type': 'playlist',\n", + " 'uri': 'spotify:playlist:5KJDMJe9EJ7QRz8FG2MIpI'},\n", + " None,\n", + " {'collaborative': False,\n", + " 'description': 'best country music of 2026 most popular trending today modern mix viral top 100 hottest contemporary country pop rock latest americana ⚠️ NO SOCIAL MEDIA ACCOUNTS ASSOCIATED WITH THIS PROFILE. BEWARE OF SCAMS!',\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/playlist/4Jb4PDWREzNnbZcOHPcZPy'},\n", + " 'href': 'https://api.spotify.com/v1/playlists/4Jb4PDWREzNnbZcOHPcZPy',\n", + " 'id': '4Jb4PDWREzNnbZcOHPcZPy',\n", + " 'images': [{'height': None,\n", + " 'url': 'https://image-cdn-fa.spotifycdn.com/image/ab67706c0000da848c8afccecd02d63d920f4c97',\n", + " 'width': None}],\n", + " 'name': 'COUNTRY HITS 2026 🔥 New Country Songs + Top Hits',\n", + " 'owner': {'display_name': 'Spot',\n", + " 'external_urls': {'spotify': 'https://open.spotify.com/user/31heeg5wrzm67nrgtehxz23z2mmq'},\n", + " 'href': 'https://api.spotify.com/v1/users/31heeg5wrzm67nrgtehxz23z2mmq',\n", + " 'id': '31heeg5wrzm67nrgtehxz23z2mmq',\n", + " 'type': 'user',\n", + " 'uri': 'spotify:user:31heeg5wrzm67nrgtehxz23z2mmq'},\n", + " 'primary_color': None,\n", + " 'public': True,\n", + " 'snapshot_id': 'AAAKBjqpjp/5B3OPj8gLaXMWp1yrwcvV',\n", + " 'items': {'href': 'https://api.spotify.com/v1/playlists/4Jb4PDWREzNnbZcOHPcZPy/items',\n", + " 'total': 109},\n", + " 'type': 'playlist',\n", + " 'uri': 'spotify:playlist:4Jb4PDWREzNnbZcOHPcZPy'},\n", + " None,\n", + " None]}}" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = sp.search(\n", + " q=\"playlist:Today's Top Hits\",\n", + " type=\"playlist\",\n", + " limit=5\n", + ")\n", + "\n", + "results" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "b0d3bbcd", + "metadata": {}, + "outputs": [], + "source": [ + "search_playlists = [\n", + " {\n", + " \"name\": playlist[\"name\"],\n", + " \"id\": playlist[\"id\"]\n", + " }\n", + " for playlist in results[\"playlists\"][\"items\"]\n", + " if playlist is not None\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "bf05c061", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'name': 'BEST HITS 2026 🔥 Popular Songs 🎵 Top Summer Music Hits 🎵 ',\n", + " 'id': '5KJDMJe9EJ7QRz8FG2MIpI'},\n", + " {'name': 'COUNTRY HITS 2026 🔥 New Country Songs + Top Hits',\n", + " 'id': '4Jb4PDWREzNnbZcOHPcZPy'}]" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "search_playlists" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "ebf91bb0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'BEST HITS 2026 🔥 Popular Songs 🎵 Top Summer Music Hits 🎵 ',\n", + " 'id': '5KJDMJe9EJ7QRz8FG2MIpI'}" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chosen_playlist = search_playlists[0]\n", + "chosen_playlist" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "59fbef44", + "metadata": {}, "outputs": [], "source": [ - "# Your answer here" + "playlist_details = sp.playlist(chosen_playlist[\"id\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "1484188b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Name: BEST HITS 2026 🔥 Popular Songs 🎵 Top Summer Music Hits 🎵 \n", + "Description: Best Hits 2026 | Popular Songs 2026 | Top Music Hits | Today Top Hits | Viral Hits | Radio Hits 2025/2026 | Summer Music Mix 2026 | Best Of 2026 | Summer Vibes | Deep House | Pop Vibes | Top Songs | Top Music | Global Hits | Best Songs| Clean | Pop Hits | Viral Pop - updated and active! :)\n" + ] + } + ], + "source": [ + "print(\"Name:\", playlist_details[\"name\"])\n", + "print(\"Description:\", playlist_details[\"description\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "eafa57e9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict_keys(['collaborative', 'description', 'external_urls', 'followers', 'href', 'id', 'images', 'name', 'owner', 'primary_color', 'public', 'snapshot_id', 'type', 'uri'])" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "playlist_details.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "502b4216", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "HTTP Error for GET to https://api.spotify.com/v1/playlists/5KJDMJe9EJ7QRz8FG2MIpI/items with Params: {'limit': 10, 'offset': 0, 'fields': None, 'market': None, 'additional_types': 'track,episode'} returned 403 due to Forbidden\n" + ] + }, + { + "ename": "SpotifyException", + "evalue": "http status: 403, code: -1 - https://api.spotify.com/v1/playlists/5KJDMJe9EJ7QRz8FG2MIpI/items?limit=10&offset=0&additional_types=track%2Cepisode:\n Forbidden, reason: None", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mHTTPError\u001b[39m Traceback (most recent call last)", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:271\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 266\u001b[39m response = \u001b[38;5;28mself\u001b[39m._session.request(\n\u001b[32m 267\u001b[39m method, url, headers=headers, proxies=\u001b[38;5;28mself\u001b[39m.proxies,\n\u001b[32m 268\u001b[39m timeout=\u001b[38;5;28mself\u001b[39m.requests_timeout, **args\n\u001b[32m 269\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m271\u001b[39m \u001b[30;43mresponse\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mraise_for_status\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 272\u001b[39m results = response.json()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/requests/models.py:1167\u001b[39m, in \u001b[36mResponse.raise_for_status\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 1166\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m http_error_msg:\n\u001b[32m-> \u001b[39m\u001b[32m1167\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m HTTPError(http_error_msg, response=\u001b[38;5;28mself\u001b[39m)\n", + "\u001b[31mHTTPError\u001b[39m: 403 Client Error: Forbidden for url: https://api.spotify.com/v1/playlists/5KJDMJe9EJ7QRz8FG2MIpI/items?limit=10&offset=0&additional_types=track%2Cepisode", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[31mSpotifyException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[51]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m playlist_items = sp.playlist_items(chosen_playlist[\u001b[33m\"id\"\u001b[39m], limit=\u001b[32m10\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:742\u001b[39m, in \u001b[36mSpotify.playlist_items\u001b[39m\u001b[34m(self, playlist_id, fields, limit, offset, market, additional_types)\u001b[39m\n\u001b[32m 730\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\" Get full details of the tracks and episodes of a playlist.\u001b[39;00m\n\u001b[32m 731\u001b[39m \n\u001b[32m 732\u001b[39m \u001b[33;03m Parameters:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 739\u001b[39m \u001b[33;03m valid types are: track and episode\u001b[39;00m\n\u001b[32m 740\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 741\u001b[39m plid = \u001b[38;5;28mself\u001b[39m._get_id(\u001b[33m\"\u001b[39m\u001b[33mplaylist\u001b[39m\u001b[33m\"\u001b[39m, playlist_id)\n\u001b[32m--> \u001b[39m\u001b[32m742\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_get\u001b[39;49m\u001b[30;43m(\u001b[39;49m\n\u001b[32m 743\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mf\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mplaylists/\u001b[39;49m\u001b[30;43;01m{\u001b[39;49;00m\u001b[30;43mplid\u001b[39;49m\u001b[30;43;01m}\u001b[39;49;00m\u001b[30;43m/items\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 744\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mlimit\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 745\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43moffset\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 746\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mfields\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mfields\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 747\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43mmarket\u001b[39;49m\u001b[30;43m,\u001b[39;49m\n\u001b[32m 748\u001b[39m \u001b[30;43m \u001b[39;49m\u001b[30;43madditional_types\u001b[39;49m\u001b[30;43m=\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mjoin\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43madditional_types\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 749\u001b[39m \u001b[30;43m\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:321\u001b[39m, in \u001b[36mSpotify._get\u001b[39m\u001b[34m(self, url, args, payload, **kwargs)\u001b[39m\n\u001b[32m 318\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m args:\n\u001b[32m 319\u001b[39m kwargs.update(args)\n\u001b[32m--> \u001b[39m\u001b[32m321\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[30;43mself\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m_internal_call\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43mGET\u001b[39;49m\u001b[30;43m\"\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43murl\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mpayload\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43mkwargs\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/anaconda3/lib/python3.13/site-packages/spotipy/client.py:291\u001b[39m, in \u001b[36mSpotify._internal_call\u001b[39m\u001b[34m(self, method, url, payload, params)\u001b[39m\n\u001b[32m 286\u001b[39m reason = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 288\u001b[39m logger.error(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mHTTP Error for \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmethod\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00murl\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m with Params: \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 289\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00margs.get(\u001b[33m'\u001b[39m\u001b[33mparams\u001b[39m\u001b[33m'\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m returned \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.status_code\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m due to \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m291\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m SpotifyException(\n\u001b[32m 292\u001b[39m response.status_code,\n\u001b[32m 293\u001b[39m -\u001b[32m1\u001b[39m,\n\u001b[32m 294\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresponse.url\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m:\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mmsg\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m,\n\u001b[32m 295\u001b[39m reason=reason,\n\u001b[32m 296\u001b[39m headers=response.headers,\n\u001b[32m 297\u001b[39m )\n\u001b[32m 298\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m requests.exceptions.RetryError \u001b[38;5;28;01mas\u001b[39;00m retry_error:\n\u001b[32m 299\u001b[39m request = retry_error.request\n", + "\u001b[31mSpotifyException\u001b[39m: http status: 403, code: -1 - https://api.spotify.com/v1/playlists/5KJDMJe9EJ7QRz8FG2MIpI/items?limit=10&offset=0&additional_types=track%2Cepisode:\n Forbidden, reason: None" + ] + } + ], + "source": [ + "playlist_items = sp.playlist_items(chosen_playlist[\"id\"], limit=10)" + ] + }, + { + "cell_type": "markdown", + "id": "e0bf5c6d", + "metadata": {}, + "source": [ + "Spotify API compatibility note: This exercise was originally designed for an earlier version of the Spotify Web API. Since then, Spotify has introduced restrictions and removed several endpoints used by the original exercise. In particular, the Artist's Top Tracks endpoint was removed in February 2026, while access to Related Artists and Featured Playlists has been restricted for new Web API applications. Playlist item retrieval is also restricted to playlists owned by or collaborated on by the current user. Therefore, some of the original exercise requirements could not be completed using a new Spotify Development Mode application. Where possible, the implementation was adapted using the currently available Search endpoint. These adaptations are clearly identified in the notebook." ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "base", "language": "python", "name": "python3" }, @@ -558,7 +1585,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.13.9" } }, "nbformat": 4,