Resumo
A API do HubSpot permite que desenvolvedores se integrem programaticamente com hubs de CRM, marketing, vendas e serviços. Ela usa autenticação OAuth 2.0 e de aplicativos privados, endpoints RESTful para contatos, empresas, negócios (deals), tickets e muito mais, com limites de taxa baseados no nível de assinatura. Este guia aborda a configuração de autenticação, endpoints principais, webhooks e estratégias de integração em produção.
Introdução
O HubSpot gerencia mais de 194.000 contas de clientes e bilhões de registros de CRM. Para desenvolvedores que constroem integrações de CRM, automação de marketing ou ferramentas de vendas, a integração com a API do HubSpot não é opcional — é essencial para alcançar mais de 7 milhões de usuários.
Aqui está a realidade: empresas perdem de 15 a 20 horas semanais com entrada manual de dados entre sistemas. Uma integração sólida com a API do HubSpot automatiza a sincronização de contatos, atualizações de negócios, fluxos de trabalho de marketing e relatórios entre plataformas.
O Que É a API do HubSpot?
O HubSpot fornece uma API RESTful para acessar dados de CRM e recursos de automação de marketing. A API lida com:
- Contatos, empresas, negócios, tickets e objetos personalizados
- E-mails de marketing e landing pages
- Pipelines de vendas e sequências
- Tickets de serviço e conversas
- Análises e relatórios
- Fluxos de trabalho e automação
- Arquivos e ativos
Principais Recursos
| Recurso | Descrição |
|---|---|
| Design RESTful | Métodos HTTP padrão com respostas JSON |
| OAuth 2.0 + Aplicativos Privados | Opções de autenticação flexíveis |
| Webhooks | Notificações em tempo real para alterações de objetos |
| Limitação de Taxas | Limites baseados em níveis (100-400 requisições/segundo) |
| Objetos CRM | Suporte para objetos padrão e personalizados |
| Associações | Conecta objetos (contato-empresa, negócio-contato) |
| Propriedades | Campos personalizados para qualquer tipo de objeto |
| API de Pesquisa | Filtragem e ordenação complexas |
Visão Geral da Arquitetura da API
O HubSpot usa APIs REST versionadas:
https://api.hubapi.com/
Versões da API Comparadas
| Versão | Status | Autenticação | Caso de Uso |
|---|---|---|---|
| CRM API v3 | Atual | OAuth 2.0, Aplicativo Privado | Todas as novas integrações |
| Automation API v4 | Atual | OAuth 2.0, Aplicativo Privado | Inscrição em fluxos de trabalho |
| Marketing Email API | Atual | OAuth 2.0, Aplicativo Privado | Campanhas de e-mail |
| Contacts API v1 | Obsoleta | Chave de API (legado) | Migrar para v3 |
| Companies API v1 | Obsoleta | Chave de API (legado) | Migrar para v3 |
Importante: O HubSpot descontinuou a autenticação por chave de API em favor do OAuth 2.0 e aplicativos privados. Migre todas as integrações imediatamente.
Primeiros Passos: Configuração da Autenticação
Passo 1: Crie Sua Conta de Desenvolvedor HubSpot
Antes de acessar a API:
- Visite o Portal do Desenvolvedor HubSpot
- Faça login com sua conta HubSpot (ou crie uma)
- Navegue até Aplicativos no painel do desenvolvedor
- Clique em Criar aplicativo
Passo 2: Escolha o Método de Autenticação
O HubSpot suporta dois métodos de autenticação:
| Método | Melhor Para | Nível de Segurança |
|---|---|---|
| OAuth 2.0 | Aplicativos multi-tenant, integrações públicas | Alto (tokens com escopo de usuário) |
| Aplicativo Privado | Integrações internas, portal único | Alto (token com escopo de portal) |
Passo 3: Configurar Aplicativo Privado (Recomendado para Integrações Internas)
Crie um aplicativo privado para acesso de portal único:
- Vá para Configurações > Integrações > Aplicativos Privados
- Clique em Criar um aplicativo privado
- Configure os escopos:
contacts
crm.objects.companies
crm.objects.deals
crm.objects.tickets
automation
webhooks
- Gere o token de acesso
- Copie e armazene com segurança
# .env file
HUBSPOT_ACCESS_TOKEN="pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
HUBSPOT_PORTAL_ID="12345678"
Passo 4: Configurar OAuth 2.0 (Para Aplicativos Multi-Tenant)
Configure OAuth para acesso multi-portal:
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;
// Build authorization URL
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()}`;
};
Passo 5: Trocar Código por Token de Acesso
Lidar com o callback 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
};
};
// Handle callback
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
// Store tokens in database
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('OAuth error:', error);
res.status(500).send('Authentication failed');
}
});
Passo 6: Atualizar Token de Acesso
Tokens de acesso expiram após 6 horas:
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,
redirect_uri: HUBSPOT_REDIRECT_URI,
refresh_token: refreshToken
})
});
const data = await response.json();
return {
accessToken: data.access_token,
refreshToken: data.refresh_token, // Always save new refresh token
expiresIn: data.expires_in
};
};
// Middleware to ensure valid token
const ensureValidToken = async (portalId) => {
const installation = await db.installations.findByPortalId(portalId);
// Refresh if expires within 30 minutes
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;
};
Passo 7: Fazer Chamadas de API Autenticadas
Crie um cliente de API reutilizável:
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(`HubSpot API Error: ${error.message}`);
}
return response.json();
};
// Usage
const contacts = await hubspotRequest('/crm/v3/objects/contacts');
Trabalhando com Objetos CRM
Criando um Contato
Crie ou atualize um contato:
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;
};
// Usage
const contact = await createContact({
email: 'john.doe@example.com',
firstName: 'John',
lastName: 'Doe',
phone: '+1-555-0123',
company: 'Acme Corp',
lifecycleStage: 'customer'
});
console.log(`Contact created: ${contact.id}`);
Propriedades do Contato
| Propriedade | Tipo | Descrição |
|---|---|---|
email |
String | E-mail principal (identificador único) |
firstname |
String | Primeiro nome |
lastname |
String | Sobrenome |
phone |
String | Número de telefone |
company |
String | Nome da empresa |
website |
String | URL do site |
lifecyclestage |
Enum | lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer, evangelist, subscriber |
createdate |
DateTime | Gerado automaticamente |
lastmodifieddate |
DateTime | Gerado automaticamente |
Obtendo um Contato
Busque um contato por ID:
const getContact = async (contactId) => {
const response = await hubspotRequest(`/crm/v3/objects/contacts/${contactId}`);
return response;
};
// Usage
const contact = await getContact('12345');
console.log(`${contact.properties.firstname} ${contact.properties.lastname}`);
console.log(`Email: ${contact.properties.email}`);
Pesquisando Contatos
Pesquise com filtros:
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;
};
// Usage - Find contacts at specific company
const results = await searchContacts({
filterGroups: [
{
filters: [
{
propertyName: 'company',
operator: 'EQ',
value: 'Acme Corp'
}
]
}
]
});
results.results.forEach(contact => {
console.log(`${contact.properties.email}`);
});
Operadores de Filtro de Pesquisa
| Operador | Descrição | Exemplo |
|---|---|---|
EQ |
Igual a | company EQ 'Acme' |
NEQ |
Diferente de | lifecyclestage NEQ 'subscriber' |
CONTAINS_TOKEN |
Contém | email CONTAINS_TOKEN 'gmail' |
NOT_CONTAINS_TOKEN |
Não contém | email NOT_CONTAINS_TOKEN 'test' |
GT |
Maior que | createdate GT '2026-01-01' |
LT |
Menor que | createdate LT '2026-12-31' |
GTE |
Maior ou igual a | deal_amount GTE 10000 |
LTE |
Menor ou igual a | deal_amount LTE 50000 |
HAS_PROPERTY |
Possui valor | phone HAS_PROPERTY |
NOT_HAS_PROPERTY |
Valor ausente | phone NOT_HAS_PROPERTY |
Criando uma Empresa
Crie um 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;
};
// Usage
const company = await createCompany({
name: 'Acme Corporation',
domain: 'acme.com',
industry: 'Technology',
employees: 500,
revenue: 50000000,
city: 'San Francisco',
state: 'CA',
country: 'USA'
});
Associando Objetos
Vincule contatos 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 // Contact to Company
}
]
})
}
);
return response;
};
// Usage
await associateContactWithCompany('12345', '67890');
Tipos de Associação
| Associação | ID do Tipo | Direção |
|---|---|---|
| Contato → Empresa | 1 | Contato está associado à Empresa |
| Empresa → Contato | 1 | Empresa tem Contato associado |
| Negócio → Contato | 3 | Negócio está associado ao Contato |
| Negócio → Empresa | 5 | Negócio está associado à Empresa |
| Ticket → Contato | 16 | Ticket está associado ao Contato |
| Ticket → Empresa | 15 | Ticket está associado à Empresa |
Criando um Negócio
Crie uma oportunidade de vendas:
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;
};
// Usage
const deal = await createDeal({
name: 'Acme Corp - Enterprise License',
amount: 50000,
stage: 'qualification',
closeDate: '2026-06-30',
type: 'newbusiness',
description: 'Enterprise annual subscription'
});
// Associate with company and contact
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 }] }) }
);
Estágios do Negócio (Pipeline Padrão)
| Estágio | Valor Interno |
|---|---|
| Compromissos Agendados | appointmentscheduled |
| Qualificado para Comprar | qualifiedtobuy |
| Apresentação Agendada | presentationscheduled |
| Tomador de Decisão Comprometido | decisionmakerboughtin |
| Contrato Enviado | contractsent |
| Fechado e Ganho | closedwon |
| Fechado e Perdido | closedlost |
Webhooks
Configurando Webhooks
Configure webhooks para notificações em tempo 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 // Optional: filter by property change
})
});
return response;
};
// Usage
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 created: ${webhook.id}`);
Tipos de Eventos de Webhook
| Tipo de Evento | Gatilho |
|---|---|
contact.creation |
Novo contato criado |
contact.propertyChange |
Propriedade de contato atualizada |
contact.deletion |
Contato excluído |
company.creation |
Nova empresa criada |
company.propertyChange |
Propriedade da empresa atualizada |
deal.creation |
Novo negócio criado |
deal.stageChange |
Estágio do negócio alterado |
deal.propertyChange |
Propriedade do negócio atualizada |
ticket.creation |
Novo ticket criado |
ticket.propertyChange |
Propriedade do ticket atualizada |
Manipulando Webhooks
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);
// Verify webhook signature
const isValid = verifyWebhookSignature(payload, signature, process.env.HUBSPOT_CLIENT_SECRET);
if (!isValid) {
console.error('Invalid webhook signature');
return res.status(401).send('Unauthorized');
}
const events = req.body;
for (const event of events) {
console.log(`Event: ${event.eventType}`);
console.log(`Object: ${event.objectType} - ${event.objectId}`);
console.log(`Property: ${event.propertyName}`);
console.log(`Value: ${event.propertyValue}`);
// Route to appropriate handler
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')
);
}
Limitação de Taxas
Entendendo os Limites de Taxa
O HubSpot aplica limites de taxa com base no nível de assinatura:
| Nível | Requisições/Segundo | Requisições/Dia |
|---|---|---|
| Grátis/Inicial | 100 | 100.000 |
| Profissional | 200 | 500.000 |
| Empresarial | 400 | 1.000.000 |
Exceder os limites resulta em respostas HTTP 429 (Muitas Requisições).
Cabeçalhos de Limite de Taxa
| Cabeçalho | Descrição |
|---|---|
X-HubSpot-RateLimit-Second-Limit |
Máx. requisições por segundo |
X-HubSpot-RateLimit-Second-Remaining |
Requisições restantes neste segundo |
X-HubSpot-RateLimit-Second-Reset |
Segundos até o reset do limite por segundo |
X-HubSpot-RateLimit-Daily-Limit |
Máx. requisições por dia |
X-HubSpot-RateLimit-Daily-Remaining |
Requisições restantes hoje |
X-HubSpot-RateLimit-Daily-Reset |
Segundos até o reset do limite diário |
Implementando o Tratamento de Limite de Taxas
const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await hubspotRequest(endpoint, options);
// Log rate limit info
const remaining = response.headers.get('X-HubSpot-RateLimit-Second-Remaining');
if (remaining < 10) {
console.warn(`Low rate limit remaining: ${remaining}`);
}
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
// Rate limiter class
class HubSpotRateLimiter {
constructor(requestsPerSecond = 90) { // Stay under limit
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;
}
}
Lista de Verificação de Implantação em Produção
Antes de entrar em produção:
- [ ] Use autenticação de aplicativo privado ou OAuth 2.0
- [ ] Armazene tokens com segurança (banco de dados criptografado)
- [ ] Implemente a atualização automática de tokens
- [ ] Configure limitação de taxa e enfileiramento de requisições
- [ ] Configure o endpoint do webhook com HTTPS
- [ ] Implemente tratamento abrangente de erros
- [ ] Adicione logs para todas as chamadas de API
- [ ] Monitore o uso do limite de taxa
- [ ] Crie um runbook para problemas comuns
Casos de Uso no Mundo Real
Sincronização de CRM
Uma empresa SaaS sincroniza dados de clientes:
- Desafio: Entrada manual de dados entre o aplicativo e o HubSpot
- Solução: Sincronização em tempo real via webhooks e API
- Resultado: Zero entrada manual, 100% de precisão dos dados
Encaminhamento de Leads
Uma agência de marketing automatiza a distribuição de leads:
- Desafio: Tempos de resposta lentos para leads
- Solução: Encaminhamento para representantes de vendas acionado por webhook
- Resultado: Tempo de resposta de 5 minutos, aumento de 40% na conversão
Conclusão
A API do HubSpot oferece recursos abrangentes de CRM e automação de marketing. Principais pontos:
- Use OAuth 2.0 para aplicativos multi-tenant, aplicativos privados para integrações internas
- Os limites de taxa variam por nível (100-400 requisições/segundo)
- Webhooks permitem sincronização em tempo real
- Objetos CRM suportam associações e propriedades personalizadas
- Apidog otimiza o teste de API e a colaboração em equipe
Seção de Perguntas Frequentes
Como faço para autenticar com a API do HubSpot?
Use OAuth 2.0 para aplicativos multi-tenant ou aplicativos privados para integrações de portal único. A autenticação por chave de API foi descontinuada.
Quais são os limites de taxa do HubSpot?
Os limites de taxa variam de 100 requisições/segundo (Grátis) a 400 requisições/segundo (Empresarial), com limites diários de 100K a 1M de requisições.
Como faço para criar um contato no HubSpot?
Faça um POST para /crm/v3/objects/contacts com propriedades incluindo e-mail, primeiro nome, sobrenome e quaisquer campos personalizados.
Posso criar propriedades personalizadas?
Sim, use a API de Propriedades para criar campos personalizados para qualquer tipo de objeto.
Como funcionam os webhooks no HubSpot?
Configure webhooks nas configurações do seu aplicativo. O HubSpot envia requisições POST para o seu endpoint quando eventos específicos ocorrem.
