SoundCloud ha reabierto su API, pero no necesitas una clave para crear un portafolio. Almacena tus pistas en Webflow CMS y renderiza un reproductor para cada elemento.
Todo músico que gestiona su propio sitio se topa con el mismo muro. Lanzas una pista, abres el sitio, pegas otro código de inserción manualmente y vuelves a publicar. El portafolio que los artistas realmente quieren se actualiza solo: un nuevo lanzamiento aparece en cuanto se añade a la lista, sin necesidad de incrustar nada a mano.
SoundCloud hace que esto vuelva a ser razonable. Su registro público de aplicaciones estuvo cerrado durante años y, en 2026, la empresa pasó a un modelo de autoservicio completo donde el ID y el secreto del cliente ahora requieren una suscripción Artist Pro. La buena noticia para tu portafolio es que puedes saltarte ese camino por completo. Tanto el endpoint de oEmbed como el widget del reproductor HTML5 funcionan sin necesidad de ninguna clave.
Esta guía almacena cada pista en una colección de Webflow CMS y renderiza un reproductor de SoundCloud para cada elemento desde una aplicación de Webflow Cloud. Me apoyo en oEmbed para el marcado y en la API de widgets para la reproducción, así que nada de esto depende de un plan de API de pago.
¿Qué necesitas para crear un sitio de portafolio musical de SoundCloud en Webflow?
Necesitas un proyecto de Webflow Cloud, una colección de CMS para guardar tus pistas y las URLs públicas de SoundCloud del audio que quieras destacar. No hace falta ninguna clave de API de SoundCloud en esta lista.
Aquí tienes todo lo que debes tener listo antes de escribir la primera línea de código:
- Proyecto de Webflow Cloud: Una aplicación de Next.js conectada a tu sitio de Webflow, ejecutándose localmente o ya desplegada.
- Colección de CMS de pistas: Una colección con un campo de texto simple para la URL pública de SoundCloud de cada pista.
- URLs públicas de SoundCloud: El enlace para compartir de cada pista, set o lista de reproducción que quieras mostrar en la página.
- Token de API del sitio de Webflow: Un token con acceso de lectura al CMS para que la aplicación pueda listar los elementos de tu colección.
- Node.js 22 o superior: El entorno de ejecución para el desarrollo local y la CLI de Webflow.
Con la colección y el token listos, el proceso pasa de Webflow al reproductor en sí.
6 pasos para crear un sitio de portafolio musical de SoundCloud en Webflow
La construcción tiene dos partes. Webflow CMS contiene las pistas y la aplicación las lee; luego, SoundCloud convierte cada URL almacenada en un reproductor que los visitantes pueden escuchar.
Los dos primeros pasos preparan los datos, los pasos intermedios generan los reproductores y el último paso añade una capa de estado de reproducción personalizada sobre ellos.
Todo permanece en el lado del servidor hasta que el reproductor final llega al navegador, lo que mantiene tu token de Webflow y cualquier futura credencial de SoundCloud fuera del cliente.
1. Crea la colección de pistas en Webflow CMS
Abre el Webflow Designer y añade una nueva colección CMS llamada Tracks. El campo importante es un campo de texto plano para la URL pública de SoundCloud de la pista, así que asígnale un nombre predecible como SoundCloud URL. Webflow convierte esa etiqueta en el slug soundcloud-url, que es la clave que leerás más adelante desde la Data API. Añade todo lo que necesite tu diseño: nombre del artista, fecha de lanzamiento, una breve descripción o una imagen de portada. Mantener la URL de SoundCloud como texto plano en lugar de un campo de enlace evita que Webflow envuelva el valor en etiquetas adicionales. Rellena algunos elementos con enlaces reales para tener algo que renderizar mientras construyes. Una vez que la colección tenga elementos, la aplicación tendrá una fuente de la cual leer.
2. Genera un token de API del sitio de Webflow con acceso de lectura al CMS
Tu aplicación de Webflow Cloud lee la colección a través de la Data API de Webflow, y esa llamada requiere un token de API del sitio. En la configuración de tu sitio, abre Apps and integrations, luego API access, y genera un nuevo token. Dale acceso de lectura al CMS para que pueda listar los elementos de la colección. El token es secreto, así que debe ir en una variable de entorno en lugar de en tu código.
Añádelo, junto con el ID de la colección, a tu archivo local .env.local y a las variables de entorno de Webflow Cloud:
WEBFLOW_SITE_TOKEN=your_site_api_token TRACKS_COLLECTION_ID=your_collection_id
El ID de la colección aparece en el Designer bajo la configuración de la colección. En Webflow Cloud, estos valores se leen con process.env en el momento de la solicitud, por lo que la aplicación toma los valores desplegados sin importar ningún archivo de configuración. Con el token almacenado, la aplicación ya puede obtener las pistas.
3. Lee la colección Tracks en tu aplicación de Webflow Cloud
Añade un asistente del lado del servidor que liste los elementos de la colección a través de la Data API. Como se ejecuta en el servidor, el token nunca llega al navegador, y una ventana de revalidación corta permite que los nuevos lanzamientos aparezcan sin necesidad de un nuevo despliegue.
Este asistente llama al endpoint y mapea cada elemento a los campos que la página necesita:
// lib/tracks.ts
// Reads the Tracks collection from the Webflow Data API on the server.
const WEBFLOW_API = "https://api.webflow.com/v2";
export type Track = {
id: string;
name: string;
soundcloudUrl: string;
};
export async function getTracks(): Promise<Track[]> {
const res = await fetch(
// The /items/live endpoint returns only published items, so drafts and
// archived tracks never render a player.
`${WEBFLOW_API}/collections/${process.env.TRACKS_COLLECTION_ID}/items/live?limit=100`,
{
headers: { Authorization: `Bearer ${process.env.WEBFLOW_SITE_TOKEN}` },
// Refresh periodically so new releases appear without a redeploy.
next: { revalidate: 300 },
}
);
if (!res.ok) {
throw new Error(`Webflow CMS request failed: ${res.status}`);
}
const { items } = await res.json();
return items.map((item: any) => ({
id: item.id,
name: item.fieldData.name,
soundcloudUrl: item.fieldData["soundcloud-url"],
}));
}La clave soundcloud-url coincide con el slug del campo que Webflow generó en el primer paso. Llamar al endpoint items/live devuelve solo los elementos publicados, por lo que una pista que aún estés redactando nunca llegará a la página. Cada elemento llega ahora como un pequeño objeto Track que contiene un id, un nombre y la URL de SoundCloud, listo para entregárselo al reproductor. Si la solicitud falla, el error resultante aparecerá en tus registros de Webflow Cloud en lugar de renderizar una página rota.
4. Convierte cada URL de pista en un reproductor con oEmbed de SoundCloud
SoundCloud publica un endpoint oEmbed que devuelve el código de marcado del reproductor ya listo para cualquier URL pública de pista, set o usuario. Responde a una petición fetch simple y no necesita clave, por eso encaja tan bien en un portafolio. Sigue el estándar oEmbed compartido.
El asistente a continuación solicita JSON al endpoint y devuelve el código de marcado iframe que este envía:
// lib/oembed.ts
// Ask SoundCloud's oEmbed endpoint for the player markup.
// No API key is required for this endpoint.
export async function getPlayerHtml(trackUrl: string): Promise<string> {
const endpoint = new URL("https://soundcloud.com/oembed");
endpoint.searchParams.set("format", "json");
endpoint.searchParams.set("url", trackUrl);
endpoint.searchParams.set("maxheight", "166");
endpoint.searchParams.set("show_comments", "false");
endpoint.searchParams.set("color", "ff5500");
const res = await fetch(endpoint, { next: { revalidate: 86400 } });
if (!res.ok) return "";
const data = await res.json();
// data.html is an <iframe> pointing at w.soundcloud.com/player.
return data.html as string;
}El endpoint lee el parámetro url y responde con una cadena html que contiene un iframe que apunta a w.soundcloud.com/player. El parámetro maxheight se refleja en ese embed, manteniendo el reproductor compacto de 166 píxeles. Los parámetros color y show_comments son parte de la especificación oEmbed, pero SoundCloud no siempre los refleja en el src del iframe devuelto, así que para un control fiable sobre el color del botón de reproducción o la visualización de comentarios, añade tú mismo los parámetros equivalentes de la Widget API a la URL del reproductor. Almacenar la respuesta en caché durante un día evita llamar al endpoint en cada renderizado. Cada pista se resuelve ahora en un bloque de HTML del reproductor.
5. Renderiza los reproductores desde tu lista del CMS
Un componente de servidor une ambos asistentes. Lee las pistas, resuelve cada una en código de marcado de reproductor en paralelo e imprime una lista. Como el html de oEmbed es código de marcado de confianza devuelto por SoundCloud, se inserta a través de dangerouslySetInnerHTML.
El componente de página mapea cada pista a su encabezado y reproductor:
// app/music/page.tsx
import { getTracks } from "@/lib/tracks";
import { getPlayerHtml } from "@/lib/oembed";
export default async function MusicPage() {
const tracks = await getTracks();
const players = await Promise.all(
tracks.map(async (track) => ({
...track,
html: await getPlayerHtml(track.soundcloudUrl),
}))
);
return (
<ul className="track-list">
{players.map((track) => (
<li key={track.id}>
<h2>{track.name}</h2>
<div dangerouslySetInnerHTML={{ __html: track.html }} />
</li>
))}
</ul>
);
}Ejecutar Promise.all resuelve todas las solicitudes oEmbed juntas en lugar de una tras otra, por lo que una página con una docena de pistas evita una docena de viajes de ida y vuelta secuenciales. El nombre de clase en la lista, track-list, es el gancho que utiliza el controlador de reproducción en el siguiente paso. Visita la ruta y cada pista del CMS se renderizará como su propio reproductor de SoundCloud.
6. Refleja la pista activa con la Widget API
Los reproductores de SoundCloud separados ya se coordinan por sí mismos: single_active está activado por defecto, por lo que iniciar un reproductor pausa los demás en la página. Donde la Widget API se gana su lugar es en tu propia interfaz. Carga su script, envuelve cada iframe en una instancia de SC.Widget y escucha los eventos de reproducción para controlar el estado personalizado.
Este componente de cliente marca qué pista está sonando para que puedas darle estilo como si estuviera reproduciéndose:
// components/PlaybackController.tsx
"use client";
import { useEffect } from "react";
declare global {
interface Window { SC?: any; }
}
export default function PlaybackController() {
useEffect(() => {
const script = document.createElement("script");
script.src = "https://w.soundcloud.com/player/api.js";
script.onload = () => {
const items = Array.from(
document.querySelectorAll<HTMLLIElement>(".track-list li")
);
items.forEach((item) => {
const iframe = item.querySelector("iframe");
if (!iframe) return;
const widget = window.SC.Widget(iframe);
// Separate players already pause each other because single_active
// defaults to true. Here we mirror playback state in our own markup.
widget.bind(window.SC.Widget.Events.PLAY, () => {
items.forEach((el) => el.classList.remove("is-playing"));
item.classList.add("is-playing");
});
const clear = () => item.classList.remove("is-playing");
widget.bind(window.SC.Widget.Events.PAUSE, clear);
widget.bind(window.SC.Widget.Events.FINISH, clear);
});
};
document.body.appendChild(script);
}, []);
return null;
}El script expone SC.Widget en window, y SC.Widget(iframeElement) devuelve un controlador con métodos para bind, play y pause. Al vincular los eventos PLAY, PAUSE y FINISH, puedes mantener una clase is-playing sincronizada con la pista actual, algo que la coordinación integrada no puede expresar en tu marcado. Coloca el componente una sola vez en la ruta que renderiza la lista. Si más adelante estableces single_active en false para que los reproductores se acumulen, ese mismo controlador de PLAY es donde volverías a añadir la pausa manual.
¿Por qué falla un portafolio de música en SoundCloud? Consejos para solucionar problemas
La mayoría de los problemas aquí provienen de cuatro fuentes: una URL de pista que el endpoint no puede resolver, marcado del reproductor que se renderiza como texto, el script del Widget ejecutándose demasiado pronto o varios reproductores compitiendo por los altavoces a la vez.
Cada síntoma a continuación empareja la causa con la solución que lo resuelve.
oEmbed no devuelve nada y el reproductor aparece en blanco
Cuando getPlayerHtml devuelve un resultado vacío, el área del reproductor se renderiza como un hueco en la página. Esto casi siempre se debe a la URL de la pista y no a tu código.
Causa: La URL enviada al endpoint de oEmbed apunta a una pista privada, una subida eliminada o una ruta mal escrita, por lo que SoundCloud no tiene nada que incrustar. Una pista privada se resuelve para ti mientras has iniciado sesión, pero devuelve un error ante una solicitud anónima del servidor.
Solución: Confirma que el valor almacenado sea la URL pública para compartir abriéndola en una ventana de incógnito. Configura la privacidad de la pista como pública en SoundCloud y asegúrate de que el campo del CMS contenga la URL completa con https y sin espacios al final. El asistente construye la consulta con URL y searchParams, por lo que la codificación se gestiona automáticamente y lo que debes verificar es la URL sin procesar.
La página muestra código iframe sin procesar en lugar de audio
En lugar de un botón de reproducción, la página muestra la etiqueta iframe literal como texto en la pantalla.
Causa: La cadena HTML de oEmbed llegó al DOM como texto escapado en lugar de marcado analizado. En React, esto sucede cuando la cadena se renderiza como un elemento hijo normal en lugar de usar dangerouslySetInnerHTML. En el Diseñador nativo de Webflow, ocurre cuando el valor termina en un elemento de texto en lugar de en un bloque de código HTML.
Solución: En una aplicación de Webflow Cloud, inyecta el marcado con la propiedad dangerouslySetInnerHTML que se muestra en el paso cinco. Si construyes la lista de forma nativa en el Diseñador, coloca un elemento de incrustación HTML (HTML Embed) dentro de la lista de colección y referencia allí el campo de la URL de SoundCloud, ya que los elementos de incrustación renderizan el marcado mientras que los elementos de texto lo escapan.
SC.Widget no está definido cuando se ejecuta el controlador
La consola informa que SC o SC.Widget no es una función y ninguna de las configuraciones de reproducción surte efecto.
Causa: El código del controlador se ejecutó antes de que el script api.js terminara de cargarse, o se ejecutó en el servidor durante el renderizado, donde no existe el objeto window. La variable global SC solo aparece después de que el script se carga en el navegador.
Solución: Adjunta la configuración del widget al controlador onload del script, como hace el componente del paso seis, para que solo se ejecute una vez que SC esté definido. Mantén el componente marcado con la directiva use client para que nunca se ejecute durante el renderizado del servidor. Si inyectas el script a través de un asistente del framework, espera a su callback de preparación antes de llamar a SC.Widget en cualquier iframe.
Los reproductores siguen sonando uno sobre otro
Un visitante inicia una pista, se desplaza hacia abajo, inicia otra y ambas siguen sonando a la vez en lugar de que la primera se detenga.
Causa: El parámetro single_active se ha establecido en false en algún lugar de la URL del reproductor. El valor predeterminado documentado de SoundCloud es true, y ese valor es precisamente lo que hace que varios reproductores en una página se apaguen entre sí, por lo que desactivarlo elimina la coordinación integrada en la que confiabas.
Solución: Elimina single_active=false de la URL de incrustación o establécelo explícitamente en true para que la pausa mutua predeterminada vuelva a aplicarse. Si realmente quieres que los reproductores funcionen de forma independiente, mantén single_active=false a propósito y utiliza el controlador de la API del Widget del paso seis para realizar un seguimiento de la reproducción y pausar las pistas tú mismo en el evento PLAY.
Lo que puedes crear con SoundCloud y Webflow
El ciclo principal ya está listo: las pistas viven en el CMS de Webflow y cada elemento genera un reproductor de SoundCloud sin claves, con reproducción coordinada. A partir de aquí, la misma colección se expande hasta convertirse en un portafolio completo.
Como los reproductores provienen de los datos del CMS, cualquier campo que añadas se convierte en un control. Un campo de género o estado de ánimo se transforma en botones de filtro sobre la lista, un campo de fecha de lanzamiento organiza tu discografía y un interruptor de destacados puede fijar una pista en la parte superior. Cambiar la URL de una pista por la de una lista de reproducción o un set en la misma llamada oEmbed genera un widget multipista sin tocar el resto del código. El parámetro de color permite que cada reproductor combine con tu marca en lugar del naranja predeterminado.
Explora Webflow + SoundCloud para conocer las opciones de conexión y los patrones de incrustación que admite la integración.
Para un control más allá de lo que ofrecen los widgets de incrustación, la documentación para desarrolladores de Webflow cubre la lectura y sincronización de tu contenido del CMS a través de la API de datos.
Preguntas frecuentes
¿Necesito una clave de API de SoundCloud para incrustar pistas?
No. Tanto el endpoint oEmbed como el widget del reproductor HTML5 funcionan sin necesidad de claves ni credenciales de cuenta. El ID de cliente y el secreto de cliente, que ahora requieren una suscripción Artist Pro, son solo para la API completa, como para subidas programáticas o lectura de datos privados. Un portafolio no necesita nada de eso.
¿Cuál es la diferencia entre oEmbed y la API de widgets?
oEmbed es una llamada al servidor que devuelve el código iframe de una pista, por lo que generas reproductores a partir de URLs almacenadas. La API de widgets es JavaScript de navegador que controla los reproductores que ya están en la página: reproducir, pausar, buscar y eventos. Esta guía utiliza oEmbed para crear reproductores y la API de widgets para coordinarlos.
¿Puedo incrustar listas de reproducción y sets de SoundCloud, no solo pistas individuales?
Sí. El endpoint oEmbed acepta la URL de una pista, un set o un usuario, por lo que al pasar el enlace de una lista de reproducción obtienes un widget multipista en el mismo campo HTML. No hace falta cambiar el código, basta con guardar la URL de la lista en tu campo del CMS. Los reproductores de sets tienen un diseño predeterminado de 450 píxeles de altura, a menos que modifiques el parámetro maxheight.
¿Funcionan los reproductores con renderizado del lado del servidor en Webflow Cloud?
Sí. Tanto la obtención de oEmbed como la lectura del CMS se ejecutan en el servidor con fetch estándar y sin dependencias exclusivas de Node, por lo que funcionan en el entorno de ejecución de Workers. Solo el script de la API de widgets se ejecuta en el navegador. El portal para desarrolladores de SoundCloud documenta ambos caminos.








