Crea tu Propia API con n8n: Guía Completa 2025

¿Quieres llevar tus automatizaciones con n8n al siguiente nivel? Crear tu propia API personalizada es el paso definitivo para conectar scripts, servicios y flujos a medida. En esta guía completa aprenderás 3 formas de crear APIs con n8n: usando Webhooks nativos, conectando con APIs Python/Flask externas, y exponiendo workflows como endpoints RESTful.

En esta guía aprenderás:

  • Crear APIs REST con webhooks de n8n (sin código)
  • Conectar n8n con APIs Python/Flask personalizadas
  • Configurar autenticación (Bearer, API Key, OAuth)
  • Manejar errores y validación de datos
  • Deploy en producción con HTTPS
  • Ejemplos prácticos copy-paste

⚠️ Antes de empezar: Esta guía asume que ya conoces n8n básico. Si eres nuevo, empieza con nuestra Guía Completa de n8n 2025 (V2) que cubre desde cero hasta nivel experto.

🎯 ¿Por Qué Crear tu Propia API?

Casos de uso reales donde una API propia es la mejor solución:

  • Automatizar tareas no cubiertas por nodos estándar – Procesar formatos propietarios, interactuar con hardware local, ejecutar scripts complejos
  • Centralizar lógica de negocio – Un endpoint /calcular-precio que usan múltiples workflows
  • Integrar con sistemas legacy – Conectar n8n con mainframes, bases de datos antiguas, protocolos no-HTTP
  • Procesar datos pesados fuera de n8n – Machine learning, análisis de imágenes, procesamiento de vídeo
  • Crear microservicios reutilizables – Un servicio de validación de emails usado por 10 workflows distintos
  • Exponer workflows como API pública – Tu workflow de análisis de sentimiento como servicio para terceros

📋 Requisitos Previos

Antes de comenzar necesitas:

  1. n8n instalado y funcionando – Si no lo tienes, consulta nuestra guía de instalación autoalojada
  2. Acceso a terminal – Para crear APIs Python/Flask (opcional si solo usas webhooks)
  3. Python 3.8+ – Si vas a crear API externa (verifica con python3 --version)
  4. Conocimientos básicos de HTTP – GET, POST, JSON, códigos de estado

🚀 Método 1: API con Webhooks de n8n (Sin Código)

Esta es la forma más rápida de crear una API: usa el nodo Webhook de n8n para recibir peticiones HTTP y el nodo Respond to Webhook para devolver respuestas.

Paso 1: Crear Endpoint Básico

1. Crea un nuevo workflow en n8n

2. Añade un nodo Webhook

3. Configura:

  • HTTP Method: POST
  • Path: procesar-texto
  • Authentication: None (por ahora)
  • Respond: Using ‘Respond to Webhook’ Node

4. Añade tu lógica de procesamiento (nodos Code, Set, etc)

5. Añade nodo Respond to Webhook al final

6. Configura la respuesta:

  • Respond With: JSON
  • Response Body: {{ $json }}

7. Activa el workflow

Probar tu API

Tu endpoint estará disponible en:

https://tu-n8n.com/webhook/procesar-texto

Pruébalo con curl:

curl -X POST https://tu-n8n.com/webhook/procesar-texto \
  -H "Content-Type: application/json" \
  -d '{"texto": "Hola mundo"}'

Ejemplo Completo: API de Procesamiento de Texto

Workflow que recibe texto, lo procesa y devuelve resultado:

{
  "nodes": [
    {
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [250, 300],
      "parameters": {
        "path": "procesar-texto",
        "httpMethod": "POST",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "name": "Validar Input",
      "type": "n8n-nodes-base.code",
      "position": [450, 300],
      "parameters": {
        "jsCode": "// Validar que existe el campo 'texto'\nconst texto = $input.item.json.texto;\n\nif (!texto || typeof texto !== 'string') {\n  throw new Error('Campo texto es requerido y debe ser string');\n}\n\nif (texto.length > 1000) {\n  throw new Error('Texto muy largo (máximo 1000 caracteres)');\n}\n\nreturn { texto };"
      }
    },
    {
      "name": "Procesar",
      "type": "n8n-nodes-base.code",
      "position": [650, 300],
      "parameters": {
        "jsCode": "const texto = $input.item.json.texto;\n\nreturn {\n  original: texto,\n  uppercase: texto.toUpperCase(),\n  lowercase: texto.toLowerCase(),\n  wordCount: texto.split(/\\s+/).length,\n  charCount: texto.length,\n  slug: texto.toLowerCase().replace(/\\s+/g, '-'),\n  timestamp: new Date().toISOString()\n};"
      }
    },
    {
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [850, 300],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}"
      }
    }
  ],
  "connections": {
    "Webhook": { "main": [[{ "node": "Validar Input", "type": "main", "index": 0 }]] },
    "Validar Input": { "main": [[{ "node": "Procesar", "type": "main", "index": 0 }]] },
    "Procesar": { "main": [[{ "node": "Respond to Webhook", "type": "main", "index": 0 }]] }
  }
}

🔐 Añadir Autenticación a tu API

Opción 1: Header Authentication (Recomendado)

En el nodo Webhook, configura:

  • Authentication: Header Auth
  • Header Name: X-API-Key
  • Header Value: tu_secreto_seguro_123

Uso del cliente:

curl -X POST https://tu-n8n.com/webhook/procesar-texto \
  -H "X-API-Key: tu_secreto_seguro_123" \
  -H "Content-Type: application/json" \
  -d '{"texto": "Hola mundo"}'

Opción 2: Bearer Token (Estándar Industria)

Usa un nodo Code después del Webhook para validar:

// Validar Bearer Token
const authHeader = $input.item.headers.authorization;

if (!authHeader || !authHeader.startsWith('Bearer ')) {
  throw new Error('Token de autenticación requerido');
}

const token = authHeader.substring(7);
const validToken = 'tu_token_secreto_aqui';

if (token !== validToken) {
  throw new Error('Token inválido');
}

// Si llegamos aquí, está autenticado
return $input.item.json;

Uso del cliente:

curl -X POST https://tu-n8n.com/webhook/procesar-texto \
  -H "Authorization: Bearer tu_token_secreto_aqui" \
  -H "Content-Type: application/json" \
  -d '{"texto": "Hola"}'

Opción 3: Query Parameter (Solo para Testing)

Menos seguro, pero útil para pruebas:

// En nodo Code después de Webhook
const apiKey = $input.item.query.api_key;

if (apiKey !== 'tu_api_key_secreta') {
  throw new Error('API key inválida');
}

return $input.item.json;

Uso: https://tu-n8n.com/webhook/test?api_key=tu_api_key_secreta

🐍 Método 2: Conectar n8n con API Python/Flask

Para lógica compleja (machine learning, procesamiento pesado), crea una API externa en Python y conéctala con n8n.

Paso 1: Crear API con Flask

# api.py
from flask import Flask, request, jsonify
from flask_cors import CORS
import os

app = Flask(__name__)
CORS(app)  # Permitir peticiones desde n8n

# Token de autenticación (usar variable de entorno en producción)
API_TOKEN = os.getenv('API_TOKEN', 'dev_token_inseguro')

def validate_token():
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return False
    token = auth_header.split(' ')[1]
    return token == API_TOKEN

@app.route('/health', methods=['GET'])
def health():
    return jsonify({"status": "ok", "version": "1.0.0"})

@app.route('/procesar-texto', methods=['POST'])
def procesar_texto():
    # Validar autenticación
    if not validate_token():
        return jsonify({"error": "Token inválido"}), 401

    # Validar request
    if not request.json or 'texto' not in request.json:
        return jsonify({"error": "Campo 'texto' requerido"}), 400

    texto = request.json['texto']

    # Validar tipo y longitud
    if not isinstance(texto, str):
        return jsonify({"error": "texto debe ser string"}), 400

    if len(texto) > 5000:
        return jsonify({"error": "Texto muy largo (max 5000 chars)"}), 400

    # Procesar (aquí iría tu lógica compleja)
    resultado = {
        "original": texto,
        "procesado": texto.upper().replace(" ", "_"),
        "longitud": len(texto),
        "palabras": len(texto.split()),
        "metadata": {
            "version": "1.0.0",
            "timestamp": "2025-01-01T00:00:00Z"
        }
    }

    return jsonify(resultado), 200

@app.route('/analizar-sentimiento', methods=['POST'])
def analizar_sentimiento():
    if not validate_token():
        return jsonify({"error": "Token inválido"}), 401

    if not request.json or 'texto' not in request.json:
        return jsonify({"error": "Campo 'texto' requerido"}), 400

    texto = request.json['texto']

    # Aquí integrarías un modelo de ML real
    # Por ahora, análisis simple de ejemplo
    palabras_positivas = ['bueno', 'excelente', 'genial', 'fantástico']
    palabras_negativas = ['malo', 'terrible', 'horrible', 'pésimo']

    texto_lower = texto.lower()
    score_positivo = sum(1 for p in palabras_positivas if p in texto_lower)
    score_negativo = sum(1 for p in palabras_negativas if p in texto_lower)

    if score_positivo > score_negativo:
        sentimiento = 'positivo'
    elif score_negativo > score_positivo:
        sentimiento = 'negativo'
    else:
        sentimiento = 'neutral'

    return jsonify({
        "texto": texto,
        "sentimiento": sentimiento,
        "confianza": abs(score_positivo - score_negativo) / max(len(texto.split()), 1),
        "detalles": {
            "palabras_positivas": score_positivo,
            "palabras_negativas": score_negativo
        }
    })

@app.errorhandler(404)
def not_found(error):
    return jsonify({"error": "Endpoint no encontrado"}), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({"error": "Error interno del servidor"}), 500

if __name__ == '__main__':
    # Desarrollo
    app.run(host='0.0.0.0', port=5000, debug=True)

    # Producción: usar gunicorn
    # gunicorn -w 4 -b 0.0.0.0:5000 api:app

Paso 2: Instalar Dependencias

# requirements.txt
Flask==3.0.0
flask-cors==4.0.0
gunicorn==21.2.0

# Instalar
pip3 install -r requirements.txt

Paso 3: Ejecutar API

# Desarrollo
python3 api.py

# Producción con Gunicorn (4 workers)
export API_TOKEN="tu_token_super_secreto"
gunicorn -w 4 -b 0.0.0.0:5000 api:app

# Con systemd (persistente)
sudo nano /etc/systemd/system/mi-api.service

Contenido del archivo service:

[Unit]
Description=Mi API para n8n
After=network.target

[Service]
Type=simple
User=tu_usuario
WorkingDirectory=/home/tu_usuario/mi-api
Environment="API_TOKEN=tu_token_super_secreto"
ExecStart=/home/tu_usuario/mi-api/venv/bin/gunicorn -w 4 -b 0.0.0.0:5000 api:app
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable mi-api
sudo systemctl start mi-api
sudo systemctl status mi-api

Paso 4: Conectar desde n8n

En n8n, añade nodo HTTP Request:

  • Method: POST
  • URL: http://localhost:5000/procesar-texto
  • Authentication: Generic Credential Type → Header Auth
  • Send Headers: Yes
  • Header Parameters:
    • Name: Authorization
    • Value: Bearer tu_token_super_secreto
  • Send Body: Yes
  • Body Content Type: JSON
  • Specify Body: Using JSON
  • JSON Body:
    {
      "texto": "{{ $json.texto }}"
    }

📊 Comparativa: Webhook n8n vs API Externa

Característica Webhook n8n API Python Externa
Facilidad Setup Muy fácil (GUI) Media (código)
Performance Buena (workflows simples) Excelente (optimizable)
Lógica Compleja Limitada (JavaScript) Ilimitada (Python completo)
ML/AI Integration Difícil Fácil (bibliotecas Python)
Mantenimiento Fácil (visual) Medio (requiere deploy)
Escalabilidad Media (limitado por n8n) Alta (workers, load balancer)
Uso Recomendado APIs simples, prototipos Producción, lógica pesada

⚙️ Manejo Avanzado de Errores

En Webhooks n8n

Usa nodos Error Trigger para capturar fallos:

// En nodo Code con manejo de errores
try {
  const texto = $input.item.json.texto;

  if (!texto) {
    throw new Error('Campo texto requerido');
  }

  // Procesar...
  return { resultado: texto.toUpperCase() };

} catch (error) {
  return {
    error: true,
    message: error.message,
    timestamp: new Date().toISOString()
  };
}

Añade nodo Error Trigger conectado a Respond to Webhook para errores:

// En nodo Code del Error Trigger
return {
  status: 'error',
  code: 500,
  message: $json.error.message,
  timestamp: new Date().toISOString()
};

En API Flask

from functools import wraps
from flask import jsonify

def handle_errors(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except ValueError as e:
            return jsonify({"error": str(e)}), 400
        except Exception as e:
            app.logger.error(f"Error en {f.__name__}: {e}")
            return jsonify({"error": "Error interno"}), 500
    return decorated_function

@app.route('/procesar', methods=['POST'])
@handle_errors
def procesar():
    data = request.json
    if not data:
        raise ValueError("Body JSON requerido")

    # Tu lógica aquí...

🔒 Seguridad y Buenas Prácticas

1. HTTPS Obligatorio en Producción

Usa Nginx como reverse proxy con Let’s Encrypt:

# /etc/nginx/sites-available/mi-api
server {
    listen 80;
    server_name api.tudominio.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.tudominio.com;

    ssl_certificate /etc/letsencrypt/live/api.tudominio.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.tudominio.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

2. Rate Limiting

En Flask con flask-limiter:

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    app=app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/procesar', methods=['POST'])
@limiter.limit("10 per minute")
def procesar():
    ...

3. Validación de Input

Con Pydantic (recomendado):

from pydantic import BaseModel, validator

class TextoRequest(BaseModel):
    texto: str
    idioma: str = "es"

    @validator('texto')
    def texto_no_vacio(cls, v):
        if not v or not v.strip():
            raise ValueError('Texto no puede estar vacío')
        if len(v) > 10000:
            raise ValueError('Texto muy largo (max 10000 chars)')
        return v.strip()

@app.route('/procesar', methods=['POST'])
def procesar():
    try:
        data = TextoRequest(**request.json)
        # Usar data.texto y data.idioma
    except Exception as e:
        return jsonify({"error": str(e)}), 400

4. Logging Completo

import logging
from logging.handlers import RotatingFileHandler

# Configurar logging
handler = RotatingFileHandler('api.log', maxBytes=10000000, backupCount=3)
handler.setFormatter(logging.Formatter(
    '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)

@app.route('/procesar', methods=['POST'])
def procesar():
    app.logger.info(f"Request from {request.remote_addr}")
    # Tu lógica...

🔗 Recursos Adicionales

Para profundizar más en automatización con n8n:

❓ Preguntas Frecuentes

¿Webhook n8n o API Python externa?

Webhook n8n: perfecto para APIs simples, prototipos rápidos, o cuando solo necesitas lógica básica (transformar datos, consultar APIs externas). API Python externa: necesaria cuando tienes lógica compleja (ML, análisis de imágenes), necesitas bibliotecas específicas de Python, o quieres máxima performance y escalabilidad. Regla práctica: si cabe en 50 líneas de JavaScript, usa webhook n8n.

¿Cómo manejar autenticación OAuth en mi API?

Para OAuth completo (con refresh tokens), es mejor usar una API externa (Flask/FastAPI) con bibliotecas como Authlib o requests-oauthlib. En n8n, el nodo HTTP Request soporta OAuth2 pero está más pensado para consumir APIs OAuth que para ser un servidor OAuth. Para servir una API con OAuth, implementa el flujo en Flask/FastAPI y conéctala desde n8n.

¿Cómo hacer que mi API sea más rápida?

Webhooks n8n: minimiza nodos en el workflow, usa nodos Code eficientes (evita bucles grandes), cachea resultados si es posible. API Python: usa async con FastAPI en vez de Flask, implementa caché con Redis, usa workers Gunicorn (regla: 2-4 workers por CPU core), optimiza queries de base de datos, implementa rate limiting para evitar sobrecarga.

¿Puedo exponer mi workflow n8n como API pública?

Sí, pero con precauciones: 1) Siempre usa autenticación (Header Auth o Bearer Token), 2) Implementa rate limiting (puedes usar Nginx con limit_req), 3) Valida TODOS los inputs en el primer nodo Code, 4) Usa HTTPS obligatoriamente, 5) Monitoriza uso (logs, alertas), 6) Considera usar un API Gateway (Kong, Tyk) delante para features empresariales.

¿Cómo versionar mi API?

En webhooks n8n: usa diferentes paths (/v1/procesar, /v2/procesar) con workflows separados. En API Python: incluye versión en URL (/api/v1/procesar, /api/v2/procesar) y mantén compatibilidad hacia atrás el mayor tiempo posible. Documenta breaking changes claramente. Usa semantic versioning (1.0.0, 1.1.0, 2.0.0).

¿Dónde guardar tokens y secretos?

NUNCA en el código o workflows. Opciones seguras: 1) Variables de entorno del sistema, 2) n8n Environment Variables (Settings → Variables), 3) Servicios de secretos (HashiCorp Vault, AWS Secrets Manager), 4) Archivos .env con permisos restrictivos (chmod 600), 5) Para producción seria: usa un secret manager siempre.

¿Cómo testear mi API antes de producción?

Webhooks n8n: usa URLs de Test Webhook (tienen /webhook-test/ en vez de /webhook/), haz requests con Postman/Insomnia. API Python: usa pytest con cliente de pruebas Flask, implementa tests unitarios para cada endpoint, usa Thunder Client (VS Code) o curl para tests manuales. Siempre testea casos edge: inputs vacíos, muy largos, tipos incorrectos.

¿Puedo usar mi API con clientes externos (móvil, web)?

Sí, pero considera: 1) Implementa CORS correctamente (flask-cors), 2) Usa autenticación robusta (JWT mejor que API keys simples), 3) Documenta tu API (Swagger/OpenAPI), 4) Implementa rate limiting estricto, 5) Valida origin de requests si es sensible, 6) Considera usar un CDN/API Gateway para distribución global.

¿Cómo documentar mi API?

Para API Flask/FastAPI: usa Swagger/OpenAPI (FastAPI lo genera automáticamente, Flask usa flask-swagger-ui). Para webhooks n8n: crea un README.md documentando endpoints, métodos, headers requeridos, ejemplos de request/response, códigos de error posibles. Incluye ejemplos curl completos. Mantén documentación en sync con código usando herramientas como Postman Collections exportables.

¿Cómo migrar de webhook n8n a API Python?

1) Documenta exactamente qué hace tu webhook actual (inputs, outputs, lógica), 2) Implementa la misma funcionalidad en Python paso a paso, 3) Mantén ambas APIs corriendo en paralelo durante testing, 4) Usa feature flags o traffic splitting para migrar gradualmente, 5) Actualiza consumers uno a uno, 6) Solo retira webhook cuando 100% traffic esté en Python API.

¿Mi API puede llamar a otros workflows de n8n?

Sí, de dos formas: 1) Usa el nodo Execute Workflow (desde dentro de n8n), 2) Llama a webhooks de otros workflows vía HTTP Request. También puedes usar la n8n API REST (requiere API key) para ejecutar workflows programáticamente desde Python: POST a /api/v1/workflows/{id}/execute con headers X-N8N-API-KEY.

¿Cómo monitorizar mi API en producción?

Implementa: 1) Health check endpoint (/health devuelve status:ok), 2) Logging completo (requests, errores, tiempos), 3) Métricas (Prometheus + Grafana), 4) Alertas (PagerDuty, Opsgenie), 5) Uptime monitoring (UptimeRobot, Pingdom), 6) Application Performance Monitoring (APM) con Sentry o New Relic. En n8n: usa workflow con Schedule Trigger que verifica tu API cada 5 minutos.

💡 Conclusión

Crear tu propia API con n8n o conectarla con APIs Python te da control total sobre tus automatizaciones. Usa webhooks n8n para APIs simples y prototipos, y APIs Python/Flask cuando necesites lógica compleja o máxima performance.

Checklist antes de ir a producción:

  • ✅ Autenticación implementada y testeada
  • ✅ HTTPS configurado (Let’s Encrypt)
  • ✅ Validación de inputs en todos los endpoints
  • ✅ Manejo de errores completo
  • ✅ Rate limiting activo
  • ✅ Logging configurado
  • ✅ Documentación API completa
  • ✅ Tests (unitarios + integración)
  • ✅ Monitorización y alertas
  • ✅ Backup y recovery plan

¿Problemas con tu API? Únete a nuestro Discord donde la comunidad te ayudará.

Por ziru

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x
El Diario IA
Resumen de privacidad

Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.