Resumo
API do HubSpot permite integrações diretas com hubs de CRM, marketing, vendas e serviços via OAuth 2.0 ou aplicativos privados, com endpoints RESTful para contatos, empresas, negócios (deals), tickets e muito mais. Os limites de taxa variam conforme a assinatura. Veja como configurar autenticação, acessar endpoints essenciais, trabalhar com webhooks e preparar sua integração para produção.
Experimente o Apidog hoje mesmo
Introdução
O HubSpot gerencia mais de 194.000 contas de clientes e bilhões de registros de CRM. Se você desenvolve integrações para CRM, automação de marketing ou vendas, conectar-se à API do HubSpot é essencial para atingir milhões de usuários.
Automatize tarefas repetitivas: empresas perdem de 15 a 20 horas por semana com digitação manual entre sistemas. Uma integração robusta com a API do HubSpot automatiza sincronização de contatos, atualizações de negócios, workflows de marketing e relatórios, eliminando trabalho manual.
💡 Dica: O Apidog facilita o teste de integrações com a API: valide endpoints do HubSpot, fluxos OAuth, webhooks e problemas de autenticação em um só lugar. Importe specs, simule respostas e compartilhe cenários de teste.
O Que É a API do HubSpot?
A API do HubSpot é RESTful e oferece acesso a dados de CRM e automação de marketing:
- Contatos, empresas, negócios, tickets, objetos customizados
- E-mails de marketing, landing pages
- Pipelines de vendas, sequências
- Tickets de suporte, conversas
- Relatórios, analytics
- Automação e workflows
- Gestão de 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
A base é:
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: Chaves de API foram descontinuadas. Migre para OAuth 2.0 ou aplicativos privados.
Primeiros Passos: Configuração da Autenticação
Passo 1: Crie Sua Conta de Desenvolvedor HubSpot
- Acesse o Portal do Desenvolvedor HubSpot
- Faça login ou crie uma conta HubSpot
- Vá até Aplicativos no painel do desenvolvedor
- Clique em Criar aplicativo
Passo 2: Escolha o Método 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)
- Acesse Configurações > Integrações > Aplicativos Privados
- Clique em Criar um aplicativo privado
- Defina os escopos necessários:
contacts
crm.objects.companies
crm.objects.deals
crm.objects.tickets
automation
webhooks
- Gere o token de acesso
- Salve em um local seguro
# .env
HUBSPOT_ACCESS_TOKEN="pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
HUBSPOT_PORTAL_ID="12345678"
Passo 4: Configurar OAuth 2.0 (Para Multi-Tenant)
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;
// URL de autorização OAuth
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
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
};
};
// Callback OAuth
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
// Armazene tokens no BD
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
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,
expiresIn: data.expires_in
};
};
// Middleware para garantir token válido
const ensureValidToken = async (portalId) => {
const installation = await db.installations.findByPortalId(portalId);
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
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();
};
// Exemplo de uso
const contacts = await hubspotRequest('/crm/v3/objects/contacts');
Trabalhando com Objetos CRM
Criando 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;
};
// 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(`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
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}`);
Pesquisando Contatos
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;
};
// Exemplo: buscar contatos de uma empresa
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
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'
});
Associando Objetos
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;
};
// Uso
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
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 - Enterprise License',
amount: 50000,
stage: 'qualification',
closeDate: '2026-06-30',
type: 'newbusiness',
description: 'Enterprise annual subscription'
});
// Associe com empresa e contato
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
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: filtrar por propriedade
})
});
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 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);
// Verifica assinatura do webhook
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}`);
// Redirecione para handler apropriado
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
| Nível | Requisições/Segundo | Requisições/Dia |
|---|---|---|
| Grátis/Inicial | 100 | 100.000 |
| Profissional | 200 | 500.000 |
| Empresarial | 400 | 1.000.000 |
Se exceder, sua API retorna 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 de rate limit
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 HubSpotRateLimiter {
constructor(requestsPerSecond = 90) {
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 ir para produção, garanta:
- [ ] Use OAuth 2.0 ou aplicativo privado
- [ ] Armazene tokens em BD criptografado
- [ ] Implemente atualização automática de tokens
- [ ] Limitação de taxa e enfileiramento de requisições
- [ ] Webhook endpoint com HTTPS
- [ ] Tratamento abrangente de erros
- [ ] Log para todas as chamadas de API
- [ ] Monitoramento dos limites de taxa
- [ ] Runbook para problemas comuns
Casos de Uso no Mundo Real
Sincronização de CRM
Cenário: SaaS sincroniza dados de clientes
- Desafio: Entrada manual entre app e HubSpot
- Solução: Sincronização em tempo real via webhooks e API
- Resultado: Zero digitação manual, dados 100% precisos
Encaminhamento de Leads
Cenário: Agência automatiza distribuição de leads
- Desafio: Tempo de resposta alto para leads
- Solução: Encaminhamento por webhook para representantes
- Resultado: Resposta em 5 minutos, +40% conversão
Conclusão
A API do HubSpot entrega recursos completos de CRM e automação de marketing. Para uma integração sólida:
- Use OAuth 2.0 para multi-tenant, aplicativo privado para integrações internas
- Fique atento aos limites de taxa (100–400 req/s)
- Webhooks para sincronização em tempo real
- CRM com associações e propriedades customizadas
- O Apidog potencializa seu fluxo de teste e colaboração em APIs
Seção de Perguntas Frequentes
Como faço para autenticar com a API do HubSpot?
Use OAuth 2.0 para multi-tenant ou aplicativo privado para integrações single-portal. Chaves de API não são mais suportadas.
Quais são os limites de taxa do HubSpot?
De 100 req/s (Grátis) a 400 req/s (Empresarial), e de 100K a 1M de requisições diárias.
Como faço para criar um contato no HubSpot?
Envie um POST para /crm/v3/objects/contacts com email, nome, sobrenome e campos customizados.
Posso criar propriedades personalizadas?
Sim, utilize a API de Propriedades para adicionar campos customizados a qualquer objeto.
Como funcionam os webhooks no HubSpot?
Configure webhooks nas configurações do app. O HubSpot envia POSTs ao seu endpoint para eventos específicos.
Top comments (0)