# How to Integrate Bolt.new with Spotify

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 30 minutes
- Last updated: April 2026

## TL;DR

Connect Bolt.new to the Spotify Web API to build music search, playlist displays, and now-playing widgets. Use Client Credentials flow for public data (search, artist info) — this works in Bolt's WebContainer during development. For user-specific features like playlists and currently playing, use Authorization Code flow with OAuth, which requires a deployed callback URL. Store your Spotify client secret in environment variables, never in client code.

## Build Music Features in Bolt.new with the Spotify Web API

The Spotify Web API gives developers access to Spotify's entire music catalog and user data through standard HTTP calls — a perfect fit for Bolt.new's WebContainer architecture. You can search 100 million tracks, fetch artist bios and album art, read playlist contents, and with user authorization, display what a user is currently listening to and control playback.

The API has two authorization modes with different development implications. Client Credentials flow authenticates as your app (not as a user) and works for public data: search, browsing catalog, fetching track metadata, and getting audio features like tempo and energy. This flow exchanges your client ID and secret for a token via a server-side API call, and it works perfectly in Bolt's WebContainer preview — no deployment required for initial development.

Authorization Code flow authenticates as a specific Spotify user, enabling access to their playlists, listening history, currently playing track, and playback controls. This flow requires redirecting the user to Spotify's authorization page and then receiving a callback at a URL you register in the Spotify Developer Dashboard. That callback URL must be a stable, public domain — which means Bolt's dynamic WebContainer preview URL does not work. Deploy to Netlify or Bolt Cloud first, register the callback URL, then test OAuth. Plan for this two-phase development approach from the start and avoid spending time debugging OAuth in the Bolt preview.

## Before you start

- A Spotify account (free or premium) at open.spotify.com
- A Spotify Developer app created at developer.spotify.com/dashboard — note the Client ID and Client Secret
- A Bolt.new account with a new Next.js project open
- A Netlify account for deploying and testing OAuth callback URLs
- For user OAuth features: add http://localhost:3000/api/spotify/callback in Spotify Dashboard Redirect URIs for local testing, and your deployed Netlify URL for production

## Step-by-step guide

### 1. Register a Spotify Developer app and get your credentials

Before writing any code, create a Spotify application in the Developer Dashboard. Go to developer.spotify.com/dashboard and log in with your Spotify account. Click 'Create App' and fill in a name (anything descriptive), description, and website URL (you can use your Bolt project URL or a placeholder). For Redirect URIs, add two entries now: `http://localhost:3000/api/spotify/callback` for local testing and a placeholder for your future deployed URL like `https://your-app.netlify.app/api/spotify/callback` — you can update this after deployment.

After creating the app, click 'Settings' to find your Client ID. Click 'View Client Secret' to reveal the secret — copy both values. Your Client ID is safe to expose publicly (it identifies your app), but your Client Secret must never appear in client-side code. It will only be used in your Next.js API routes.

For the API scopes, decide upfront which features you need. Public catalog access (search, albums, artists, playlists) requires no user scopes and uses Client Credentials. User features require specific scopes: `user-read-currently-playing` and `user-read-playback-state` for the Now Playing widget, `playlist-read-private` for private playlists, `user-top-read` for top artists and tracks. Requesting only the scopes you need reduces user friction during the authorization consent screen.

**Expected result:** A Spotify Developer app with Client ID and Client Secret, and a Bolt.new Next.js project with environment variable placeholders in .env.local.

### 2. Build the Client Credentials API route for public Spotify data

Client Credentials flow is the simpler of the two authorization flows and works perfectly in Bolt's WebContainer. You exchange your client ID and secret for an access token with no user interaction — the token is scoped to public catalog data only. This token expires after 3,600 seconds (1 hour), so your API route should cache it and refresh automatically.

Create a token manager module that caches the current token in memory and fetches a new one when it expires. Then create a search API route that calls the Spotify search endpoint with your cached token. The Spotify search endpoint accepts a `q` parameter (the search query) and a `type` parameter specifying what to search (track, album, artist, playlist — comma-separated for multiple).

All Spotify API calls from your Next.js API routes work in Bolt's WebContainer preview — outbound HTTP requests are fully supported. The preview cannot receive incoming webhooks or OAuth callbacks, but for read-only Spotify data lookups, the WebContainer is a complete development environment. Test your search endpoint in the preview before building the UI.

```
// lib/spotify.ts
interface SpotifyToken {
  access_token: string;
  expires_at: number;
}

let cachedToken: SpotifyToken | null = null;

export async function getAccessToken(): Promise<string> {
  if (cachedToken && Date.now() < cachedToken.expires_at - 60000) {
    return cachedToken.access_token;
  }

  const clientId = process.env.SPOTIFY_CLIENT_ID!;
  const clientSecret = process.env.SPOTIFY_CLIENT_SECRET!;
  const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

  const response = await fetch('https://accounts.spotify.com/api/token', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${credentials}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: 'grant_type=client_credentials',
  });

  if (!response.ok) {
    throw new Error(`Spotify token error: ${response.status}`);
  }

  const data = await response.json();
  cachedToken = {
    access_token: data.access_token,
    expires_at: Date.now() + data.expires_in * 1000,
  };

  return cachedToken.access_token;
}

export async function searchTracks(query: string, limit = 20) {
  const token = await getAccessToken();
  const params = new URLSearchParams({
    q: query,
    type: 'track,artist',
    limit: String(limit),
  });

  const response = await fetch(`https://api.spotify.com/v1/search?${params}`, {
    headers: { 'Authorization': `Bearer ${token}` },
  });

  if (!response.ok) {
    throw new Error(`Spotify search error: ${response.status}`);
  }

  return response.json();
}

export async function getPlaylist(playlistId: string) {
  const token = await getAccessToken();
  const response = await fetch(
    `https://api.spotify.com/v1/playlists/${playlistId}?fields=id,name,description,images,tracks.items(track(id,name,artists,duration_ms,preview_url,album(images)))`,
    { headers: { 'Authorization': `Bearer ${token}` } }
  );
  if (!response.ok) throw new Error(`Spotify playlist error: ${response.status}`);
  return response.json();
}
```

**Expected result:** A /api/spotify/search endpoint that returns track search results from Spotify, testable directly in the Bolt WebContainer preview.

### 3. Build the music search UI with audio previews

With the search API route working, build the React search interface. The UI centers around a debounced search input, a results grid showing album art and track metadata, and inline audio preview playback. Spotify provides 30-second MP3 preview clips for most tracks via the `preview_url` field — these work in standard HTML audio elements with no Spotify SDK required.

Implement debouncing on the search input (300ms delay) to avoid firing API calls on every keystroke. Use React's `useState` and `useEffect` for the debounce pattern, or install the `use-debounce` package if available. The results grid shows album art (use the smallest image from `album.images` array for thumbnails), track name, artist names (comma-joined), and album name.

For the audio preview player: use a single `<audio>` element managed by React ref — only one track plays at a time, and clicking a new track stops the current one. Show a play/pause toggle button on each track card, and a mini player at the bottom of the page showing the currently playing track with the album art and a progress bar.

Note: preview_url is null for some tracks (particularly in certain regions or for very new releases). Handle this gracefully by hiding the play button for tracks without a preview URL.

```
// app/api/spotify/search/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { searchTracks } from '@/lib/spotify';

export async function GET(request: NextRequest) {
  const query = request.nextUrl.searchParams.get('q');
  if (!query || query.trim().length < 2) {
    return NextResponse.json({ tracks: { items: [] } });
  }

  try {
    const data = await searchTracks(query.trim());
    return NextResponse.json(data);
  } catch (error) {
    console.error('Spotify search error:', error);
    return NextResponse.json(
      { error: 'Failed to search Spotify' },
      { status: 500 }
    );
  }
}
```

**Expected result:** A working music search interface with album art, track metadata, and 30-second audio preview playback, running in Bolt's WebContainer preview.

### 4. Implement OAuth Authorization Code flow for user-specific features

For user-specific Spotify data — currently playing, private playlists, listening history, playback controls — you need Authorization Code flow with user consent. This requires implementing three API routes: a login initiator that redirects to Spotify, a callback handler that exchanges the code for tokens, and a token refresh route for keeping sessions alive.

The critical constraint: Bolt.new's WebContainer preview has a dynamic URL that changes between sessions and cannot be registered as a Spotify redirect URI. Authorization Code flow OAuth callbacks will not work in the Bolt preview. You must deploy to Netlify first, then register the deployed URL (`https://your-app.netlify.app/api/spotify/callback`) in your Spotify app settings.

Store the refresh token in an HTTP-only cookie — this is more secure than localStorage and works correctly with Next.js server components. The refresh token is long-lived (until the user revokes access) and lets you get new access tokens without re-prompting the user. Store access tokens in memory (module-level cache or Redis for production) since they expire every hour.

For PKCE (Proof Key for Code Exchange): Spotify supports PKCE as an alternative to client secret for the Authorization Code flow. PKCE is appropriate for pure client-side apps with no server. Since your Bolt app has Next.js API routes, use the standard Authorization Code flow with client secret on the server — it is more secure and better supported.

```
// app/api/spotify/callback/route.ts
import { NextRequest, NextResponse } from 'next/server';

export async function GET(request: NextRequest) {
  const { searchParams } = request.nextUrl;
  const code = searchParams.get('code');
  const state = searchParams.get('state');
  const error = searchParams.get('error');

  if (error) {
    return NextResponse.redirect(new URL('/?error=spotify_denied', request.url));
  }

  const storedState = request.cookies.get('spotify_auth_state')?.value;
  if (!state || state !== storedState) {
    return NextResponse.redirect(new URL('/?error=state_mismatch', request.url));
  }

  const clientId = process.env.SPOTIFY_CLIENT_ID!;
  const clientSecret = process.env.SPOTIFY_CLIENT_SECRET!;
  const redirectUri = process.env.SPOTIFY_REDIRECT_URI!;
  const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

  const tokenResponse = await fetch('https://accounts.spotify.com/api/token', {
    method: 'POST',
    headers: {
      'Authorization': `Basic ${credentials}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      code: code!,
      redirect_uri: redirectUri,
    }),
  });

  if (!tokenResponse.ok) {
    return NextResponse.redirect(new URL('/?error=token_exchange_failed', request.url));
  }

  const tokens = await tokenResponse.json();
  const response = NextResponse.redirect(new URL('/', request.url));

  // Store refresh token in HTTP-only cookie (7 days)
  response.cookies.set('spotify_refresh_token', tokens.refresh_token, {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    maxAge: 7 * 24 * 60 * 60,
    path: '/',
  });

  // Clear the state cookie
  response.cookies.delete('spotify_auth_state');

  return response;
}
```

**Expected result:** OAuth login flow that redirects to Spotify authorization, handles the callback, stores the refresh token in a cookie, and enables user-specific API calls on the deployed site.

### 5. Deploy to Netlify and register the production callback URL

With both Client Credentials (search) and Authorization Code (user features) implemented, deploy to Netlify for full end-to-end testing of the OAuth flow. In Bolt.new, click the Deploy button, connect to Netlify via OAuth, and complete the deployment. Your app will get a `*.netlify.app` subdomain.

After deployment, go to Netlify's dashboard → Site Configuration → Environment Variables and add: `SPOTIFY_CLIENT_ID`, `SPOTIFY_CLIENT_SECRET`, and `SPOTIFY_REDIRECT_URI` (set this to `https://your-site.netlify.app/api/spotify/callback`). Trigger a redeploy for the new environment variables to take effect.

Now register the production callback URL in Spotify: go to developer.spotify.com/dashboard → your app → Settings → Redirect URIs → Add URI → enter your Netlify callback URL and save. You should now have both `http://localhost:3000/api/spotify/callback` and `https://your-site.netlify.app/api/spotify/callback` in the list.

Test the full OAuth flow on the deployed site: visit your Netlify URL, click the Spotify login button, authorize the app, and confirm you are redirected back to your app with user data loading. The now-playing widget will show 'Nothing playing' if you are not listening to Spotify — open Spotify on your phone or desktop and start a song to verify the widget works.

Note: during development in Bolt's WebContainer, outbound API calls to Spotify work (search, catalog browsing), but OAuth callbacks cannot reach the WebContainer's dynamic preview URL. This is a WebContainer limitation — always test OAuth on the deployed Netlify URL.

```
# netlify.toml
[build]
  command = "npm run build"
  publish = ".next"

[[plugins]]
  package = "@netlify/plugin-nextjs"

[build.environment]
  NODE_VERSION = "20"

# Headers for security
[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-Content-Type-Options = "nosniff"
```

**Expected result:** A fully deployed Spotify app on Netlify with working search (Client Credentials) and user OAuth flow for personalized features.

## Best practices

- Cache Client Credentials tokens in memory with an expiry check — avoid fetching a new token on every API request, as this doubles your response time and wastes Spotify API quota
- Use Client Credentials flow for all public catalog data (search, albums, playlists with known IDs) and only request user OAuth when the feature specifically needs personal data
- Always store the Spotify Client Secret in environment variables and access it only from API routes — never from client-side React components or NEXT_PUBLIC_ prefixed variables
- Register both localhost and your deployed domain in Spotify's Redirect URIs simultaneously so you can test OAuth in both environments without changing settings
- Handle the case where preview_url is null (about 10-15% of tracks) by hiding the play button rather than showing a broken audio element
- Use Spotify's pagination (offset and limit parameters) for playlist tracks — playlists can have thousands of tracks, and the default limit is 100 items per request
- Display Spotify's branding guidelines-compliant logos when building public apps — Spotify's developer terms require crediting Spotify in apps that use their data
- For the Web Playback SDK, warn users that playback control requires a Spotify Premium account — the SDK will fail silently for free tier users

## Use cases

### Music Search and Discovery App

Build a Spotify-powered music search interface that lets users search for tracks, albums, and artists and display results with album art, audio previews, and Spotify links. Uses Client Credentials flow — fully testable in Bolt's WebContainer without deployment.

Prompt example:

```
Build a Spotify music search app in Next.js. Create an API route at /api/spotify/token that exchanges my Spotify client ID and secret (from environment variables SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET) for a Client Credentials access token. Create another API route at /api/spotify/search that accepts a query string parameter, calls the Spotify search endpoint for tracks and artists, and returns results. Build a search page with an input field, call the search API on each keystroke (debounced 300ms), and display results in a grid showing album art, track name, artist name, and a play preview button using the preview_url. Use Tailwind CSS with a dark theme.
```

### Now Playing Widget for Personal Dashboard

Add a live 'Now Playing' widget to a personal dashboard app that shows the authenticated user's current Spotify track with album art, track name, artist, and a progress bar. Requires Authorization Code OAuth — needs a deployed URL for the callback.

Prompt example:

```
Add a Spotify Now Playing widget to my Next.js app. Implement the Spotify Authorization Code OAuth flow: create /api/spotify/login that redirects to Spotify's authorization URL requesting user-read-currently-playing and user-read-playback-state scopes. Create /api/spotify/callback that exchanges the code for tokens and stores the refresh token in an HTTP-only cookie. Create /api/spotify/now-playing that uses the stored token to call the Spotify currently-playing endpoint and returns track name, artist, album art URL, duration, and progress. Build a NowPlayingWidget component that polls this endpoint every 5 seconds and displays the current track with a progress bar.
```

### Playlist Showcase Page

Build a public playlist showcase that displays a curated set of Spotify playlists with track listings, perfect for music blogs, event pages, or personal portfolio sites. Uses Client Credentials with known playlist IDs — no user OAuth required.

Prompt example:

```
Build a Spotify playlist showcase page in Next.js. Create an API route at /api/spotify/playlist/[playlistId] that fetches a Spotify playlist by ID using Client Credentials auth and returns the playlist name, description, cover image, and up to 50 tracks with their names, artists, and duration. Create a PlaylistCard component that displays this data with the playlist cover as a background, track count, and an expandable track list. Add a page that shows 3 hardcoded playlist IDs in a responsive grid.
```

## Troubleshooting

### Getting 'INVALID_CLIENT: Invalid redirect URI' error when trying to log in with Spotify

Cause: The SPOTIFY_REDIRECT_URI environment variable does not exactly match one of the Redirect URIs registered in the Spotify Developer Dashboard. Even a trailing slash difference or http vs https mismatch causes this error.

Solution: Go to developer.spotify.com/dashboard → your app → Settings and compare the registered Redirect URIs with your SPOTIFY_REDIRECT_URI env var character by character. They must be identical. After updating either the Dashboard or the env var, redeploy on Netlify for the change to take effect.

```
// Check your registered URI matches exactly:
// Dashboard: https://your-app.netlify.app/api/spotify/callback
// Env var:   SPOTIFY_REDIRECT_URI=https://your-app.netlify.app/api/spotify/callback
// NOT:       SPOTIFY_REDIRECT_URI=https://your-app.netlify.app/api/spotify/callback/
```

### Spotify search works in development but returns 401 Unauthorized errors after deployment

Cause: The SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET environment variables are not set in the Netlify dashboard, so the token request fails and all subsequent API calls return 401.

Solution: In Netlify dashboard, go to Site Configuration → Environment Variables and add SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET. Trigger a redeploy (Deploys → Trigger deploy → Deploy site) to make the variables available to the build. Confirm by checking the deploy logs for any 'undefined' values in authentication.

### The Now Playing widget always shows 'Nothing playing' even when Spotify is active

Cause: Three possible causes: (1) the user is not the currently authenticated user in the app, (2) the access token expired and the refresh token flow failed, or (3) the user is listening on a private session in Spotify.

Solution: Check the /api/spotify/now-playing endpoint directly in the browser. If it returns a 204 (No Content), Spotify correctly says nothing is playing — this is the expected response when the user has no active device or just paused. If it returns 401, the token refresh is failing — check that the spotify_refresh_token cookie is being set correctly. Users in a private Spotify session will always return 204.

```
// app/api/spotify/now-playing/route.ts — handle 204 explicitly
const spotifyResponse = await fetch('https://api.spotify.com/v1/me/player/currently-playing', {
  headers: { 'Authorization': `Bearer ${accessToken}` },
});

// 204 = no active playback (not an error)
if (spotifyResponse.status === 204) {
  return NextResponse.json({ isPlaying: false });
}

if (!spotifyResponse.ok) {
  return NextResponse.json({ error: 'Playback unavailable' }, { status: spotifyResponse.status });
}
```

### Track preview audio plays correctly on desktop but fails on iOS Safari with no sound

Cause: iOS Safari requires a direct user gesture (tap) to initiate audio playback. If the audio element's .play() call happens in a setTimeout, async callback, or any context removed from the direct user interaction event, iOS blocks it.

Solution: Ensure the audio .play() call happens synchronously within the click event handler, not inside an async function that awaits anything before calling play. Create the Audio object and call play() in the same synchronous execution context as the user click.

```
// WRONG - iOS blocks this (async before play):
const handlePlay = async (previewUrl: string) => {
  const track = await fetchTrackDetails(previewUrl); // iOS blocks play() after this await
  audioRef.current?.play();
};

// CORRECT - play() in direct click handler:
const handlePlay = (previewUrl: string) => {
  if (audioRef.current) {
    audioRef.current.src = previewUrl;
    audioRef.current.play(); // synchronous in click handler — iOS allows this
  }
};
```

## Frequently asked questions

### Can I test Spotify API calls in Bolt.new's WebContainer preview without deploying?

Yes, for Client Credentials flow (public data like search and catalog). Outbound HTTP calls to Spotify's API work in Bolt's WebContainer. You can build and test search, album browsing, and playlist display entirely in the preview. Authorization Code OAuth requires a deployed URL for the callback and cannot be tested in the WebContainer preview.

### Does the Spotify Web Playback SDK work in Bolt.new?

The Web Playback SDK should load in the WebContainer since it is a JavaScript library. However, full testing requires a deployed environment and a Spotify Premium account. The SDK requires a valid access token from a Premium user — attempting to initialize it with a free account token will result in an authentication error. For development, use the preview_url approach (30-second MP3 clips) which works without Premium.

### How do I handle Spotify API rate limits in my Bolt app?

Spotify does not publish exact rate limits but enforces them with 429 Too Many Requests responses that include a Retry-After header in seconds. Cache access tokens to avoid unnecessary token requests, cache search results with a short TTL (30-60 seconds), and add exponential backoff retry logic in your API routes. For most small apps the limits are generous — you are unlikely to hit them unless making hundreds of requests per minute.

### Can I use Spotify API without a Spotify Premium account?

Yes, for most read-only features. Client Credentials flow and Authorization Code flow both work with free Spotify accounts. The only Premium-exclusive feature is the Web Playback SDK, which controls in-browser playback. Search, catalog browsing, playlist reading, and the currently-playing endpoint all work with free accounts. The 30-second preview_url clips also work without Premium.

### How do I deploy a Bolt.new Spotify app and register the callback URL?

Click Deploy in Bolt.new and connect to Netlify. After deployment, you will get a Netlify URL like your-app.netlify.app. Copy that URL, go to developer.spotify.com/dashboard, select your app, click Settings, and add https://your-app.netlify.app/api/spotify/callback to the Redirect URIs list. Then update your SPOTIFY_REDIRECT_URI environment variable in Netlify's dashboard to match this URL and trigger a redeploy.

### Can I build a Spotify bot.new with Bolt.new?

Yes for API features, with limitations. Bolt.new can build apps that search Spotify, display music data, and trigger playback via the API. Real-time features like live playlist collaboration or instant now-playing updates require WebSockets or polling. During development in the WebContainer, outbound Spotify calls work — but incoming webhooks (like Spotify's webhook for playback events) require a deployed URL.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/spotify-api
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/spotify-api
