Cómo integrar una API de criptomonedas en Webflow Cloud y mostrar precios en tiempo real sin exponer tu clave

Cómo integrar una API de criptomonedas en Webflow Cloud y mostrar precios en tiempo real sin exponer tu clave

Aprende a crear una ruta en Webflow Cloud que oculte tu clave de API de CoinGecko, almacene precios en caché mediante Key Value Store y alimente un ticker en vivo en tu sitio de Webflow.

Cómo integrar una API de criptomonedas en Webflow Cloud y mostrar precios en tiempo real sin exponer tu clave

Ismail Ajagbe
Redactor técnico
Ver perfil del autor
Ismail Ajagbe
Redactor técnico
Ver perfil del autor
Índice de contenidos

Una ruta de Webflow Cloud puede mantener tu clave de API de cripto fuera de la página y servir precios en caché que se ajustan a la cuota del plan gratuito.

La mayoría de los proveedores de datos cripto contabilizan cada solicitud mediante una clave de API. Si pones esa clave en un script de la página, cualquiera puede copiarla desde el código fuente, mientras que el navegador de cada visitante consume tu cuota directamente.

Webflow Cloud ejecuta una aplicación Next.js en una ruta de tu sitio de Webflow, por lo que una ruta de servidor puede guardar la clave mientras el navegador solo interactúa con tu propio dominio.

En este proyecto, crearemos un manejador de rutas que obtiene precios del plan Demo gratuito de CoinGecko, los guarda en el Key Value Store de Webflow Cloud y los envía a un ticker en tu página.

¿Qué necesitas para integrar una API de criptomonedas en Webflow?

Necesitas cuatro cosas:

  • Una clave de API Demo de CoinGecko: Gratis desde la página de precios de API de CoinGecko, que ofrece 10,000 llamadas al mes y requiere atribución en la página donde se muestran los datos.
  • Node.js 22 y npm: La página de bring-your-own-app de Webflow indica que "Actualmente, Webflow Cloud solo admite el gestor de paquetes npm".
  • Next.js 15: Fijado al crear la estructura, para que el proyecto comience en la línea de lanzamiento que admite Webflow Cloud.
  • Un proyecto de Webflow Cloud: Conectado a tu sitio, con una ruta de montaje elegida para la aplicación.

Con estos requisitos listos, puedes configurar el proyecto e integrar la API.

5 pasos para integrar una API de criptomonedas en Webflow Cloud

La estructura es una aplicación Next.js con un manejador de rutas en caché y un componente de cliente. El navegador nunca ve la clave ni el formato de respuesta del proveedor.

1. Prepara la aplicación Next.js con npm

Crea la aplicación con npm y fija Next.js 15, luego añade los dos paquetes que necesita la caché: el asistente OpenNext Cloudflare que expone los enlaces de almacenamiento de Webflow Cloud y las definiciones de tipo de Workers.

Ejecuta estos comandos desde la carpeta donde guardas tus proyectos:

npx create-next-app@15 crypto-prices --ts --app --use-npm --yes
cd crypto-prices
npm install @opennextjs/cloudflare
npm install -D @cloudflare/workers-types

Fijar create-next-app@15 instala la versión más reciente de Next.js 15, que cumple con el requisito de versión mínima de Webflow Cloud sin incluir Next.js 16. El indicador --yes acepta los valores predeterminados, incluido el App Router, por lo que el proyecto queda listo para un manejador de rutas.

El asistente OpenNext solo lee el enlace de almacenamiento; Webflow Cloud proporciona el adaptador durante la compilación, así que no hay nada que configurar.

2. Guarda la clave y declara el Key Value Store

Coloca la clave en las variables de entorno, localmente en .env.local y en el entorno de Webflow Cloud para los despliegues:

  • COINGECKO_API_KEY: Tu clave de demostración. Márcala como secreta en Webflow Cloud y nunca incluyas el prefijo NEXT_PUBLIC_, ya que Next.js envía esos valores al navegador.
  • NEXT_PUBLIC_BASE_PATH: La ruta de montaje, como /prices. Webflow Cloud no crea esta variable y el componente cliente la necesita. Déjala vacía en el entorno local.

CoinGecko responde sin necesidad de una clave, pero las llamadas sin ella están limitadas por dirección IP y comparten ese límite con todas las demás aplicaciones de la misma red; por lo tanto, si la clave falta o está mal escrita, el servicio se degrada silenciosamente en lugar de fallar.

Luego, declara un enlace de almacenamiento de clave-valor añadiendo wrangler.json a la raíz del proyecto.

Webflow Cloud asigna el ID de espacio de nombres real al realizar el despliegue:

{
  "kv_namespaces": [{ "binding": "PRICES_KV", "id": "replace-after-deployment" }]
}

3. Crea el gestor de rutas para el precio en caché

La ruta lee una instantánea almacenada en caché desde el almacenamiento de clave-valor y solo llama a CoinGecko cuando dicha instantánea ha caducado.

Webflow Cloud reemplaza cualquier encabezado Cache-Control que establezca una ruta por private, no-cache, por lo que el almacenamiento en caché del navegador no puede proteger la cuota; este caché del servidor mantiene al proveedor en una solicitud por ventana de caché, sin importar cuántos visitantes realicen consultas.

El endpoint simple price de CoinGecko acepta symbols y un vs_currencies obligatorio, y devuelve los precios identificados por el símbolo en minúsculas.

Implementación de la ruta

Crea :app/api/prices/route.ts

// app/api/prices/route.ts
import { NextResponse } from 'next/server';
import { getCloudflareContext } from '@opennextjs/cloudflare';
import type { KVNamespace } from '@cloudflare/workers-types';

const ALLOWED_SYMBOLS = ['BTC', 'ETH', 'SOL'];
const CACHE_KEY = 'prices:usd';
const CACHE_TTL_SECONDS = 300; // about 8,640 calls a month, inside the Demo plan

type Snapshot = { prices: Record<string, number>; fetchedAt: string };

async function fetchUpstream(): Promise<Snapshot> {
  const url = new URL('https://api.coingecko.com/api/v3/simple/price');
  url.searchParams.set('symbols', ALLOWED_SYMBOLS.join(',').toLowerCase());
  url.searchParams.set('vs_currencies', 'usd');

  const res = await fetch(url, {
    headers: {
      'x-cg-demo-api-key': process.env.COINGECKO_API_KEY ?? '',
      accept: 'application/json',
    },
    signal: AbortSignal.timeout(5000),
  });
  if (!res.ok) throw new Error(`Upstream returned ${res.status}`);

  const body = (await res.json()) as Record<string, { usd?: number }>;
  const prices: Record<string, number> = {};
  for (const symbol of ALLOWED_SYMBOLS) {
    const usd = body[symbol.toLowerCase()]?.usd;
    if (typeof usd === 'number') prices[symbol] = usd;
  }
  return { prices, fetchedAt: new Date().toISOString() };
}

function getKv(): KVNamespace | undefined {
  try {
    const env = getCloudflareContext().env as unknown as { PRICES_KV?: KVNamespace };
    return env.PRICES_KV;
  } catch {
    return undefined; // no bindings under plain `next dev`
  }
}

export async function GET(request: Request) {
  if (!process.env.COINGECKO_API_KEY) {
    return NextResponse.json({ error: 'Price service is not configured' }, { status: 500 });
  }

  const requested = [
    ...new Set(
      (new URL(request.url).searchParams.get('symbols') ?? '')
        .split(',')
        .map((s) => s.trim().toUpperCase())
        .filter((s) => ALLOWED_SYMBOLS.includes(s)),
    ),
  ];
  if (requested.length === 0) {
    return NextResponse.json({ error: 'No supported symbols requested' }, { status: 400 });
  }

  const kv = getKv();
  let snapshot = kv ? await kv.get<Snapshot>(CACHE_KEY, 'json') : null;
  if (!snapshot) {
    try {
      snapshot = await fetchUpstream();
      await kv?.put(CACHE_KEY, JSON.stringify(snapshot), { expirationTtl: CACHE_TTL_SECONDS });
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Upstream request failed';
      return NextResponse.json({ error: message }, { status: 502 });
    }
  }

  const prices = Object.fromEntries(requested.map((s) => [s, snapshot.prices[s] ?? null]));
  return NextResponse.json({ symbols: requested, prices, fetchedAt: snapshot.fetchedAt });
}

La ruta es pública porque un ticker de precios no tiene un usuario con sesión iniciada, y la lista de permitidos significa que un visitante solo puede solicitar las monedas que tú elijas. Todas las monedas comparten una misma clave de caché, por lo que una página con mucho tráfico solo genera una escritura en el almacenamiento de clave-valor por ventana de caché, en lugar de una por cada moneda.

Omite export const runtime = 'edge' del archivo: la página de "bring-your-own-app" todavía lo sugiere para las rutas de API, pero el adaptador de OpenNext para Cloudflare que utiliza Webflow Cloud no es compatible con ese destino de tiempo de ejecución de Next.js.

Ejecuta npm run dev y solicita http://localhost:3000/api/prices?symbols=BTC,ETH. Bajo next dev, no existe un almacenamiento de clave-valor, por lo que cada solicitud llega a CoinGecko; el JSON debería mostrar el precio de cada moneda y una marca de tiempo fetchedAt.

4. Renderiza precios en tiempo real en un componente cliente

El ticker consulta la ruta cada dos minutos. Los datos de demostración de CoinGecko se actualizan aproximadamente una vez por minuto y la ruta se almacena en caché durante cinco minutos, por lo que consultar con mayor frecuencia solo repetiría la misma instantánea.

La página de "bring-your-own-app" indica que "las llamadas de obtención en el lado del cliente deben incluir manualmente la ruta base para llegar correctamente a tus endpoints", por lo que el componente añade la ruta de montaje como prefijo a cada solicitud.

Crea :app/components/PriceTicker.tsx

// app/components/PriceTicker.tsx
'use client';

import { useEffect, useState } from 'react';

type PriceResponse = {
  symbols: string[];
  prices: Record<string, number | null>;
  fetchedAt: string;
};

const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH ?? '';
const REFRESH_MS = 120_000;

const usd = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 2 });
const usdSmall = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumSignificantDigits: 4 });

function formatValue(value: number | null | undefined): string {
  if (typeof value !== 'number') return 'n/a';
  return value >= 1 ? usd.format(value) : usdSmall.format(value);
}

export default function PriceTicker({ symbols }: { symbols: string[] }) {
  const key = symbols.join(',');
  const [data, setData] = useState<PriceResponse | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let cancelled = false;

    async function load() {
      try {
        const res = await fetch(`${BASE_PATH}/api/prices?symbols=${encodeURIComponent(key)}`);
        const body = await res.json();
        if (!res.ok) throw new Error(body.error ?? `Request failed with ${res.status}`);
        if (!cancelled) {
          setData(body as PriceResponse);
          setError(null);
        }
      } catch (err) {
        if (!cancelled) setError(err instanceof Error ? err.message : 'Unknown error');
      }
    }

    load();
    const interval = setInterval(load, REFRESH_MS);
    return () => {
      cancelled = true;
      clearInterval(interval);
    };
  }, [key]);

  if (error) return <p role="alert">Prices unavailable: {error}</p>;
  if (!data) return <p>Loading prices…</p>;

  return (
    <div>
      <ul>
        {data.symbols.map((symbol) => (
          <li key={symbol}>
            <strong>{symbol}</strong>: {formatValue(data.prices[symbol])}
          </li>
        ))}
      </ul>
      <small>
        Updated {new Date(data.fetchedAt).toLocaleTimeString()}. Data provided by{' '}
        <a href="https://www.coingecko.com/en/api">CoinGecko</a>. Prices may be delayed and are not
        financial advice.
      </small>
    </div>
  );
}

Los precios inferiores a un dólar utilizan dígitos significativos, de modo que una moneda que vale menos de un centavo muestra su valor real en lugar de redondearse a cero. La línea de atribución cumple con el requisito de CoinGecko para el plan gratuito, y la nota sobre los retrasos evita que el ticker se interprete como una cotización comercial.

Reemplaza la página inicial en app/page.tsx para que renderice el ticker:

// app/page.tsx
import PriceTicker from './components/PriceTicker';

export default function Home() {
  return (
    <main>
      <h1>Live crypto prices</h1>
      <PriceTicker symbols={['BTC', 'ETH', 'SOL']} />
    </main>
  );
}

Recarga http://localhost:3000 y deberías ver una fila por cada moneda con el precio formateado y la línea de atribución.

5. Despliega en Webflow Cloud y monta la aplicación en tu sitio

Crea un proyecto en Webflow Cloud conectado a tu repositorio y elige una ruta de montaje, siguiendo la guía de "bring-your-own-app" de Webflow. Añade ambas variables del paso 2 al entorno de Webflow Cloud, marcando la clave como secreta y configurando NEXT_PUBLIC_BASE_PATH con la ruta de montaje exacta.

La página de variables de entorno de Webflow indica que "tanto las variables de entorno secretas como las no secretas están disponibles para el proceso de compilación de su aplicación y para la aplicación desplegada en tiempo de ejecución", por lo que Next.js puede integrar el valor público durante la compilación.

Despliegue después de guardar las variables. Webflow Cloud crea el espacio de nombres del almacén de clave-valor a partir de wrangler.json en el primer despliegue. En el Designer, añada un enlace en la barra de navegación o en el pie de página a la ruta de montaje para que los visitantes puedan acceder al ticker desde sus páginas existentes.

Abra su sitio en la ruta de montaje y debería ver el ticker. En la pestaña de red del navegador, la solicitud a la ruta de montaje más /api/prices debería devolver un 200, y las solicitudes repetidas en un plazo de cinco minutos deberían devolver el mismo valor fetchedAt, lo que confirma que la caché funciona.

¿Qué provoca que falle una integración de API de criptomonedas en Webflow Cloud?

La mayoría de los fallos provienen de la compilación o de la ruta de montaje; el resto se debe a la cuota. El valor fetchedAt en la respuesta de la ruta es la forma más rápida de saber si la caché está haciendo su trabajo.

La compilación falla después de añadir export const runtime = 'edge' a la ruta

Causa: Webflow Cloud despliega Next.js a través del adaptador OpenNext Cloudflare, que no es compatible con el destino de tiempo de ejecución edge de Next.js. La página de "bring-your-own-app" describe que Webflow Cloud despliega "usando el tiempo de ejecución Edge", refiriéndose a la plataforma Cloudflare Workers, y aun así indica añadir la directiva a las rutas de la API.

Solución: Elimine la exportación runtime de cada archivo de ruta; la ruta ya se ejecuta en Workers. Si añade middleware más adelante, manténgalo en middleware.ts, ya que la documentación de personalización del framework de Webflow indica que "el middleware de tiempo de ejecución de Node.js no es compatible. Solo el middleware de tiempo de ejecución Edge funciona en el entorno de ejecución de Workers".

El ticker muestra un error 404 o un error de análisis JSON en producción, pero funciona en localhost

Causa: El navegador está solicitando /api/prices en la raíz de su sitio, pero la aplicación está montada bajo una ruta. La respuesta es la página HTML 404 del sitio de Webflow, que el componente no puede analizar como JSON.

Solución: Compruebe la URL de la solicitud fallida en la pestaña de red. Si falta la ruta de montaje, establezca NEXT_PUBLIC_BASE_PATH en la ruta de montaje exacta en el entorno de Webflow Cloud y vuelva a desplegar, ya que Next.js integra los valores públicos en tiempo de compilación.

La ruta devuelve "Price service is not configured" después del despliegue

Causa: COINGECKO_API_KEY existe en .env.local pero no en el entorno de Webflow Cloud en el que realizó el despliegue.

Solución: Añada la clave a ese entorno como un secreto y vuelva a desplegar. Los valores secretos se ocultan en los registros de compilación, así que confirme el valor pegándolo de nuevo en lugar de leer un registro.

El ticker muestra "Prices unavailable: Upstream returned 429"

Causa: CoinGecko está limitando la tasa de la clave y la caché no está absorbiendo el tráfico. Por lo general, falta wrangler.json o su nombre de enlace difiere de PRICES_KV, por lo que getKv no encuentra ningún almacén y cada sondeo desde cada pestaña abierta llega al proveedor.

Solución: Compruebe que wrangler.json se encuentra en la raíz del proyecto con el enlace PRICES_KV y vuelva a desplegar. Las solicitudes repetidas deberían devolver entonces el mismo fetchedAt. Si la caché funciona y sigue alcanzando el límite, aumente CACHE_TTL_SECONDS para ajustarse a la asignación mensual.

Qué más puede construir con una API de criptomonedas y Webflow

Un segundo controlador de ruta puede devolver precios históricos para un gráfico, utilizando su propia clave de caché para no compartir la ventana del ticker. El contenido más lento alrededor de los números, como los perfiles de monedas y los comentarios de mercado, puede residir en el CMS de Webflow, donde los editores publican sin tocar la aplicación.

Para recibir eventos de otros servicios en la misma aplicación, la guía de webhooks cubre las comprobaciones de firma en Webflow Cloud.

Para las opciones de almacenamiento en las que se basa esta ruta, la documentación para desarrolladores de Webflow cubre Key Value Store y SQLite en Webflow Cloud.

Preguntas frecuentes

¿Puedo construir esto con Astro o Vite en lugar de Next.js?

Sí, con Astro, que Webflow Cloud admite desde la versión 5 y que lee los enlaces mediante locals en lugar de getCloudflareContext. El enfoque es el mismo: mantén la clave en un endpoint del servidor y haz que el navegador llame solo a ese endpoint. Una aplicación de página única de Vite no tiene rutas de servidor, por lo que necesitaría un endpoint independiente para alojar la clave.

¿Puedo almacenar los precios en el CMS de Webflow en lugar de en la aplicación?

Puedes escribir precios en el CMS a través de la API REST de Webflow, pero los precios que cambian cada minuto implicarían escrituras y republicaciones constantes. Yo mantengo las cotizaciones en tiempo real en la aplicación de Webflow Cloud y uso el CMS para contenido que cambia más despacio, como los perfiles de las monedas.

¿Puedo usar CoinMarketCap en lugar de CoinGecko?

Sí. Cambia fetchUpstream para llamar al endpoint de cotizaciones de CoinMarketCap con el encabezado X-CMC_PRO_API_KEY y asigna su respuesta al mismo { prices, fetchedAt } snapshot. Como la ruta normaliza la respuesta, el componente del ticker no necesita cambios.

¿Puedo cargar metadatos de monedas desde un archivo JSON local con node:fs?

No. El entorno de ejecución Workers de Webflow Cloud no tiene acceso al sistema de archivos, como señala su página de compatibilidad con Node.js. Importa el archivo JSON como un módulo, para que el empaquetador lo incluya en el momento de la compilación y la ruta lo lea como cualquier otra importación.


Última actualización
October 24, 2026
Categoría

Artículos relacionados


verifone logomonday.com logospotify logoted logogreenhouse logoclear logocheckout.com logoreddit logothe new york times logoideo logoupwork logodiscord logo
verifone logomonday.com logospotify logoted logogreenhouse logoclear logocheckout.com logoreddit logothe new york times logoideo logoupwork logodiscord logo

Empieza gratis

Prueba Webflow todo el tiempo que quieras con nuestro plan Starter gratuito. Compra un plan de sitio de pago para publicar, alojar y desbloquear funciones adicionales.

Empieza, es gratis
Ver demostración

Prueba Webflow todo el tiempo que quieras con nuestro plan Starter gratuito. Compra un plan de sitio de pago para publicar, alojar y desbloquear funciones adicionales.