To integrate the SoundCloud API with Lovable, register a SoundCloud app to get your client_id, store it in Cloud → Secrets, and create Edge Functions that proxy SoundCloud HTTP API requests for track search, user profiles, playlists, and streaming URLs. SoundCloud's simpler client_id authentication (no user OAuth required for public data) makes it faster to set up than Spotify for apps focused on indie music discovery and podcast directories.
Build audio-powered features in Lovable using the SoundCloud HTTP API
SoundCloud hosts over 300 million tracks from independent artists, podcasters, and audio creators worldwide. Its API enables music discovery apps, podcast directories, audio search tools, artist profile showcases, and community listening platforms. Unlike Spotify, SoundCloud's public catalog data is accessible with just a client_id — no OAuth user login required for browsing and searching.
The integration pattern in Lovable uses Edge Functions as a proxy: your React frontend requests track data or search results from an Edge Function, the function appends your stored client_id to the SoundCloud API request, and returns the formatted audio data. This keeps your client_id server-side and prevents it from appearing in browser network requests.
SoundCloud also offers an oEmbed endpoint (api.soundcloud.com/oembed) that requires no authentication at all — it returns embed HTML for any public SoundCloud track URL. For use cases that only need embedded players rather than raw API data, oEmbed is the simplest path with zero credential setup.
For apps building podcast discovery or audio directories on top of SoundCloud content, the track search endpoint supports filtering by tags, genres, and BPM ranges, making it possible to build niche audio discovery experiences like 'ambient music for focus' or 'tech startup podcasts' that pull from SoundCloud's deep catalog of indie content.
Integration method
SoundCloud API uses client_id authentication for public data access, which is simpler than Spotify's OAuth2 PKCE flow. The client_id is stored in Cloud → Secrets and Edge Functions proxy all SoundCloud API calls. For user-specific data (private playlists, likes), user OAuth authorization is also supported but not required for public catalog access.
Prerequisites
- A Lovable project with at least one deployed app (Edge Functions require Lovable Cloud)
- A SoundCloud account (free at soundcloud.com)
- A SoundCloud developer app registered at soundcloud.com/you/apps — this provides your client_id
- Understanding that SoundCloud's API grants access to public content without user OAuth (for private tracks and user-specific data, OAuth is required)
- Basic HTML5 audio knowledge for the playback implementation in React
Step-by-step guide
Register a SoundCloud app and get your client_id
Register a SoundCloud app and get your client_id
To use the SoundCloud API, you need a client_id from a registered SoundCloud app. The client_id identifies your application in API requests and is your authentication credential for public data access. Log in to SoundCloud at soundcloud.com. Navigate to soundcloud.com/you/apps (or go to your profile → Settings → scroll to find the developer apps section). Click 'Register a new application'. Fill in the app registration form: - App name: your Lovable app's name - App URI (Homepage): your Lovable app's deployed URL (e.g., https://myapp.lovable.app) - Redirect URI: your app's callback URL if you plan to implement user OAuth (e.g., https://myapp.lovable.app/soundcloud-callback). For public-only data access, this can be any URL. Click 'Save app'. SoundCloud will show your app's details including the Client ID. The Client ID is the credential you will store as a secret. Note that SoundCloud has historically had varying approval times and developer program availability — if app registration is unavailable, the oEmbed endpoint works without any credentials for embedded players. The client_id is somewhat sensitive — while it only provides access to public SoundCloud data, it can be rate-limited or revoked if abused. Keep it in Cloud → Secrets rather than hardcoding it.
Pro tip: If SoundCloud's app registration portal is temporarily unavailable or slow to approve new apps, you can still use the oEmbed endpoint (no credentials required) to embed players for any public SoundCloud track URL — this covers many podcast and music showcase use cases.
Expected result: A SoundCloud app is registered and your client_id is copied. The app appears in your SoundCloud settings under Apps.
Add the SoundCloud client_id to Lovable Cloud Secrets
Add the SoundCloud client_id to Lovable Cloud Secrets
Store your SoundCloud client_id in Lovable's Cloud Secrets panel. While the SoundCloud client_id only provides read access to public data (unlike database passwords or payment keys), keeping it server-side prevents rate limit issues from browser-side use and makes it easy to rotate if needed. In Lovable, click '+' next to the Preview label → Cloud panel → Secrets tab. Click 'Add new secret'. Name: SOUNDCLOUD_CLIENT_ID Value: your SoundCloud client_id Click Save. This single secret is all the credential configuration needed for public SoundCloud data access. If you later implement user OAuth for accessing private playlists and liked tracks, you will also add SOUNDCLOUD_CLIENT_SECRET. With the secret saved, your Edge Function will access it via Deno.env.get('SOUNDCLOUD_CLIENT_ID') and append it to all SoundCloud API requests.
Pro tip: SoundCloud API rate limits apply per client_id. For apps with high traffic, consider adding request caching in Supabase — cache track search results for common queries with a 10-minute TTL to reduce API calls without impacting freshness.
Expected result: SOUNDCLOUD_CLIENT_ID appears in Cloud → Secrets with its value masked. The Edge Function in the next step will use this to authenticate API requests.
Create the SoundCloud track search Edge Function
Create the SoundCloud track search Edge Function
Create the main Edge Function that proxies SoundCloud API calls. SoundCloud's REST API uses standard GET requests with the client_id appended as a query parameter (not as a header). The base URL for most API calls is https://api.soundcloud.com/. The track search endpoint is: GET https://api.soundcloud.com/tracks?q={query}&client_id={clientId}&limit={limit}&linked_partitioning=1. Additional filter parameters: genres, tags, bpm[from], bpm[to], duration[from], duration[to], filter (public, private, streamable, downloadable). SoundCloud track objects include: id, title, permalink_url, user (username and avatar_url), artwork_url, stream_url, playback_count, favoritings_count, duration (in milliseconds), tag_list, and genre. For audio playback, the stream_url from the API response requires the client_id appended: stream_url + '?client_id=' + clientId. Since the client_id should stay server-side, your Edge Function should either: (a) return a time-limited signed URL, or (b) proxy the audio stream itself. For most apps, returning the stream URL with the client_id appended is acceptable since the client_id only provides read access to public audio — but if you want maximum security, proxy the stream through the Edge Function. The function below shows the track search pattern. The URL resolve endpoint (GET https://api.soundcloud.com/resolve?url={soundcloud_url}&client_id={clientId}) is also demonstrated — it converts any public SoundCloud URL (profile URL, track URL, playlist URL) to its API representation.
Create an Edge Function called soundcloud-search at supabase/functions/soundcloud-search/index.ts. It should accept GET requests with query parameters: q (search query), genre (optional), limit (default 20). Use SOUNDCLOUD_CLIENT_ID from secrets. Call the SoundCloud tracks API and return formatted results with id, title, artist, artworkUrl (high-res 500x500 version), duration, playbackCount, permalinkUrl, and streamUrl (with client_id appended). Include CORS headers.
Paste this in Lovable chat
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'23const corsHeaders = {4 'Access-Control-Allow-Origin': '*',5 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',6}78serve(async (req) => {9 if (req.method === 'OPTIONS') {10 return new Response('ok', { headers: corsHeaders })11 }1213 try {14 const url = new URL(req.url)15 const query = url.searchParams.get('q') || ''16 const genre = url.searchParams.get('genre') || ''17 const limit = parseInt(url.searchParams.get('limit') || '20', 10)1819 const clientId = Deno.env.get('SOUNDCLOUD_CLIENT_ID')20 if (!clientId) {21 throw new Error('SOUNDCLOUD_CLIENT_ID secret is not configured')22 }2324 if (!query && !genre) {25 return new Response(26 JSON.stringify({ error: 'Provide at least a search query or genre' }),27 { status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }28 )29 }3031 const searchUrl = new URL('https://api.soundcloud.com/tracks')32 if (query) searchUrl.searchParams.set('q', query)33 if (genre) searchUrl.searchParams.set('genres', genre)34 searchUrl.searchParams.set('client_id', clientId)35 searchUrl.searchParams.set('limit', String(limit))36 searchUrl.searchParams.set('linked_partitioning', '1')37 searchUrl.searchParams.set('filter', 'public')3839 const response = await fetch(searchUrl.toString())4041 if (!response.ok) {42 const error = await response.text()43 throw new Error(`SoundCloud API error ${response.status}: ${error}`)44 }4546 const data = await response.json()47 const tracks = (data.collection || data).map((track: Record<string, unknown>) => ({48 id: track.id,49 title: track.title,50 artist: (track.user as Record<string, string>)?.username || 'Unknown Artist',51 artworkUrl: track.artwork_url52 ? (track.artwork_url as string).replace('large', 't500x500')53 : null,54 duration: track.duration, // milliseconds55 playbackCount: track.playback_count,56 permalinkUrl: track.permalink_url,57 // Append client_id for streaming — client can play this URL directly58 streamUrl: track.stream_url ? `${track.stream_url}?client_id=${clientId}` : null,59 }))6061 return new Response(62 JSON.stringify({ tracks, nextHref: data.next_href }),63 { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }64 )65 } catch (error) {66 return new Response(67 JSON.stringify({ error: error.message }),68 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }69 )70 }71})Pro tip: SoundCloud artwork URLs default to 100x100 px ('large' size). To get high-resolution artwork for your UI, replace 'large' with 't500x500' in the artwork_url string. Other available sizes: t67x67, t300x300, t200x200.
Expected result: The soundcloud-search Edge Function is deployed. Test it from Cloud → Edge Functions with a GET request containing ?q=lofi+beats. The response should include tracks with artwork URLs, durations, and stream URLs.
Build the audio player UI in Lovable React
Build the audio player UI in Lovable React
With the search Edge Function deployed, build the frontend components that display track results and play audio. SoundCloud's stream URLs work directly in HTML5 audio elements — use React's useRef to manage the audio element and control playback programmatically. The most important UI states to handle: loading (fetching search results), idle (no audio selected), playing (audio in progress), paused, and error (stream URL unavailable or track deleted). Use the HTML5 audio element's onEnded, onError, onPlay, onPause, and onTimeUpdate events to sync your React state with the actual audio playback state. For a polished player experience, show a waveform visualization or a simple progress bar using the audio element's currentTime and duration properties. Many audio apps use SoundCloud's waveform images (waveform_url in the API response) as visual placeholders — they are pre-rendered PNG images of each track's audio waveform. For building a podcast directory or music curation tool, pair the SoundCloud search Edge Function with a Supabase table that stores curated track IDs, editorial notes, and category tags. Your app presents a curated experience while the audio streams from SoundCloud's CDN.
Create a music discovery page that calls the soundcloud-search Edge Function with a search input. Display results as a track list with artwork, title, artist, duration, and play button. When a play button is clicked, play the track's streamUrl using an HTML5 audio element. Show a now-playing bar at the bottom with the track name, artist, artwork, play/pause button, and a progress bar. Handle loading and error states.
Paste this in Lovable chat
1import { useState, useRef, useEffect } from 'react'2import { supabase } from '@/lib/supabase'34interface Track {5 id: number6 title: string7 artist: string8 artworkUrl: string | null9 duration: number10 streamUrl: string | null11 permalinkUrl: string12}1314export const useSoundCloudPlayer = () => {15 const audioRef = useRef<HTMLAudioElement | null>(null)16 const [currentTrack, setCurrentTrack] = useState<Track | null>(null)17 const [isPlaying, setIsPlaying] = useState(false)18 const [progress, setProgress] = useState(0)1920 useEffect(() => {21 if (!audioRef.current) {22 audioRef.current = new Audio()23 audioRef.current.addEventListener('timeupdate', () => {24 if (audioRef.current) {25 setProgress(26 (audioRef.current.currentTime / audioRef.current.duration) * 100 || 027 )28 }29 })30 audioRef.current.addEventListener('ended', () => setIsPlaying(false))31 }32 }, [])3334 const play = (track: Track) => {35 if (!track.streamUrl) return36 if (audioRef.current) {37 if (currentTrack?.id === track.id) {38 isPlaying ? audioRef.current.pause() : audioRef.current.play()39 setIsPlaying(!isPlaying)40 } else {41 audioRef.current.src = track.streamUrl42 audioRef.current.play()43 setCurrentTrack(track)44 setIsPlaying(true)45 }46 }47 }4849 return { currentTrack, isPlaying, progress, play }50}Pro tip: Not all SoundCloud tracks have stream URLs — private tracks and tracks where the artist has disabled streaming return null for stream_url. Always check for null before rendering a play button, and fall back to a 'Listen on SoundCloud' link using the permalink_url.
Expected result: The music discovery page renders SoundCloud track results with playable audio. Clicking a track's play button streams the audio directly from SoundCloud's CDN. The now-playing bar shows the current track and progress.
Common use cases
Build an indie music discovery page with SoundCloud track search
Create a music discovery interface in Lovable where users can search SoundCloud's catalog by genre, tag, or keyword. The Edge Function calls SoundCloud's track search endpoint with the query and returns tracks with artwork, title, artist, play count, and streaming URL. Audio playback uses HTML5 audio elements with the streaming URL proxied through the Edge Function.
Create an Edge Function called soundcloud-search that accepts { query, genre, limit } and searches the SoundCloud API using SOUNDCLOUD_CLIENT_ID from secrets. Return tracks with id, title, username, artworkUrl, streamUrl, playbackCount, duration, and permalinkUrl. Then create a music discovery page with a search bar, genre filter chips, and a track grid that plays audio previews. Use HTML5 audio elements for playback.
Copy this prompt to try it in Lovable
Display a SoundCloud artist profile with their public tracks
Build an artist showcase page that pulls a SoundCloud user's profile information and their public tracks via the API. Given a SoundCloud username or profile URL, the Edge Function fetches the artist's avatar, bio, follower count, and most recent tracks. This works for fan pages, artist portfolios, and 'featured artist' sections in music apps.
Create an Edge Function called soundcloud-artist that accepts { username } and fetches the SoundCloud user profile and their 10 most recent public tracks using SOUNDCLOUD_CLIENT_ID from secrets. Resolve the username to a user ID first, then fetch tracks. Return user avatar, display name, description, follower count, and track list. Create an artist profile page component that displays this data.
Copy this prompt to try it in Lovable
Embed SoundCloud players in a podcast directory
Build a podcast listing page where each episode has an embedded SoundCloud player. Use SoundCloud's oEmbed endpoint to generate the embed HTML for each track URL — no API key required for oEmbed. Display episode titles, descriptions, and the embedded players in a podcast directory layout that users can browse without any account connection.
Create a podcast directory page in my Lovable app. I have a Supabase table called podcasts with columns: title, description, soundcloud_url, episode_number. Fetch the podcast list from Supabase and for each entry, call the SoundCloud oEmbed endpoint (https://soundcloud.com/oembed?url={soundcloud_url}&format=json) to get the embed HTML. Render each episode with title, description, and the embedded SoundCloud player. No API key needed for oEmbed.
Copy this prompt to try it in Lovable
Troubleshooting
SoundCloud API returns 401 Unauthorized with 'Invalid client_id' error
Cause: The SOUNDCLOUD_CLIENT_ID secret contains an invalid or revoked client_id, or the secret name has a typo.
Solution: In Cloud → Secrets, verify the secret name is exactly SOUNDCLOUD_CLIENT_ID (case-sensitive). Then verify the value matches your app's client_id from soundcloud.com/you/apps. If the client_id was regenerated or the app was deleted and recreated, update the secret with the new value and redeploy the Edge Function.
Audio playback fails with 'XMLHttpRequest error' or CORS error when playing stream URLs
Cause: SoundCloud's streaming CDN has CORS restrictions that prevent direct browser-side streaming from some origins. The browser can play the audio via an HTML5 audio element, but AJAX/fetch requests to the stream URL from JavaScript fail.
Solution: Use an HTML5 audio element with the src attribute set directly to the stream URL, rather than fetching the stream via JavaScript. The browser's audio player bypasses CORS restrictions for audio element src. Create the Audio element via new Audio(streamUrl) or use an <audio src={streamUrl}> React element. If CORS errors persist, proxy the stream through your Edge Function.
1// Use Audio element directly - bypasses CORS:2const audio = new Audio(track.streamUrl)3audio.play()45// Do NOT use fetch() to download the stream:6// fetch(track.streamUrl) // This WILL fail with CORS errorSoundCloud API returns 403 Forbidden for some tracks but not others
Cause: Some SoundCloud tracks are private, restricted to specific regions, or set to 'stream disabled' by the artist. These tracks return 403 when stream_url is accessed.
Solution: Filter tracks with stream_url: null out of your results before displaying play buttons — these tracks cannot be played via the API. For tracks that return stream_url but fail with 403 on playback, they have been restricted by the artist or SoundCloud. Show a 'Listen on SoundCloud' link using the permalink_url instead of a play button. Add filter=streamable to your search query parameters to only return tracks that can be streamed.
1// Add to your search URL to only return streamable tracks:2searchUrl.searchParams.set('filter', 'streamable')SoundCloud API returns 429 Too Many Requests
Cause: The client_id has exceeded SoundCloud's rate limits for API requests. SoundCloud's rate limits are per client_id and are enforced based on request frequency.
Solution: Implement caching for search results and track data in Supabase. Cache common search queries for 5-10 minutes to reduce repeated API calls. For artist profile pages, cache the profile data for 1 hour. Implement debouncing in your search input so the API is only called when the user stops typing (300-500ms debounce), rather than on every keystroke.
Best practices
- Store SOUNDCLOUD_CLIENT_ID in Cloud → Secrets even though it only grants read access to public data — this makes it easy to rotate if the key is rate-limited or abused, without any code changes.
- Add filter=streamable to all track search queries to ensure you only display tracks that users can actually play, preventing broken play buttons.
- Cache SoundCloud API responses in Supabase with appropriate TTLs — track metadata (5 minutes to 1 hour) and artist profiles (1-24 hours) — to reduce API calls and stay within rate limits.
- Always replace 'large' with 't500x500' in artwork URLs when displaying artwork larger than 100x100 pixels — this prevents blurry images in your UI.
- Use SoundCloud's oEmbed endpoint for simple player embeds without API authentication — it is the fastest path for podcast directory or track showcase use cases.
- Fall back gracefully when stream_url is null — always provide a 'Listen on SoundCloud' link using permalink_url so users with restricted tracks can still access the content.
- Debounce search input at 300-500ms to avoid triggering an API call on every keystroke — this reduces API quota usage and prevents rate limiting for active searchers.
Alternatives
Choose Spotify if you need the mainstream music catalog with high audio quality and full playback control, but expect more complex OAuth2 PKCE setup and stricter rate limits.
Choose Twitch if your app targets live streaming and gaming audio content rather than recorded music and podcast archives.
Choose ElevenLabs if you need AI-generated audio content like text-to-speech and voice synthesis rather than access to user-uploaded audio from SoundCloud's catalog.
Frequently asked questions
What is the difference between SoundCloud's client_id auth and Spotify's OAuth2 PKCE?
SoundCloud's client_id authentication allows read access to all public data without any user interaction — you just append your client_id to API requests. No user needs to log in to SoundCloud to browse public tracks and playlists. Spotify's OAuth2 PKCE requires users to log in with their Spotify account and approve permissions for any personalized data. For public catalog browsing, SoundCloud's approach is significantly simpler. Both require user OAuth if you need access to private data (private playlists, liked tracks, user-specific data).
Does SoundCloud allow commercial use of its API?
SoundCloud's API terms (developer.soundcloud.com/docs/api/terms-of-use) allow non-commercial and commercial applications but require compliance with their Terms of Service, including not downloading or permanently storing SoundCloud content, proper attribution to SoundCloud and track artists, and not creating competing services. For commercial apps that monetize SoundCloud content directly, contact SoundCloud about commercial licensing. The API is appropriate for apps that link back to SoundCloud and respect artist content permissions.
Can I search for podcasts specifically on SoundCloud?
SoundCloud does not have a separate podcast API endpoint — podcasts are uploaded as regular tracks and playlists. You can search for podcasts using tags and genre filters: add genres=podcast or search for specific show names. Many podcast creators on SoundCloud use the 'podcast' tag, so searching with the tag filter (tag_list:podcast) works reasonably well for podcast discovery.
What happens when SoundCloud's developer portal is closed to new registrations?
SoundCloud has periodically restricted new developer app registrations. If the developer portal is currently unavailable, you have two alternatives: (1) use the oEmbed endpoint (api.soundcloud.com/oembed?url=) which requires no credentials and returns embeddable HTML for any public track URL; (2) check community resources where some developers share information about alternative access methods. The oEmbed approach handles most podcast and music showcase use cases without API credentials.
How do I display a SoundCloud waveform in my Lovable app?
SoundCloud track API responses include a waveform_url field containing a URL to a pre-rendered PNG image of the audio waveform. You can display this image as a visual indicator in your player UI. The waveform image is typically 1800x280 pixels. For an interactive waveform with seek functionality, use the waveform image as a background and overlay a CSS progress bar that updates based on the audio element's currentTime — this creates a clickable waveform without any audio analysis library.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation