SoundCloud's developer API has been effectively closed since 2017 — new app registrations have been suspended for years and existing credentials are difficult to obtain. The available paths are: (1) SoundCloud's oEmbed endpoint for embedding players (no auth required), (2) the Widget API for controlling embedded players via JavaScript. For a fully capable music API, Spotify's API is a far more viable alternative.
SoundCloud Integration in Bolt.new: What Still Works and What Doesn't
SoundCloud was once home to a thriving developer ecosystem — in its heyday, third-party apps built entirely on the SoundCloud API powered music discovery, DJ tools, and social listening experiences. In 2017, SoundCloud suspended new application registrations for its developer API following company restructuring, cutting off access to the endpoints that allowed searching tracks, streaming audio, accessing user libraries, and uploading content. Seven years later, this closure remains in effect. The developer portal at developers.soundcloud.com still exists, but the 'Register a new app' path leads to a waitlist that has never reopened. Existing applications with client IDs that were registered before 2017 still work — but they are untransferable and tied to the original registered account.
This is not a temporary situation or a solvable configuration problem — it is a deliberate policy decision by SoundCloud. Any tutorial or library claiming to offer full SoundCloud API access for new developers is either exploiting undocumented internal endpoints (which break without notice) or is outdated. Using undocumented endpoints violates SoundCloud's Terms of Service and risks your app being blocked.
Despite the closed API, two legitimate capabilities remain accessible to all developers. First, SoundCloud's oEmbed endpoint is fully public and requires no authentication. Given any public SoundCloud track or playlist URL, the oEmbed endpoint returns standardized embeddable HTML code — an iframe with a SoundCloud player. This approach is limited but legitimate and stable. Second, SoundCloud's Widget API is a JavaScript library that lets you programmatically control an embedded SoundCloud player (play, pause, seek, get current time, listen for events). Combined with oEmbed, you can build React components that embed and control SoundCloud audio.
For teams who need a fully capable music API — searching tracks, accessing user libraries, controlling playback programmatically, accessing audio features — Spotify's Web API is the gold standard. Spotify has an active developer program, generous free tier, comprehensive documentation, and SDKs for every major platform. If your use case requires more than embedding specific public SoundCloud tracks, we strongly recommend the Spotify integration path described at the end of this guide.
Integration method
SoundCloud's full REST API (tracks, users, streams) requires a client ID that is no longer available to new developers through any official registration process. Two paths remain: the oEmbed endpoint (GET https://soundcloud.com/oembed?url=TRACK_URL) works without authentication and returns embeddable iframe HTML, and the SoundCloud Widget API allows JavaScript control of embedded players. For a robust music integration, Spotify API is the recommended alternative.
Prerequisites
- Public SoundCloud track or playlist URLs for the content you want to embed — oEmbed works with any public SoundCloud URL
- Understanding that SoundCloud's full REST API (search, streams, user libraries) is not available to new developers — this guide covers only the oEmbed and Widget API paths
- A Bolt.new project using Next.js (for the API route that proxies oEmbed requests to avoid CORS issues)
- If you need a full music API: a Spotify developer account at developer.spotify.com — Spotify's API is the recommended alternative
- No API key required for the oEmbed and Widget API approaches covered in this guide
Step-by-step guide
Understand the SoundCloud API situation and choose the right path
Understand the SoundCloud API situation and choose the right path
Before writing any code, take a moment to understand what is and is not possible with SoundCloud in 2026. This will save you time and prevent frustration from pursuing paths that do not work. SoundCloud's developer API (the API that allowed searching tracks, getting user libraries, accessing waveform data, and controlling streaming) was closed to new registrations in 2017. The suspension has never been lifted. If you search GitHub for SoundCloud API libraries and find Python or Node.js packages that claim to provide full API access, these libraries exploit unofficial internal endpoints discovered through reverse engineering. These endpoints are not documented, not officially supported, and can be blocked or changed at any time without notice. Using them violates SoundCloud's Terms of Service. Multiple popular SoundCloud scraping libraries have been broken overnight when SoundCloud updated their internal authentication. Here is exactly what does work legitimately in 2026. The oEmbed endpoint is an international standard for embedding content across platforms. SoundCloud maintains a public oEmbed endpoint at https://soundcloud.com/oembed that accepts any public SoundCloud URL and returns JSON with an html field containing an iframe embed code. No authentication is required. This works for any public track or playlist URL. Limitation: you must already know the track URL — this is not a search endpoint. The Widget API is a JavaScript library at https://w.soundcloud.com/player/api.js that lets you interact with a SoundCloud embedded player. Once a track is embedded as an iframe using the standard SoundCloud embed URL format (https://w.soundcloud.com/player/?url=TRACK_URL), the Widget API lets you call play(), pause(), seekTo(), and listen to events like PLAY, PAUSE, PLAY_PROGRESS, and FINISH. This enables building custom player UIs around the embedded SoundCloud player. If your use case requires any of the following, SoundCloud's available paths will not work and you need Spotify: searching for tracks by keyword, fetching a user's liked tracks or follows, accessing audio features or waveform data, discovering related tracks, building a music recommendation engine, or allowing users to log in with their SoundCloud account. Decide now which path fits your use case. If you have specific track URLs and just need to embed players, proceed with this guide. If you need any discovery or user data features, jump to the Spotify alternative section at the end.
Pro tip: If you encounter a GitHub repository promising full SoundCloud API access for new developers, check the last commit date and the issues section. Most such projects have been unmaintained since 2020 and have open issues reporting broken authentication.
Expected result: You have decided which integration path is appropriate: oEmbed+Widget API for embedding specific known tracks, or Spotify API for full music data and discovery capabilities.
Use the SoundCloud oEmbed endpoint to embed tracks
Use the SoundCloud oEmbed endpoint to embed tracks
The SoundCloud oEmbed endpoint is the most reliable way to embed SoundCloud audio in a Bolt.new app. It is maintained by SoundCloud as a standard feature, requires no authentication, and works for any public SoundCloud track, playlist, or set URL. The oEmbed endpoint is: GET https://soundcloud.com/oembed. It accepts these query parameters: url (required — the SoundCloud content URL, URL-encoded), format (json or xml — use json), auto_play (true or false), hide_related (true or false), show_comments (true or false), show_user (true or false), show_reposts (true or false), color (hex color without # for the player color). The response JSON contains: html (the complete iframe embed code), width, height, title, description, thumbnail_url, and author_name. In Bolt.new, calling oEmbed directly from a React component would cause CORS errors because the SoundCloud oEmbed endpoint does not include CORS headers for arbitrary origins. The solution is to create a Next.js API route that proxies the oEmbed request — the API route runs server-side where CORS restrictions do not apply. Create the API route at app/api/soundcloud/oembed/route.ts. It accepts a 'url' query parameter, calls the SoundCloud oEmbed endpoint with that URL, and returns the JSON response. In your React component, call /api/soundcloud/oembed?url=ENCODED_TRACK_URL to get the embed code, then render it using dangerouslySetInnerHTML. Note that dangerouslySetInnerHTML is safe here because the HTML comes from SoundCloud (a trusted source), not from user input. Bolt's WebContainer handles outbound HTTPS calls from API routes perfectly — you will see the embedded SoundCloud player in the Bolt preview as soon as you add a real SoundCloud track URL.
Create a SoundCloud oEmbed integration. (1) Create a Next.js API route at app/api/soundcloud/oembed/route.ts that accepts a 'url' query param, calls https://soundcloud.com/oembed?url={encodedUrl}&format=json&show_user=true and returns the response JSON. Handle invalid URLs with a 400 error and fetch failures with 500. (2) Create a SoundCloudEmbed React component that accepts a soundcloudUrl prop and fetches from /api/soundcloud/oembed. Show a loading skeleton while fetching. Render the returned html field using dangerouslySetInnerHTML in a wrapper div. Handle error states with an error message. (3) Add the component to a demo page with 2-3 example SoundCloud track URLs. Use TypeScript.
Paste this in Bolt.new chat
1// app/api/soundcloud/oembed/route.ts2import { NextResponse } from 'next/server';34interface OEmbedResponse {5 html: string;6 title: string;7 description: string;8 thumbnail_url: string;9 author_name: string;10 width: number;11 height: number;12}1314export async function GET(request: Request) {15 const { searchParams } = new URL(request.url);16 const trackUrl = searchParams.get('url');1718 if (!trackUrl) {19 return NextResponse.json({ error: 'url parameter is required' }, { status: 400 });20 }2122 // Validate it looks like a SoundCloud URL23 if (!trackUrl.includes('soundcloud.com')) {24 return NextResponse.json({ error: 'Invalid SoundCloud URL' }, { status: 400 });25 }2627 try {28 const oembedUrl = new URL('https://soundcloud.com/oembed');29 oembedUrl.searchParams.set('url', trackUrl);30 oembedUrl.searchParams.set('format', 'json');31 oembedUrl.searchParams.set('auto_play', 'false');32 oembedUrl.searchParams.set('show_user', 'true');33 oembedUrl.searchParams.set('show_comments', 'false');34 oembedUrl.searchParams.set('hide_related', 'true');3536 const res = await fetch(oembedUrl.toString());37 if (!res.ok) {38 return NextResponse.json({ error: 'Track not found or not public' }, { status: 404 });39 }4041 const data: OEmbedResponse = await res.json();42 return NextResponse.json({43 html: data.html,44 title: data.title,45 author: data.author_name,46 thumbnail: data.thumbnail_url,47 });48 } catch (error) {49 return NextResponse.json({ error: 'Failed to fetch embed data' }, { status: 500 });50 }51}Pro tip: SoundCloud oEmbed returns responsive iframe HTML that automatically adjusts height. The default iframe height for tracks is 166px (compact player) and 450px for playlists. You can request a specific height by adding &maxheight=300 to the oEmbed URL.
Expected result: The SoundCloudEmbed component renders an embedded SoundCloud player in the Bolt preview for any public SoundCloud track URL. The player loads SoundCloud's standard waveform player with play controls.
Add Widget API for custom playback controls
Add Widget API for custom playback controls
For a more custom music player experience, SoundCloud's Widget API gives you JavaScript control over an embedded player. Instead of just showing the default SoundCloud iframe player, you can build your own play/pause button, seek bar, time display, and volume control while the embedded iframe handles the actual audio streaming. The Widget API is loaded as a script from https://w.soundcloud.com/player/api.js. It provides a SC.Widget(iframeElement) constructor that returns a widget object. The widget object has methods: play(), pause(), seekTo(milliseconds), setVolume(0-1), and getPosition(callback). It also supports event binding via widget.bind(SC.Widget.Events.EVENT_NAME, callback). The key events are READY (widget is loaded and ready), PLAY, PAUSE, PLAY_PROGRESS (fires periodically with current position), and FINISH. Binding to PLAY_PROGRESS lets you update a custom seek bar position as the track plays. Binding to READY lets you call getDuration() to get the total track length for displaying remaining time. In the React component, use a ref to hold the iframe element and another ref for the widget object (not state, since we do not want React to re-render when the widget updates position). Create play/pause toggle state that calls widget.play() or widget.pause(). Use a useEffect cleanup to unbind all event listeners when the component unmounts to prevent memory leaks. Note: The Widget API requires the iframe src to use the SoundCloud player URL format (https://w.soundcloud.com/player/?url=ENCODED_TRACK_URL) rather than the standard soundcloud.com track URL. This is different from the oEmbed approach — here you construct the iframe src manually rather than using the HTML returned by oEmbed.
Build a custom SoundCloud player component using the Widget API. Create components/SoundCloudWidget.tsx that: (1) Renders an iframe with src set to https://w.soundcloud.com/player/?url={encodedTrackUrl}&auto_play=false&hide_related=true&show_comments=false. (2) Loads the Widget API script from https://w.soundcloud.com/player/api.js in a useEffect. (3) After script loads, creates a SC.Widget instance on the iframe ref. (4) Binds to READY, PLAY, PAUSE, and PLAY_PROGRESS events. (5) Shows a custom play/pause button below the iframe. (6) Shows current time and duration (formatted as MM:SS). Use TypeScript with proper SC.Widget type declarations and cleanup on unmount.
Paste this in Bolt.new chat
1// components/SoundCloudWidget.tsx2'use client';3import { useEffect, useRef, useState } from 'react';45// Type declarations for SoundCloud Widget API6declare global {7 interface Window {8 SC?: {9 Widget: ((iframe: HTMLIFrameElement) => SCWidget) & {10 Events: { READY: string; PLAY: string; PAUSE: string; PLAY_PROGRESS: string; FINISH: string };11 };12 };13 }14}1516interface SCWidget {17 play: () => void;18 pause: () => void;19 seekTo: (ms: number) => void;20 getPosition: (cb: (position: number) => void) => void;21 getDuration: (cb: (duration: number) => void) => void;22 bind: (event: string, cb: (data?: { currentPosition?: number }) => void) => void;23 unbind: (event: string) => void;24}2526function formatTime(ms: number): string {27 const secs = Math.floor(ms / 1000);28 return `${Math.floor(secs / 60)}:${String(secs % 60).padStart(2, '0')}`;29}3031export function SoundCloudWidget({ trackUrl }: { trackUrl: string }) {32 const iframeRef = useRef<HTMLIFrameElement>(null);33 const widgetRef = useRef<SCWidget | null>(null);34 const [isPlaying, setIsPlaying] = useState(false);35 const [currentTime, setCurrentTime] = useState(0);36 const [duration, setDuration] = useState(0);3738 const embedUrl = `https://w.soundcloud.com/player/?url=${encodeURIComponent(trackUrl)}&auto_play=false&hide_related=true&show_comments=false&show_user=true`;3940 useEffect(() => {41 if (!document.getElementById('sc-widget-script')) {42 const script = document.createElement('script');43 script.id = 'sc-widget-script';44 script.src = 'https://w.soundcloud.com/player/api.js';45 script.async = true;46 script.onload = () => initWidget();47 document.head.appendChild(script);48 } else {49 // Give iframe time to load before initializing50 setTimeout(() => initWidget(), 500);51 }5253 function initWidget() {54 if (!iframeRef.current || !window.SC) return;55 const widget = window.SC.Widget(iframeRef.current);56 widgetRef.current = widget;5758 widget.bind(window.SC.Widget.Events.READY, () => {59 widget.getDuration((dur) => setDuration(dur));60 });61 widget.bind(window.SC.Widget.Events.PLAY, () => setIsPlaying(true));62 widget.bind(window.SC.Widget.Events.PAUSE, () => setIsPlaying(false));63 widget.bind(window.SC.Widget.Events.FINISH, () => setIsPlaying(false));64 widget.bind(window.SC.Widget.Events.PLAY_PROGRESS, (data) => {65 if (data?.currentPosition !== undefined) setCurrentTime(data.currentPosition);66 });67 }6869 return () => {70 if (widgetRef.current && window.SC) {71 Object.values(window.SC.Widget.Events).forEach((e) => widgetRef.current?.unbind(e));72 }73 };74 }, [trackUrl]);7576 return (77 <div className="rounded-xl overflow-hidden border">78 <iframe ref={iframeRef} src={embedUrl} width="100%" height="166"79 allow="autoplay" className="block" />80 <div className="flex items-center gap-3 bg-gray-900 p-3">81 <button onClick={() => isPlaying ? widgetRef.current?.pause() : widgetRef.current?.play()}82 className="rounded-full bg-orange-500 p-2 text-white hover:bg-orange-600">83 {isPlaying ? '⏸' : '▶'}84 </button>85 <span className="font-mono text-sm text-gray-300">86 {formatTime(currentTime)} / {formatTime(duration)}87 </span>88 </div>89 </div>90 );91}Pro tip: The SoundCloud Widget API fires PLAY_PROGRESS roughly every 200-250ms. Do not update React state on every progress event as it causes excessive re-renders. Throttle updates to every 500ms or 1 second for the seek bar display.
Expected result: The SoundCloudWidget component shows the SoundCloud embedded player with custom play/pause controls below it. Clicking the custom play button triggers audio playback through the SoundCloud embed. The time display updates as the track plays.
Implement Spotify API as the full-featured alternative
Implement Spotify API as the full-featured alternative
If your use case requires search, discovery, user libraries, or any feature beyond embedding specific known tracks, the Spotify Web API is the right choice. Spotify has an active developer program, comprehensive documentation, officially maintained SDKs, and a generous free tier that works for development and small-scale production apps. To get started with Spotify, go to developer.spotify.com/dashboard and log in with a Spotify account. Click 'Create App.' Fill in the app name, description, and redirect URI (use http://localhost:3000/api/auth/callback/spotify for development). Accept the Developer Terms and click Save. On the app page, click 'Settings' to see your Client ID and Client Secret. Spotify's API uses OAuth 2.0 with two common flows: the Client Credentials Flow (server-to-server, no user login required, works for searching public content) and the Authorization Code Flow (requires user login, works for accessing user playlists, liked songs, and playback control). For building a public music search and discovery feature, Client Credentials is sufficient. For building a personalized player or playlist manager, you need Authorization Code. In Bolt.new, create a Next.js API route that uses the Client Credentials flow: POST to https://accounts.spotify.com/api/token with your client credentials to get an access token, then use that token to call Spotify's search endpoint (GET /v1/search?q=QUERY&type=track). The access token expires after 3600 seconds, so implement the same caching pattern used in the CoStar guide. Spotify also provides web playback through the Spotify Web Playback SDK — a JavaScript library that turns any browser into a Spotify streaming device. This requires user authentication (Authorization Code flow) and a Spotify Premium account on the user's side, but it enables true in-browser audio streaming without opening the Spotify app.
Create a Spotify music search integration as an alternative to SoundCloud. Add SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET to .env. Create lib/spotify.ts with a getSpotifyToken() function using client credentials flow (POST to https://accounts.spotify.com/api/token) with token caching. Export a searchTracks(query, limit=10) function that calls GET https://api.spotify.com/v1/search?q={query}&type=track&limit={limit}. Return an array of {id, name, artists, albumName, albumImageUrl, previewUrl, spotifyUrl, durationMs}. Create an API route at /api/spotify/search?q=QUERY that calls searchTracks and returns results. Build a search input UI that shows track results as cards with album art, name, artists, and a preview play button (using the previewUrl 30-second preview clip in an HTML audio element).
Paste this in Bolt.new chat
1// lib/spotify.ts2// Full-featured alternative to SoundCloud for music search and discovery3const SPOTIFY_BASE = 'https://api.spotify.com/v1';4const TOKEN_URL = 'https://accounts.spotify.com/api/token';56interface TokenCache { token: string; expiresAt: number; }7let tokenCache: TokenCache | null = null;89export async function getSpotifyToken(): Promise<string> {10 if (tokenCache && tokenCache.expiresAt > Date.now() + 60_000) {11 return tokenCache.token;12 }13 const clientId = process.env.SPOTIFY_CLIENT_ID;14 const clientSecret = process.env.SPOTIFY_CLIENT_SECRET;15 if (!clientId || !clientSecret) throw new Error('Spotify credentials not configured');1617 const res = await fetch(TOKEN_URL, {18 method: 'POST',19 headers: {20 'Content-Type': 'application/x-www-form-urlencoded',21 Authorization: `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`,22 },23 body: 'grant_type=client_credentials',24 });25 if (!res.ok) throw new Error(`Spotify token error: ${res.status}`);26 const data = await res.json();27 tokenCache = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };28 return tokenCache.token;29}3031export async function searchTracks(query: string, limit = 10) {32 const token = await getSpotifyToken();33 const params = new URLSearchParams({ q: query, type: 'track', limit: String(limit) });34 const res = await fetch(`${SPOTIFY_BASE}/search?${params}`, {35 headers: { Authorization: `Bearer ${token}` },36 });37 if (!res.ok) throw new Error(`Spotify search error: ${res.status}`);38 const data = await res.json();39 return data.tracks.items.map((t: {40 id: string; name: string;41 artists: Array<{ name: string }>;42 album: { name: string; images: Array<{ url: string }> };43 preview_url: string | null;44 external_urls: { spotify: string };45 duration_ms: number;46 }) => ({47 id: t.id,48 name: t.name,49 artists: t.artists.map((a) => a.name).join(', '),50 albumName: t.album.name,51 albumImageUrl: t.album.images[0]?.url ?? '',52 previewUrl: t.preview_url,53 spotifyUrl: t.external_urls.spotify,54 durationMs: t.duration_ms,55 }));56}Pro tip: Spotify's 30-second preview clips (previewUrl) are available for most (but not all) tracks without requiring user authentication or a Premium account. They are a great way to let users sample music before clicking through to the full Spotify track.
Expected result: The Spotify search integration returns real track results from Spotify's catalog. Search cards show album art, artist names, and have a play button for 30-second preview clips. This replaces SoundCloud's closed API with a fully functional, officially supported music API.
Common use cases
Embedding Specific SoundCloud Tracks or Playlists
A musician's portfolio site or an editorial blog that embeds specific SoundCloud tracks or playlists using the oEmbed API. The track URLs are known in advance — this is not a search feature, just embedding specific public tracks the developer controls or has permission to embed.
Create a React component called SoundCloudPlayer that accepts a soundcloudUrl prop (a full SoundCloud track or playlist URL). Fetch the oEmbed HTML from https://soundcloud.com/oembed?url={encodedUrl}&format=json&auto_play=false&color=%23ff5500&show_user=true using a Next.js API route at /api/soundcloud/oembed that proxies the request. Render the returned html field as innerHTML using dangerouslySetInnerHTML. Show a loading skeleton while fetching and handle invalid URLs with an error message. Use TypeScript.
Copy this prompt to try it in Bolt.new
Interactive Music Player with Widget API
A custom music player interface that embeds a SoundCloud track and exposes playback controls (play/pause, seek, volume) through your own UI. The SoundCloud Widget JavaScript API provides events and methods to control the embedded iframe player, enabling a fully custom player design.
Build a custom SoundCloud player component using the Widget API. Load the SoundCloud Widget API from https://w.soundcloud.com/player/api.js in a useEffect. Embed a SoundCloud track using an iframe with the SoundCloud embed URL. After loading, create a SC.Widget(iframeElement) instance and bind to the PLAY, PAUSE, FINISH, and PLAY_PROGRESS events. Add custom play/pause, seek bar, and track duration display built with your own React UI. The widget controls the actual SoundCloud embedded player beneath the custom UI. Handle the case where the Widget API script fails to load.
Copy this prompt to try it in Bolt.new
Portfolio Page with Multiple Track Embeds
An artist or DJ portfolio page listing their top tracks with embedded SoundCloud players for each. Track URLs are hardcoded or stored in a CMS. The oEmbed endpoint is called for each URL to get the embed HTML. Lazy loading prevents all players from loading at once.
Build a music portfolio page with multiple SoundCloud track embeds. Create an array of track objects with title, description, and soundcloudUrl. For each track, use the SoundCloudPlayer component that fetches oEmbed HTML from /api/soundcloud/oembed. Implement intersection observer lazy loading so players only load when they enter the viewport. Show a gradient placeholder while loading. Display track title and description below each player. Use Tailwind CSS with a dark music-themed design.
Copy this prompt to try it in Bolt.new
Troubleshooting
SoundCloud oEmbed returns 404 for a track URL
Cause: The track is private, deleted, or the URL format is incorrect. SoundCloud oEmbed only works for public tracks and playlists.
Solution: Verify the SoundCloud track is set to 'Public' in SoundCloud's sharing settings (not 'Private'). Confirm the URL is the full track URL from the browser address bar (e.g., https://soundcloud.com/artist/track-name), not a share link or shortened URL. Test the oEmbed URL directly in a browser: https://soundcloud.com/oembed?url=YOUR_ENCODED_URL&format=json.
SoundCloud Widget API SC is undefined or Widget API events never fire
Cause: The Widget API script has not loaded, or the iframe has not fully loaded the SoundCloud player before the Widget instance was created. The Widget API requires the iframe to be fully initialized, which takes a moment after the script loads.
Solution: Add a READY event binding before calling any other Widget methods. Only use widget methods after the READY event fires. Add a small setTimeout (500ms) between script load and widget initialization to give the iframe time to boot the SoundCloud player.
1// Only call widget methods after READY fires2widget.bind(SC.Widget.Events.READY, () => {3 // Safe to call getDuration(), play(), etc. here4 widget.getDuration((dur) => setDuration(dur));5});CORS error when calling soundcloud.com/oembed directly from a React component
Cause: SoundCloud's oEmbed endpoint does not include CORS headers for arbitrary browser origins. Direct fetch() calls from React components are blocked by the browser's same-origin policy.
Solution: Always proxy oEmbed requests through your Next.js API route at /api/soundcloud/oembed. The API route runs server-side where CORS restrictions do not apply. Never call soundcloud.com/oembed directly from client-side React code.
1// CORRECT — call your own API route2const res = await fetch(`/api/soundcloud/oembed?url=${encodeURIComponent(trackUrl)}`);34// WRONG — CORS will block this in the browser5// const res = await fetch(`https://soundcloud.com/oembed?url=${trackUrl}`);Best practices
- Never use unofficial SoundCloud API wrappers or reverse-engineered client IDs — they violate SoundCloud's Terms of Service and break without warning when SoundCloud updates its internal authentication
- Use the oEmbed endpoint for any static embedding of known track URLs — it is the simplest, most stable, and officially supported path for SoundCloud content
- Proxy all oEmbed API calls through a Next.js API route to avoid CORS errors — do not call soundcloud.com/oembed directly from React components
- Add graceful degradation if the oEmbed API is unavailable — show a direct SoundCloud link button as a fallback when the embed fails to load
- Use Spotify API instead of SoundCloud when you need track search, user library access, audio features, or any capability beyond embedding specific known tracks
- If using the Widget API, always clean up event listeners in the useEffect return function to prevent memory leaks when the component unmounts
- Lazy-load SoundCloud player embeds that are below the fold using IntersectionObserver — loading many SoundCloud iframes simultaneously creates significant page weight and slows initial render
Alternatives
Choose Spotify API for any use case requiring music search, user library access, or playback control — it is the definitive alternative to SoundCloud's closed API with active developer support and comprehensive documentation.
Choose YouTube API if your content focuses on music videos, live performances, or official artist content — YouTube has an active Data API v3 with search, playlist management, and embed capabilities.
Choose Vimeo if you need to embed high-quality video content rather than audio-only tracks — Vimeo has an active developer API with oEmbed support and the Vimeo Player SDK for custom controls.
Frequently asked questions
Does the SoundCloud API work in Bolt.new?
SoundCloud's full REST API (search, user libraries, streams) has been closed to new developers since 2017 and is not available regardless of what development environment you use. What works in Bolt.new is the oEmbed endpoint (embedding specific public tracks by URL) and the Widget JavaScript API (controlling embedded players). For full music API capabilities, use Spotify's Web API instead.
How do I embed a SoundCloud player in a Bolt.new React app?
Use SoundCloud's oEmbed endpoint. Create a Next.js API route that calls https://soundcloud.com/oembed?url=YOUR_TRACK_URL&format=json and returns the embed HTML. In your React component, fetch from this API route and render the returned html field using dangerouslySetInnerHTML. This works for any public SoundCloud track URL without requiring any API credentials.
Why is SoundCloud's API closed to new developers?
SoundCloud suspended new app registrations in 2017 during a period of financial difficulty and organizational restructuring. The developer program was deprioritized as SoundCloud focused on the consumer platform and monetization. As of 2026, the restriction remains in place — there is no developer portal for new API registrations and no announced timeline for reopening.
Can I search for SoundCloud tracks in a Bolt.new app?
Not through official, supported means. SoundCloud's search API requires a client ID that is only available to pre-2017 registered applications. Any library or tutorial claiming to provide SoundCloud search for new developers is using unofficial, unsupported endpoints that violate SoundCloud's Terms of Service and can break at any time. For music search in Bolt.new apps, use Spotify's Web API.
What is Spotify API and how does it compare to SoundCloud for Bolt.new?
Spotify's Web API is an actively maintained, officially supported REST API with a generous free developer tier. It provides track and artist search, user playlist access, audio features (tempo, key, danceability), and the Web Playback SDK for browser-based streaming. Unlike SoundCloud's closed API, Spotify has a self-service developer portal at developer.spotify.com where you can create an app and get credentials in minutes.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation