node.jsexpressspotify

401 error: Spotify web API upload custom image to playlist


I've been stuck on this for some time now and no matter how hard I search I can't find an answer. I'm trying to add a custom playlist cover to a Spotify playlist but I keep getting a response of 401. I've checked many times that I'm passing the correct access token and playlist ID. I added all the required scope as pointed out on Spotify's API too. I've even tested different images with different KB sizes. Below is my code

const url = `https://api.spotify.com/v1/playlists/${playlist.id}/images`;
        let imgFile = process.env.COVER_PHOTO;
        axios.put(url, imgFile, {
            headers: {
            'Authorization': "Bearer " + token,
            'Content-Type': 'image/jpeg'
            }
        })

Solution

  • Error 401 means Unauthorized your token. Not collect assign scopes

    OR Not get token by Authorization Code Flow

    Image Update API

    URL

    PUT /playlists/{playlist_id}/images
    

    Scopes

    ugc-image-upload
    playlist-modify-public
    playlist-modify-private
    

    enter image description here

    In here

    demo.js

    const fs = require('fs'); - Adds the ability to read and write files in the program.

    const imageData = fs.readFileSync('car.jpg', { encoding: 'base64' }); - Reads the 'car.jpg' file and converts it to a Base64 string.

    const accessToken = await getToken(code); - Retrieves an access token using a provided code.

    const updateStatus = await updatePlaylistCoverImage(accessToken, imageData); - Sends the encoded image to Spotify to update the playlist cover.

    response.send(Image Update Status: ${updateStatus}); - Responds with the status of the image upload process.

    const express = require("express");
    const axios = require('axios');
    const cors = require("cors");
    const fs = require('fs'); // Required to read files
    
    const app = express();
    app.use(cors());
    
    const CLIENT_ID = "{your client ID}";
    const CLIENT_SECRET = "{your client secret}";
    const PORT = 3000; // {your redirect URI port number}
    const REDIRECT_URI = `http://localhost:${PORT}/callback`;
    const SCOPE = 'ugc-image-upload playlist-modify-public playlist-modify-private';
    const PLAYLIST_ID = '{Your playlist ID}'; // Your playlist ID
    
    const getToken = async (code) => {
        try {
            const resp = await axios.post(
                'https://accounts.spotify.com/api/token',
                new URLSearchParams({
                    'grant_type': 'authorization_code',
                    'redirect_uri': REDIRECT_URI,
                    'code': code
                }),
                {
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    auth: {
                        username: CLIENT_ID,
                        password: CLIENT_SECRET
                    }
                })
            return Promise.resolve(resp.data.access_token);
        } catch (err) {
            console.error(err)
            return Promise.reject(err)
        }
    }
    
    const updatePlaylistCoverImage = async (accessToken, imageData) => {
        try {
            const response = await axios.put(
                `https://api.spotify.com/v1/playlists/${PLAYLIST_ID}/images`,
                imageData,
                {
                    headers: {
                        'Authorization': `Bearer ${accessToken}`,
                        'Content-Type': 'image/jpeg'
                    }
                }
            );
            return response.status;
        } catch (error) {
            console.error(error);
            return error.response.status;
        }
    }
    
    app.get("/login", (request, response) => {
        const redirect_url = `https://accounts.spotify.com/authorize?response_type=code&client_id=${CLIENT_ID}&scope=${encodeURIComponent(SCOPE)}&state=123456&redirect_uri=${REDIRECT_URI}&prompt=consent`
        response.redirect(redirect_url);
    });
    
    app.get("/callback", async (request, response) => {
        const code = request.query["code"];
        try {
            const accessToken = await getToken(code);
            const imageData = fs.readFileSync('car.jpg', { encoding: 'base64' });
            const updateStatus = await updatePlaylistCoverImage(accessToken, imageData);
            response.send(`Image Update Status: ${updateStatus}`);
        } catch (error) {
            response.status(500).send(`Error: ${error.message}`);
        }
    });
    
    app.listen(PORT, () => {
        console.log(`Listening on :${PORT}`);
    });
    

    custom image car.jpg Base64 encoded JPEG image data, maximum payload size is 256 KB. enter image description here

    Access

    http://localhost:{your port}/login
    

    Before enter image description here

    After enter image description here