La API de código personalizado de Webflow te permite aplicar scripts a páginas específicas sin tocar el Diseñador; es útil para etiquetas de análisis, píxeles de seguimiento o cualquier fragmento que deba incluirse en páginas concretas a gran escala.
La API de código personalizado de Webflow te permite registrar scripts una vez a nivel de sitio y luego aplicarlos a páginas específicas mediante programación, sin necesidad del Diseñador. Es el enfoque ideal cuando necesitas implementar píxeles de seguimiento, utilidades de depuración o etiquetas de análisis en páginas concretas a gran escala, o cuando requieres que estas implementaciones estén automatizadas y sean repetibles.
Esta guía recorre todo el flujo de trabajo: ambos tipos de registro, el patrón de GET antes de PUT que evita la pérdida de datos y los cuatro modos de fallo que atrapan a la mayoría de los desarrolladores en su primer intento.
¿Qué necesitas para aplicar código personalizado a través de la API de datos de Webflow?
Necesitas tres cosas: una aplicación de Webflow con autorización OAuth, el ID de tu sitio y los IDs de las páginas de destino, y el contenido del script en sí.
Cada uno de estos tres requisitos tiene una restricción específica que debes tener en cuenta antes de escribir una sola línea de código. La más importante es el tipo de credencial. Usar un token de sitio es el obstáculo más común, y el mensaje de error no te dirá por qué.
Aquí te mostramos cómo se ve cada requisito en la práctica.
Una aplicación de Webflow con OAuth (no un token de sitio)
Los tokens de sitio (las claves de API generadas en Configuración del sitio > Aplicaciones e integraciones > Acceso a la API) no otorgan acceso a los endpoints de código personalizado. Este es el obstáculo más frecuente con el que se topan los desarrolladores.
Las limitaciones de los tokens de Webflow son claras: el código personalizado requiere una aplicación cliente de datos de Webflow autorizada mediante OAuth, con los alcances custom_code:read y custom_code:write. Un token de sitio en cualquier llamada a estos endpoints devolverá un error 401.
Si ya has trabajado con la API de CMS, ya sabes cómo funcionan los tokens de sitio. Olvida ese patrón para este flujo de trabajo. Necesitas una aplicación cliente de datos de Webflow completa. La documentación para desarrolladores de Webflow explica paso a paso la creación de aplicaciones, la configuración de URI de redirección y el intercambio de tokens OAuth. El token de acceso generado por ese flujo autentica cada solicitud que verás a continuación.
Una vez que tengas un token, establécelo como una variable de entorno:
export WEBFLOW_OAUTH_TOKEN=your_oauth_access_token_here
Todas las solicitudes en esta guía utilizan este token en el Authorization: Bearer encabezado.
Si estás integrando esto en una aplicación Node.js, extráelo de process.env.WEBFLOW_OAUTH_TOKEN. Los ejemplos de código a continuación utilizan la fetch API nativa, disponible en Node 18+. No se requieren dependencias adicionales más allá del paquete npm webflow-api si prefieres el envoltorio del SDK.
El ID de tu sitio y los IDs de las páginas a las que quieres dirigirte
El ID del sitio aparece en la URL del Diseñador de Webflow: webflow.com/dashboard/sites/[site-id]/....
Obtenlo desde allí o recupéralo a través de la API:
const sitesResponse = await fetch("https://api.webflow.com/v2/sites", {
headers: {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
},
});
const { sites } = await sitesResponse.json();
const siteId = sites[0].id;
console.log("Site ID:", siteId);
Para los IDs de página, enumera las páginas del sitio:
const pagesResponse = await fetch(
`https://api.webflow.com/v2/sites/${siteId}/pages`,
{
headers: {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
},
}
);
const { pages } = await pagesResponse.json();
pages.forEach((page: { title: string; id: string; slug: string }) => {
console.log(`${page.title} (${page.slug}): ${page.id}`);
});
Crea un mapa de slugs de página a IDs. Al aplicar scripts de forma masiva, harás referencia a los IDs con frecuencia, y tener el mapa en una variable evita llamadas repetidas a la API.
Nota: La CLI de Webflow incluirá compatibilidad nativa para listar páginas desde la línea de comandos en una próxima versión, eliminando la necesidad de usar un script puntual como este. Puedes consultar la referencia de comandos de la CLI para ver los comandos disponibles más recientes.
El contenido del script o la URL alojada
Para el registro en línea, prepara la cadena de JavaScript sin procesar. El límite de 2000 caracteres se aplica solo al código fuente. Si tu fragmento está cerca o supera ese límite, alójalo externamente y utiliza la ruta de registro alojado en su lugar.
Para el registro alojado, necesitas la URL pública donde reside el archivo del script y su hash SRI.
Genera el hash a partir del contenido real del archivo:
curl -s https://your-cdn.com/script.js | openssl dgst -sha256 -binary | openssl enc -base64
Antepone sha256- al resultado. El valor completo va en el campo integrityHash : sha256-abc123.... Si este hash no coincide con el contenido del archivo en la URL alojada, el registro fallará con un error 400.
Una vez que estos tres elementos estén listos, el flujo de trabajo se ejecutará en unos cinco minutos. Así es como funciona.
5 pasos para aplicar un script a una página de Webflow mediante la API de datos
El flujo de trabajo tiene dos fases distintas: registro a nivel de sitio (una vez por script) y aplicación a nivel de página (una vez por página, por script). El paso de aplicación requiere leer el estado actual de la página antes de escribir en ella. Omitir la lectura provoca la pérdida de datos.
Antes de empezar, asegúrate de tener configurado tu token de OAuth, tu ID de sitio a mano y el contenido del script listo.
1. Registra tu script en el sitio
El registro es una operación única por script. Ejecútalo una vez por entorno; después, harás referencia al script por su ID desde cualquier llamada a nivel de página. El endpoint y el cuerpo de la solicitud difieren ligeramente entre el registro en línea y el alojado.
Para el registro en línea:
const registerResponse = await fetch(
`https://api.webflow.com/v2/sites/${siteId}/registered_scripts/inline`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
sourceCode: `console.log("Custom Code API loaded");`,
displayName: "Page Debug Logger",
version: "1.0.0",
location: "footer",
canCopy: true,
}),
}
);
const script = await registerResponse.json();
console.log("Script ID:", script.id);
// Output: Script ID: page_debug_logger
El campo location controla dónde inyecta Webflow el script. "header" lo coloca dentro de <head>. "footer" lo coloca justo antes de </body>. Configúralo según cuándo necesites que se ejecute el script: las herramientas de análisis y los gestores de etiquetas suelen ir en el encabezado; los scripts que manipulan el DOM van en el pie de página.
El campo id en la respuesta es el ID de script derivado automáticamente que usarás para cada llamada a nivel de página. Regístralo en tu consola y guárdalo. No necesitas volver a registrarlo para recuperarlo más tarde, pero tenerlo a mano te ahorra un paso de búsqueda.
Registro alojado (cuando tu script supera el límite en línea)
El registro alojado apunta a Webflow hacia un archivo de script en tu CDN en lugar de incrustar el código fuente en línea. Usa esta ruta cuando tu fragmento supere el límite de 2000 caracteres o cuando quieras actualizar el contenido del script sin tener que volver a registrarlo.
La contrapartida es el campo `integrityHash`: tú eres responsable de generar y mantener el hash SRI, y si no coincide con el archivo en el momento del registro, la llamada fallará con un error 400.
Para el registro alojado:
const registerHostedResponse = await fetch(
`https://api.webflow.com/v2/sites/${siteId}/registered_scripts/hosted`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
hostedLocation: "https://your-cdn.com/analytics.js",
integrityHash: "sha256-abc123yourhashhere",
displayName: "Custom Analytics",
version: "1.0.0",
location: "header",
canCopy: false,
}),
}
);
const hostedScript = await registerHostedResponse.json();
console.log("Hosted Script ID:", hostedScript.id);
Si recibes un error 409 en esta llamada, significa que ya existe un script con el mismo ID derivado en el sitio. Puedes incrementar la versión, usar el ID de script existente directamente en tus llamadas de página o elegir un nombre para mostrar diferente.
2. Confirma los scripts existentes en el sitio (opcional, pero recomendado)
Antes de trabajar con llamadas a nivel de página, una revisión rápida de la lista completa de scripts registrados confirma que el registro se realizó correctamente y muestra todos los ID de script en el sitio. Esto también sirve para auditar antes de alcanzar el límite de 800 scripts.
Ejecuta esto:
const registeredResponse = await fetch(
`https://api.webflow.com/v2/sites/${siteId}/registered_scripts`,
{
headers: {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
},
}
);
const { registeredScripts } = await registeredResponse.json();
registeredScripts.forEach(
(s: { id: string; version: string; location: string }) => {
console.log(`${s.id} — v${s.version} — ${s.location}`);
}
);
La respuesta devuelve todos los scripts registrados en el sitio, sin filtrar por página. Si trabajas con varios scripts, esto te ofrece una referencia clara antes de empezar con el trabajo a nivel de página.
3. Obtén los scripts actuales de la página de destino
Este paso no es opcional. El endpoint PUT que aplica scripts a una página reemplaza la lista completa de scripts. Si te saltas el GET y haces un PUT de un solo script, todos los scripts que estaban en esa página anteriormente desaparecerán.
He depurado este error más de una vez. Un desarrollador nuevo en la API se salta la lectura, hace un PUT con un solo script y luego pasa minutos tratando de averiguar por qué todos sus otros scripts de página han dejado de funcionar. La API está haciendo exactamente lo que se le pidió. Lee siempre el estado actual antes de escribir.
Obtén la lista de scripts actual de la página:
const pageId = "your-target-page-id";
const currentResponse = await fetch(
`https://api.webflow.com/v2/pages/${pageId}/custom_code`,
{
headers: {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
},
}
);
const { scripts: currentScripts = [] } = await currentResponse.json();
console.log(`Found ${currentScripts.length} existing script(s) on this page:`);
currentScripts.forEach((s: { id: string; location: string }) => {
console.log(` - ${s.id} at ${s.location}`);
});
Si la página aún no tiene código personalizado, la respuesta devuelve una matriz vacía. El paso de combinación en el paso 4 maneja ese caso de la misma manera, ya que la expansión solo produce una matriz con tu nuevo script.
4. Comprueba si hay duplicados y luego combina tu nuevo script en la lista
Antes de crear la matriz actualizada, confirma que el script no esté ya aplicado. Enviar un PUT con el mismo ID de script listado dos veces hará que la llamada falle con un error 409.
Aquí tienes la comprobación de duplicados y la combinación:
const scriptId = "page_debug_logger";
const scriptLocation = "footer" as const;
const alreadyApplied = currentScripts.some(
(s: { id: string }) => s.id === scriptId
);
if (alreadyApplied) {
console.log(`Script "${scriptId}" is already on this page. Skipping.`);
process.exit(0);
}
// Merge: keep all existing scripts, append the new one
const updatedScripts = [
...currentScripts,
{
id: scriptId,
location: scriptLocation,
attributes: {}, // optional: pass key-value pairs as HTML attributes on the script tag
},
];
El attributes campo es un objeto de clave-valor que se asigna a los atributos HTML en la etiqueta <script> renderizada. Pasar { "data-page": "home", async: "true" } se renderiza como <script data-page="home" async="true">. Déjalo como un objeto vacío si no necesitas atributos personalizados.
El location valor en la llamada de aplicación a nivel de página puede diferir de lo que estableciste en el registro. El registro establece un valor predeterminado, pero una aplicación a nivel de página te permite anularlo página por página. La mayoría de los flujos de trabajo lo establecen de forma coherente con el mismo valor que el registro.
5. Haz un PUT de la lista de scripts actualizada en la página
Con la matriz combinada lista, escríbela en la página:
const applyResponse = await fetch(
`https://api.webflow.com/v2/pages/${pageId}/custom_code`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ scripts: updatedScripts }),
}
);
if (applyResponse.ok) {
console.log("Scripts applied successfully.");
} else {
const error = await applyResponse.json();
console.error("Apply failed:", error);
}
La llamada PUT reemplaza la lista completa de scripts en la página con los scripts que envíes. Como obtuviste la lista existente en el paso 3 y la combinaste en el paso 4, todos los scripts anteriores permanecen en su lugar y tu nuevo script se añade al final.
Para verificar: vuelve a hacer un GET de los scripts de la página después del PUT y confirma que tu ID de script aparece en la matriz de respuesta. Todo el proceso (registrar, leer, combinar, escribir) toma menos de un segundo por página. Para operaciones masivas en docenas de páginas, envuelve los pasos 3 a 5 en un bucle sobre tus ID de página.
Aplicación masiva en varias páginas:
const targetPageIds = ["page-id-1", "page-id-2", "page-id-3"];
for (const pid of targetPageIds) {
const res = await fetch(`https://api.webflow.com/v2/pages/${pid}/custom_code`, {
headers: { Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`},
});
const { scripts: existing = [] } = await res.json();
if (existing.some((s: { id: string }) => s.id === scriptId)) {
console.log(`Page ${pid}: already has script, skipping`);
continue;
}
await fetch(`https://api.webflow.com/v2/pages/${pid}/custom_code`, {
method: "PUT",
headers: {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ scripts: [...existing, { id: scriptId, location: "footer" }] }),
});
console.log(`Page ${pid}: script applied`);
}
En los planes Starter y Basic, el límite de velocidad es de 60 solicitudes por minuto por clave de API. En los planes CMS, eCommerce y Business, es de 120. Para sitios grandes, añade un pequeño retraso entre iteraciones si estás aplicando cambios a cientos de páginas de forma consecutiva.
Un await new Promise(r => setTimeout(r, 600)) entre cada iteración del bucle te mantiene muy por debajo del límite.
¿Qué provoca que fallen las llamadas a la API de código personalizado de Webflow?
La mayoría de los fallos se deben a una de estas cuatro causas: un tipo de credencial incorrecto, un PUT destructivo sin un GET previo, problemas con el hash SRI o una colisión de ID de script.
Aquí te explicamos cómo identificar y solucionar cada uno.
Error 401 en cada solicitud de código personalizado: se usó un token de sitio en lugar de OAuth
La API de código personalizado devuelve un 401 cuando te autenticas con un token de sitio. Este es el error número uno que cometen los desarrolladores al intentar usar estos endpoints por primera vez. El mensaje de error de la API es genérico y no dice explícitamente "tipo de credencial incorrecto". Simplemente devuelve "no autorizado".
Revisa los encabezados de tu solicitud. Si Authorization: Bearer va seguido de un token que generaste desde Configuración del sitio > Aplicaciones e integraciones > Acceso a la API, ese es el problema. Los tokens de sitio funcionan bien para la API de CMS y la mayoría de los otros endpoints de la API de Webflow, pero explícitamente no cubren el código personalizado.
Cambia al token de acceso OAuth de tu aplicación de Webflow y el 401 desaparecerá.
Scripts que desaparecen de una página después de un PUT
Un PUT que reemplaza solo algunos de los scripts de la página, mientras elimina silenciosamente el resto, siempre significa que la lista existente no se obtuvo antes del PUT, o que se obtuvo pero no se incluyó en el cuerpo de la solicitud.
El patrón más seguro es una función reutilizable que siempre lea antes de escribir:
async function addScriptToPage(
pageId: string,
scriptId: string,
location: "header" | "footer"
) {
const headers = {
Authorization: `Bearer ${process.env.WEBFLOW_OAUTH_TOKEN}`,
"Content-Type": "application/json",
};
// Always read first
const getRes = await fetch(`https://api.webflow.com/v2/pages/${pageId}/custom_code`, {
headers,
});
const { scripts: existing = [] } = await getRes.json();
// Skip if already applied
if (existing.some((s: { id: string }) => s.id === scriptId)) return;
// Merge and write
await fetch(`https://api.webflow.com/v2/pages/${pageId}/custom_code`, {
method: "PUT",
headers,
body: JSON.stringify({
scripts: [...existing, { id: scriptId, location }],
}),
});
}
Trata esto como la única forma de aplicar scripts. Nunca construyas la matriz scripts desde cero sin leer primero el estado actual.
Conflicto 409 en el registro de scripts
Un 409 en el endpoint de registro significa que el displayName que proporcionaste genera un ID de script que ya existe en el sitio. Debido a que los ID se derivan de los nombres para mostrar y son inmutables, no puedes sobrescribir un registro existente.
Tus opciones son usar el ID de script existente directamente (no es necesario volver a registrar el mismo script), cambiar el displayName por algo que genere un ID diferente, o incrementar el campo version . Ten en cuenta que incrementar la versión crea un registro separado. No actualiza el existente. El registro antiguo permanece en el sitio.
Si no estás seguro de qué ID ya están registrados, obtén la lista de scripts registrados del sitio antes de intentar un nuevo registro. Esa lista muestra todos los ID que se están utilizando actualmente.
Errores de hash SRI en el registro de scripts alojados
Si el registro devuelve un 400 y estás utilizando el registro alojado, el integrityHash está mal formado o no coincide con el contenido del archivo. El formato debe ser exactamente sha256-[base64-encoded hash] sin espacios ni saltos de línea en la cadena hash.
Regenera el hash desde el archivo en vivo:
curl -s https://your-cdn.com/script.js | openssl dgst -sha256 -binary | openssl enc -base64 -A
La bandera -A en el comando base64 genera el hash en una sola línea. Copia el resultado y anteponle sha256- antes de colocarlo en integrityHash . Si el archivo en hostedLocation cambia después del registro, vuelve a registrarlo con el nuevo hash y una cadena de versión incrementada.
Webflow valida el hash en el momento del registro; los cambios posteriores en el archivo alojado no provocarán errores automáticamente, pero sí romperán la verificación de integridad en los navegadores.
Crea la capa de automatización de tu sitio
Esta guía cubrió el registro de scripts tanto a nivel de línea como alojados, la lectura del estado de la página antes de escribir en ella y los patrones que evitan los fallos de pérdida de datos más comunes.
El mismo patrón GET-merge-PUT se aplica también al código personalizado a nivel de sitio; el endpoint cambia (`/v2/sites/{siteId}/custom_code`), pero la lógica de lectura antes de escritura es idéntica. Si necesitas enviar un script a todas las páginas en una sola llamada en lugar de hacerlo página por página, el endpoint de código personalizado a nivel de sitio es el siguiente paso.
Explora la documentación para desarrolladores para conocer capacidades más allá de lo que exponen estos endpoints (gestión de scripts en todo el sitio, webhooks de aplicaciones y arquitectura de aplicaciones OAuth).
Preguntas frecuentes
¿Puedo usar un token de sitio para la API de código personalizado?
No. Los tokens de sitio están excluidos explícitamente de los endpoints de código personalizado. Cualquier solicitud que utilice un token de sitio devuelve un 401. Necesitas una aplicación cliente de datos de Webflow con autorización OAuth y custom_code:read / custom_code:write ámbitos.
¿Qué sucede si hago un PUT con una matriz de scripts vacía?
Elimina todo el código personalizado de esa página. El endpoint PUT trata la scripts matriz como el estado deseado completo. Pasar una matriz vacía equivale a borrar todos los scripts de la página. Este es el comportamiento esperado, no un error.
¿Existe un límite de velocidad en las llamadas a la API de código personalizado?
Sí. Los límites son por clave de API: 60 solicitudes por minuto en los planes Starter y Basic, 120 por minuto en los planes CMS, eCommerce y Business. Consulta los precios de Webflow para ver los detalles actuales de los planes. Al aplicar scripts en cientos de páginas en un bucle, añade un breve retraso entre llamadas para mantenerte por debajo del límite.
¿Puedo actualizar el código fuente de un script registrado después del registro?
No. Los registros son inmutables. Para actualizar el código de un script en línea o la URL de un script alojado, registra una nueva versión con el mismo nombre para mostrar y una version cadena incrementada. Luego, actualiza las referencias a nivel de página a la nueva versión si es necesario. Webflow rastrea los registros por ID y versión por separado.
¿Cómo elimino un script de una página sin borrar todos los scripts?
Obtén (GET) los scripts actuales de la página, filtra el script que deseas eliminar y luego vuelve a enviar (PUT) la matriz filtrada. El PUT con los scripts restantes elimina solo el excluido y deja todo lo demás intacto. El mismo patrón GET-merge-PUT se aplica tanto para añadir como para eliminar.




