DEV Community

Cover image for Cómo Usar la API de HubSpot en 2026
Roobia
Roobia

Posted on • Originally published at apidog.com

Cómo Usar la API de HubSpot en 2026

TL;DR

La API de HubSpot permite a los desarrolladores integrarse programáticamente con los hubs de CRM, marketing, ventas y servicio. Utiliza autenticación OAuth 2.0 y de aplicaciones privadas, endpoints RESTful para contactos, empresas, negocios, tickets y más, con límites de tasa basados en el nivel de suscripción. Esta guía cubre la configuración de autenticación, los endpoints principales, los webhooks y las estrategias de integración en producción.

Prueba Apidog hoy

Introducción

HubSpot gestiona más de 194.000 cuentas de clientes y miles de millones de registros de CRM. Para desarrolladores que crean integraciones de CRM, automatización de marketing o herramientas de ventas, la integración con la API de HubSpot es esencial para llegar a más de 7 millones de usuarios.

Las empresas pierden entre 15 y 20 horas semanales en la introducción manual de datos entre sistemas. Una integración sólida con la API de HubSpot automatiza la sincronización de contactos, actualizaciones de negocios, flujos de trabajo de marketing y reportes entre plataformas.

💡 Apidog simplifica las pruebas de integración de API. Prueba tus endpoints de HubSpot, valida flujos de OAuth, inspecciona cargas útiles de webhook y depura problemas de autenticación en un solo espacio de trabajo. Importa especificaciones de API, simula respuestas y comparte escenarios de prueba con tu equipo.

¿Qué es la API de HubSpot?

HubSpot proporciona una API RESTful para acceder a datos de CRM y funciones de automatización de marketing. Con esta API puedes gestionar:

  • Contactos, empresas, negocios, tickets y objetos personalizados
  • Correos electrónicos de marketing y páginas de destino
  • Embudo de ventas y secuencias
  • Tickets de servicio y conversaciones
  • Análisis e informes
  • Flujos de trabajo y automatización
  • Archivos y activos

Características Clave

Característica Descripción
Diseño RESTful Métodos HTTP estándar con respuestas JSON
OAuth 2.0 + Apps Privadas Opciones de autenticación flexibles
Webhooks Notificaciones en tiempo real para cambios de objetos
Limitación de Tasa Límites basados en niveles (100-400 solicitudes/segundo)
Objetos CRM Soporte para objetos estándar y personalizados
Asociaciones Vincular objetos (contacto-empresa, negocio-contacto)
Propiedades Campos personalizados para cualquier tipo de objeto
API de Búsqueda Filtrado y clasificación complejos

Descripción General de la Arquitectura de la API

HubSpot utiliza APIs REST versionadas:

https://api.hubapi.com/
Enter fullscreen mode Exit fullscreen mode

Versiones de la API Comparadas

Versión Estado Autenticación Caso de Uso
CRM API v3 Actual OAuth 2.0, App Privada Todas las nuevas integraciones
Automation API v4 Actual OAuth 2.0, App Privada Inscripción en flujos de trabajo
Marketing Email API Actual OAuth 2.0, App Privada Campañas de email
Contacts API v1 Obsoleta Clave API (legado) Migrar a v3
Companies API v1 Obsoleta Clave API (legado) Migrar a v3

Importante: HubSpot ha dejado de usar la autenticación con clave API en favor de OAuth 2.0 y las aplicaciones privadas. Migra todas tus integraciones inmediatamente.

Primeros Pasos: Configuración de Autenticación

Paso 1: Crea tu Cuenta de Desarrollador de HubSpot

  1. Visita el Portal de Desarrolladores de HubSpot
  2. Inicia sesión con tu cuenta de HubSpot (o crea una)
  3. Ve a Apps en el panel de desarrolladores
  4. Haz clic en Crear app

Paso 2: Elige el Método de Autenticación

HubSpot soporta dos métodos de autenticación:

Método Mejor para Nivel de Seguridad
OAuth 2.0 Aplicaciones multi-tenant, integraciones públicas Alto (tokens con alcance de usuario)
App Privada Integraciones internas, portal único Alto (token con alcance de portal)

Paso 3: Configura una Aplicación Privada (Integraciones Internas)

  1. Ve a Configuración > Integraciones > Aplicaciones Privadas
  2. Haz clic en Crear una aplicación privada
  3. Configura los ámbitos (scopes):
contacts
crm.objects.companies
crm.objects.deals
crm.objects.tickets
automation
webhooks
Enter fullscreen mode Exit fullscreen mode
  1. Genera un token de acceso y guárdalo de forma segura.
# archivo .env
HUBSPOT_ACCESS_TOKEN="pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
HUBSPOT_PORTAL_ID="12345678"
Enter fullscreen mode Exit fullscreen mode

Paso 4: Configura OAuth 2.0 (Aplicaciones Multi-tenant)

Configura OAuth para acceso a múltiples portales:

const HUBSPOT_CLIENT_ID = process.env.HUBSPOT_CLIENT_ID;
const HUBSPOT_CLIENT_SECRET = process.env.HUBSPOT_CLIENT_SECRET;
const HUBSPOT_REDIRECT_URI = process.env.HUBSPOT_REDIRECT_URI;

// Construir URL de autorización
const getAuthUrl = (state) => {
  const params = new URLSearchParams({
    client_id: HUBSPOT_CLIENT_ID,
    redirect_uri: HUBSPOT_REDIRECT_URI,
    scope: 'crm.objects.contacts.read crm.objects.contacts.write',
    state: state,
    optional_scope: 'crm.objects.deals.read'
  });

  return `https://app.hubspot.com/oauth/authorize?${params.toString()}`;
};
Enter fullscreen mode Exit fullscreen mode

Paso 5: Intercambia el Código por un Token de Acceso

Maneja la devolución de llamada de OAuth:

const exchangeCodeForToken = async (code) => {
  const response = await fetch('https://api.hubapi.com/oauth/v1/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: HUBSPOT_CLIENT_ID,
      client_secret: HUBSPOT_CLIENT_SECRET,
      redirect_uri: HUBSPOT_REDIRECT_URI,
      code: code
    })
  });

  const data = await response.json();

  return {
    accessToken: data.access_token,
    refreshToken: data.refresh_token,
    expiresIn: data.expires_in,
    portalId: data.hub_portal_id
  };
};

// Manejar la devolución de llamada
app.get('/oauth/callback', async (req, res) => {
  const { code, state } = req.query;

  try {
    const tokens = await exchangeCodeForToken(code);

    // Almacenar tokens en la base de datos
    await db.installations.create({
      portalId: tokens.portalId,
      accessToken: tokens.accessToken,
      refreshToken: tokens.refreshToken,
      tokenExpiry: Date.now() + (tokens.expiresIn * 1000)
    });

    res.redirect('/success');
  } catch (error) {
    console.error('Error de OAuth:', error);
    res.status(500).send('Autenticación fallida');
  }
});
Enter fullscreen mode Exit fullscreen mode

Paso 6: Refresca el Token de Acceso

Los tokens de acceso expiran después de 6 horas. Implementa refresco automático:

const refreshAccessToken = async (refreshToken) => {
  const response = await fetch('https://api.hubapi.com/oauth/v1/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      client_id: HUBSPOT_CLIENT_ID,
      client_secret: HUBSPOT_CLIENT_SECRET,
      refresh_token: refreshToken
    })
  });

  const data = await response.json();

  return {
    accessToken: data.access_token,
    refreshToken: data.refresh_token, // Siempre guarda el nuevo refresh token
    expiresIn: data.expires_in
  };
};

// Middleware para asegurar un token válido
const ensureValidToken = async (portalId) => {
  const installation = await db.installations.findByPortalId(portalId);

  // Refrescar si expira dentro de 30 minutos
  if (installation.tokenExpiry < Date.now() + 1800000) {
    const newTokens = await refreshAccessToken(installation.refreshToken);

    await db.installations.update(installation.id, {
      accessToken: newTokens.accessToken,
      refreshToken: newTokens.refreshToken,
      tokenExpiry: Date.now() + (newTokens.expiresIn * 1000)
    });

    return newTokens.accessToken;
  }

  return installation.accessToken;
};
Enter fullscreen mode Exit fullscreen mode

Paso 7: Realiza Llamadas a la API Autenticadas

Crea un cliente reutilizable para tus requests:

const HUBSPOT_BASE_URL = 'https://api.hubapi.com';

const hubspotRequest = async (endpoint, options = {}, portalId = null) => {
  const accessToken = portalId ? await ensureValidToken(portalId) : process.env.HUBSPOT_ACCESS_TOKEN;

  const response = await fetch(`${HUBSPOT_BASE_URL}${endpoint}`, {
    ...options,
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
      ...options.headers
    }
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Error de la API de HubSpot: ${error.message}`);
  }

  return response.json();
};

// Uso
const contacts = await hubspotRequest('/crm/v3/objects/contacts');
Enter fullscreen mode Exit fullscreen mode

Trabajando con Objetos CRM

Creando un Contacto

Crea o actualiza un contacto:

const createContact = async (contactData) => {
  const contact = {
    properties: {
      email: contactData.email,
      firstname: contactData.firstName,
      lastname: contactData.lastName,
      phone: contactData.phone,
      company: contactData.company,
      website: contactData.website,
      lifecyclestage: contactData.lifecycleStage || 'lead'
    }
  };

  const response = await hubspotRequest('/crm/v3/objects/contacts', {
    method: 'POST',
    body: JSON.stringify(contact)
  });

  return response;
};

// Uso
const contact = await createContact({
  email: 'john.doe@example.com',
  firstName: 'John',
  lastName: 'Doe',
  phone: '+1-555-0123',
  company: 'Acme Corp',
  lifecycleStage: 'customer'
});

console.log(`Contacto creado: ${contact.id}`);
Enter fullscreen mode Exit fullscreen mode

Propiedades del Contacto

Propiedad Tipo Descripción
email Cadena Email principal (identificador único)
firstname Cadena Nombre
lastname Cadena Apellido
phone Cadena Número de teléfono
company Cadena Nombre de la empresa
website Cadena URL del sitio web
lifecyclestage Enumeración lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer, evangelist, subscriber
createdate Fecha y Hora Generado automáticamente
lastmodifieddate Fecha y Hora Generado automáticamente

Obteniendo un Contacto

Recupera un contacto por ID:

const getContact = async (contactId) => {
  const response = await hubspotRequest(`/crm/v3/objects/contacts/${contactId}`);
  return response;
};

// Uso
const contact = await getContact('12345');
console.log(`${contact.properties.firstname} ${contact.properties.lastname}`);
console.log(`Email: ${contact.properties.email}`);
Enter fullscreen mode Exit fullscreen mode

Buscando Contactos

Filtra contactos por propiedades:

const searchContacts = async (searchCriteria) => {
  const response = await hubspotRequest('/crm/v3/objects/contacts/search', {
    method: 'POST',
    body: JSON.stringify({
      filterGroups: searchCriteria,
      properties: ['firstname', 'lastname', 'email', 'company'],
      limit: 100
    })
  });

  return response;
};

// Uso - Buscar contactos de empresa específica
const results = await searchContacts({
  filterGroups: [
    {
      filters: [
        {
          propertyName: 'company',
          operator: 'EQ',
          value: 'Acme Corp'
        }
      ]
    }
  ]
});

results.results.forEach(contact => {
  console.log(`${contact.properties.email}`);
});
Enter fullscreen mode Exit fullscreen mode

Operadores de Filtro de Búsqueda

Operador Descripción Ejemplo
EQ Igual a company EQ 'Acme'
NEQ No igual a lifecyclestage NEQ 'subscriber'
CONTAINS_TOKEN Contiene email CONTAINS_TOKEN 'gmail'
NOT_CONTAINS_TOKEN No contiene email NOT_CONTAINS_TOKEN 'test'
GT Mayor que createdate GT '2026-01-01'
LT Menor que createdate LT '2026-12-31'
GTE Mayor o igual deal_amount GTE 10000
LTE Menor o igual deal_amount LTE 50000
HAS_PROPERTY Tiene valor phone HAS_PROPERTY
NOT_HAS_PROPERTY Valor faltante phone NOT_HAS_PROPERTY

Creando una Empresa

Crea un registro de empresa:

const createCompany = async (companyData) => {
  const company = {
    properties: {
      name: companyData.name,
      domain: companyData.domain,
      industry: companyData.industry,
      numberofemployees: companyData.employees,
      annualrevenue: companyData.revenue,
      city: companyData.city,
      state: companyData.state,
      country: companyData.country
    }
  };

  const response = await hubspotRequest('/crm/v3/objects/companies', {
    method: 'POST',
    body: JSON.stringify(company)
  });

  return response;
};

// Uso
const company = await createCompany({
  name: 'Acme Corporation',
  domain: 'acme.com',
  industry: 'Technology',
  employees: 500,
  revenue: 50000000,
  city: 'San Francisco',
  state: 'CA',
  country: 'USA'
});
Enter fullscreen mode Exit fullscreen mode

Asociando Objetos

Vincula contactos a empresas:

const associateContactWithCompany = async (contactId, companyId) => {
  const response = await hubspotRequest(
    `/crm/v3/objects/contacts/${contactId}/associations/companies/${companyId}`,
    {
      method: 'PUT',
      body: JSON.stringify({
        types: [
          {
            associationCategory: 'HUBSPOT_DEFINED',
            associationTypeId: 1 // Contacto a Empresa
          }
        ]
      })
    }
  );

  return response;
};

// Uso
await associateContactWithCompany('12345', '67890');
Enter fullscreen mode Exit fullscreen mode

Tipos de Asociación

Asociación ID de Tipo Dirección
Contacto → Empresa 1 El contacto está asociado con la empresa
Empresa → Contacto 1 La empresa tiene un contacto asociado
Negocio → Contacto 3 El negocio está asociado con el contacto
Negocio → Empresa 5 El negocio está asociado con la empresa
Ticket → Contacto 16 El ticket está asociado con el contacto
Ticket → Empresa 15 El ticket está asociado con la empresa

Creando un Negocio

Crea una oportunidad de venta (deal):

const createDeal = async (dealData) => {
  const deal = {
    properties: {
      dealname: dealData.name,
      amount: dealData.amount.toString(),
      dealstage: dealData.stage || 'appointmentscheduled',
      pipeline: dealData.pipelineId || 'default',
      closedate: dealData.closeDate,
      dealtype: dealData.type || 'newbusiness',
      description: dealData.description
    }
  };

  const response = await hubspotRequest('/crm/v3/objects/deals', {
    method: 'POST',
    body: JSON.stringify(deal)
  });

  return response;
};

// Uso
const deal = await createDeal({
  name: 'Acme Corp - Licencia Empresarial',
  amount: 50000,
  stage: 'qualification',
  closeDate: '2026-06-30',
  type: 'newbusiness',
  description: 'Suscripción anual empresarial'
});

// Asociar con empresa y contacto
await hubspotRequest(
  `/crm/v3/objects/deals/${deal.id}/associations/companies/${companyId}`,
  { method: 'PUT', body: JSON.stringify({ types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 5 }] }) }
);

await hubspotRequest(
  `/crm/v3/objects/deals/${deal.id}/associations/contacts/${contactId}`,
  { method: 'PUT', body: JSON.stringify({ types: [{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 3 }] }) }
);
Enter fullscreen mode Exit fullscreen mode

Etapas de Negocio (Embudo por Defecto)

Etapa Valor Interno
Citas Programadas appointmentscheduled
Calificado para Comprar qualifiedtobuy
Presentación Programada presentationscheduled
Responsable de la Toma de Decisiones Comprometido decisionmakerboughtin
Contrato Enviado contractsent
Cerrado Ganado closedwon
Cerrado Perdido closedlost

Webhooks

Configurando Webhooks

Configura webhooks para recibir notificaciones en tiempo real:

const createWebhook = async (webhookData) => {
  const response = await hubspotRequest('/webhooks/v3/my-app/webhooks', {
    method: 'POST',
    body: JSON.stringify({
      webhookUrl: webhookData.url,
      eventTypes: webhookData.events,
      objectType: webhookData.objectType,
      propertyName: webhookData.propertyName // Opcional
    })
  });

  return response;
};

// Uso
const webhook = await createWebhook({
  url: 'https://myapp.com/webhooks/hubspot',
  events: [
    'contact.creation',
    'contact.propertyChange',
    'company.creation',
    'deal.creation',
    'deal.stageChange'
  ],
  objectType: 'contact'
});

console.log(`Webhook creado: ${webhook.id}`);
Enter fullscreen mode Exit fullscreen mode

Tipos de Eventos de Webhook

Tipo de Evento Disparador
contact.creation Nuevo contacto creado
contact.propertyChange Propiedad de contacto actualizada
contact.deletion Contacto eliminado
company.creation Nueva empresa creada
company.propertyChange Propiedad de empresa actualizada
deal.creation Nuevo negocio creado
deal.stageChange Etapa de negocio cambiada
deal.propertyChange Propiedad de negocio actualizada
ticket.creation Nuevo ticket creado
ticket.propertyChange Propiedad de ticket actualizada

Manejo de Webhooks

Implementa la verificación de firma y manejo básico de eventos:

const express = require('express');
const crypto = require('crypto');
const app = express();

app.post('/webhooks/hubspot', express.json(), async (req, res) => {
  const signature = req.headers['x-hubspot-signature'];
  const payload = JSON.stringify(req.body);

  // Verificar firma del webhook
  const isValid = verifyWebhookSignature(payload, signature, process.env.HUBSPOT_CLIENT_SECRET);

  if (!isValid) {
    console.error('Firma de webhook inválida');
    return res.status(401).send('No autorizado');
  }

  const events = req.body;

  for (const event of events) {
    console.log(`Evento: ${event.eventType}`);
    console.log(`Objeto: ${event.objectType} - ${event.objectId}`);
    console.log(`Propiedad: ${event.propertyName}`);
    console.log(`Valor: ${event.propertyValue}`);

    // Dirigir al manejador apropiado
    switch (event.eventType) {
      case 'contact.creation':
        await handleContactCreation(event);
        break;
      case 'contact.propertyChange':
        await handleContactUpdate(event);
        break;
      case 'deal.stageChange':
        await handleDealStageChange(event);
        break;
    }
  }

  res.status(200).send('OK');
});

function verifyWebhookSignature(payload, signature, clientSecret) {
  const expectedSignature = crypto
    .createHmac('sha256', clientSecret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}
Enter fullscreen mode Exit fullscreen mode

Limitación de Tasa

Entendiendo los Límites de Tasa

HubSpot aplica límites de tasa según nivel de suscripción:

Nivel Solicitudes/Segundo Solicitudes/Día
Gratis/Starter 100 100.000
Profesional 200 500.000
Empresarial 400 1.000.000

Si excedes los límites, recibirás HTTP 429 (Demasiadas Solicitudes).

Encabezados de Límite de Tasa

Encabezado Descripción
X-HubSpot-RateLimit-Second-Limit Máximo de solicitudes por segundo
X-HubSpot-RateLimit-Second-Remaining Solicitudes restantes este segundo
X-HubSpot-RateLimit-Second-Reset Segundos hasta que se restablezca el límite de segundo
X-HubSpot-RateLimit-Daily-Limit Máximo de solicitudes por día
X-HubSpot-RateLimit-Daily-Remaining Solicitudes restantes hoy
X-HubSpot-RateLimit-Daily-Reset Segundos hasta que se restablezca el límite diario

Implementando el Manejo de Límites de Tasa

Implementa gestión y reintentos:

const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await hubspotRequest(endpoint, options);

      // Registrar información de límite de tasa
      const remaining = response.headers.get('X-HubSpot-RateLimit-Second-Remaining');
      if (remaining < 10) {
        console.warn(`Límite de tasa bajo restante: ${remaining}`);
      }

      return response;
    } catch (error) {
      if (error.message.includes('429') && attempt < maxRetries) {
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Límite de tasa alcanzado. Reintentando en ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
};

// Clase de limitador de tasa
class HubSpotRateLimiter {
  constructor(requestsPerSecond = 90) { // Mantenerse por debajo del límite
    this.queue = [];
    this.interval = 1000 / requestsPerSecond;
    this.processing = false;
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.process();
    });
  }

  async process() {
    if (this.processing || this.queue.length === 0) return;
    this.processing = true;

    while (this.queue.length > 0) {
      const { requestFn, resolve, reject } = this.queue.shift();
      try {
        const result = await requestFn();
        resolve(result);
      } catch (error) {
        reject(error);
      }
      if (this.queue.length > 0) {
        await new Promise(r => setTimeout(r, this.interval));
      }
    }
    this.processing = false;
  }
}
Enter fullscreen mode Exit fullscreen mode

Lista de Verificación para Despliegue en Producción

Antes de lanzar tu integración:

  • [ ] Usa autenticación de aplicación privada o OAuth 2.0
  • [ ] Almacena los tokens de forma segura (base de datos cifrada)
  • [ ] Implementa la actualización automática de tokens
  • [ ] Configura limitación de tasa y encolamiento de solicitudes
  • [ ] Usa HTTPS en tu endpoint de webhook
  • [ ] Maneja errores de forma exhaustiva
  • [ ] Registra todas las llamadas a la API
  • [ ] Monitorea el uso del límite de tasa
  • [ ] Documenta procedimientos para problemas comunes

Casos de Uso en el Mundo Real

Sincronización de CRM

Escenario: Una empresa SaaS sincroniza los datos de clientes

  • Desafío: Entrada manual de datos entre la app y HubSpot
  • Solución: Sincronización en tiempo real usando webhooks y API
  • Resultado: 0 entrada manual, 100% precisión de datos

Enrutamiento de Leads

Escenario: Una agencia de marketing automatiza la distribución de leads

  • Desafío: Tiempos de respuesta lentos a los leads
  • Solución: Enrutamiento a representantes de ventas activado por webhook
  • Resultado: Tiempo de respuesta de 5 min, +40% conversión

Conclusión

La API de HubSpot proporciona capacidades integrales de CRM y automatización de marketing. Resumen de prácticas clave:

  • Usa OAuth 2.0 para aplicaciones multi-tenant, aplicaciones privadas para integraciones internas
  • Los límites de tasa varían según el nivel (100-400 solicitudes/segundo)
  • Los webhooks permiten sincronización en tiempo real
  • Los objetos de CRM admiten asociaciones y propiedades personalizadas
  • Apidog agiliza las pruebas de API y la colaboración en equipo

Sección de Preguntas Frecuentes

¿Cómo me autentico con la API de HubSpot?

Utiliza OAuth 2.0 para aplicaciones multi-tenant o aplicaciones privadas para integraciones de portal único. La autenticación con clave API está obsoleta.

¿Cuáles son los límites de tasa de HubSpot?

Los límites de tasa varían de 100 solicitudes/segundo (Gratis) a 400 solicitudes/segundo (Empresarial), con límites diarios de 100K a 1M de solicitudes.

¿Cómo creo un contacto en HubSpot?

Realiza una solicitud POST a /crm/v3/objects/contacts con propiedades que incluyan email, firstname, lastname y cualquier campo personalizado.

¿Puedo crear propiedades personalizadas?

Sí, utiliza la API de Propiedades para crear campos personalizados para cualquier tipo de objeto.

¿Cómo funcionan los webhooks en HubSpot?

Configura los webhooks en la configuración de tu aplicación. HubSpot envía solicitudes POST a tu endpoint cuando ocurren eventos específicos.

Top comments (0)