En bref
L'API HubSpot permet aux développeurs de s'intégrer de manière programmatique aux hubs CRM, marketing, ventes et services. Elle utilise l'authentification OAuth 2.0 et des applications privées, des points de terminaison RESTful pour les contacts, les entreprises, les transactions, les tickets, et plus encore, avec des limites de débit basées sur le niveau d'abonnement. Ce guide couvre la configuration de l'authentification, les points de terminaison principaux, les webhooks et les stratégies d'intégration en production.
Introduction
HubSpot gère plus de 194 000 comptes clients et des milliards d'enregistrements CRM. Pour les développeurs qui créent des intégrations CRM, de l'automatisation marketing ou des outils de vente, l'intégration de l'API HubSpot n'est pas une option, elle est essentielle pour atteindre plus de 7 millions d'utilisateurs.
Voici la réalité : les entreprises perdent 15 à 20 heures par semaine en saisie manuelle de données entre les systèmes. Une intégration solide de l'API HubSpot automatise la synchronisation des contacts, les mises à jour des transactions, les flux de travail marketing et le reporting sur toutes les plateformes.
Qu'est-ce que l'API HubSpot ?
HubSpot fournit une API RESTful pour accéder aux données CRM et aux fonctionnalités d'automatisation marketing. L'API gère :
- Les contacts, les entreprises, les transactions, les tickets et les objets personnalisés
- Les e-mails marketing et les pages de destination
- Les pipelines de vente et les séquences
- Les tickets de service et les conversations
- Les analyses et les rapports
- Les flux de travail et l'automatisation
- Les fichiers et les actifs
Fonctionnalités clés
| Fonctionnalité | Description |
|---|---|
| Conception RESTful | Méthodes HTTP standard avec réponses JSON |
| OAuth 2.0 + Applications privées | Options d'authentification flexibles |
| Webhooks | Notifications en temps réel des changements d'objets |
| Limitation de débit | Limites basées sur le niveau (100-400 requêtes/seconde) |
| Objets CRM | Prise en charge des objets standard et personnalisés |
| Associations | Lier des objets (contact-entreprise, transaction-contact) |
| Propriétés | Champs personnalisés pour tout type d'objet |
| API de recherche | Filtrage et tri complexes |
Vue d'ensemble de l'architecture de l'API
HubSpot utilise des API REST versionnées :
https://api.hubapi.com/
Comparaison des versions de l'API
| Version | Statut | Authentification | Cas d'utilisation |
|---|---|---|---|
| API CRM v3 | Actuelle | OAuth 2.0, Application privée | Toutes les nouvelles intégrations |
| API d'automatisation v4 | Actuelle | OAuth 2.0, Application privée | Inscription au flux de travail |
| API E-mail marketing | Actuelle | OAuth 2.0, Application privée | Campagnes d'e-mail |
| API Contacts v1 | Obsolète | Clé API (héritée) | Migrer vers v3 |
| API Entreprises v1 | Obsolète | Clé API (héritée) | Migrer vers v3 |
Important : HubSpot a déprécié l'authentification par clé API au profit d'OAuth 2.0 et des applications privées. Migrez toutes les intégrations immédiatement.
Premiers pas : Configuration de l'authentification
Étape 1 : Créez votre compte développeur HubSpot
Avant d'accéder à l'API :
- Visitez le Portail des développeurs HubSpot
- Connectez-vous avec votre compte HubSpot (ou créez-en un)
- Naviguez vers Applications dans le tableau de bord des développeurs
- Cliquez sur Créer une application
Étape 2 : Choisissez la méthode d'authentification
HubSpot prend en charge deux méthodes d'authentification :
| Méthode | Idéale pour | Niveau de sécurité |
|---|---|---|
| OAuth 2.0 | Applications multi-locataires, intégrations publiques | Élevé (jetons étendus à l'utilisateur) |
| Application privée | Intégrations internes, portail unique | Élevé (jeton étendu au portail) |
Étape 3 : Configurer une application privée (recommandé pour les intégrations internes)
Créez une application privée pour un accès à un portail unique :
- Allez dans Paramètres > Intégrations > Applications privées
- Cliquez sur Créer une application privée
- Configurez les étendues :
contacts
crm.objects.companies
crm.objects.deals
crm.objects.tickets
automation
webhooks
- Générez un jeton d'accès
- Copiez et stockez en toute sécurité
# fichier .env
HUBSPOT_ACCESS_TOKEN="pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
HUBSPOT_PORTAL_ID="12345678"
Étape 4 : Configurer OAuth 2.0 (pour les applications multi-locataires)
Configurez OAuth pour un accès multi-portails :
- Allez dans Applications > Créer une application
- Configurez les paramètres d'authentification :
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;
// Construire l'URL d'autorisation
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()}`;
};
Étape 5 : Échangez le code contre un jeton d'accès
Gérez le rappel 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
};
};
// Gérer le rappel
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
try {
const tokens = await exchangeCodeForToken(code);
// Stocker les jetons dans la base de données
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('Erreur OAuth :', error);
res.status(500).send('Échec de l\'authentification');
}
});
Étape 6 : Actualiser le jeton d'accès
Les jetons d'accès expirent après 6 heures :
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, // Toujours enregistrer le nouveau jeton d'actualisation
expiresIn: data.expires_in
};
};
// Middleware pour assurer un jeton valide
const ensureValidToken = async (portalId) => {
const installation = await db.installations.findByPortalId(portalId);
// Actualiser s'il expire dans les 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;
};
Étape 7 : Effectuer des appels API authentifiés
Créez un client API réutilisable :
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(`Erreur API HubSpot : ${error.message}`);
}
return response.json();
};
// Utilisation
const contacts = await hubspotRequest('/crm/v3/objects/contacts');
Travailler avec les objets CRM
Création d'un contact
Créer ou mettre à jour un contact :
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;
};
// Utilisation
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 créé : ${contact.id}`);
Propriétés des contacts
| Propriété | Type | Description |
|---|---|---|
email |
Chaîne de caractères | E-mail principal (identifiant unique) |
firstname |
Chaîne de caractères | Prénom |
lastname |
Chaîne de caractères | Nom de famille |
phone |
Chaîne de caractères | Numéro de téléphone |
company |
Chaîne de caractères | Nom de l'entreprise |
website |
Chaîne de caractères | URL du site web |
lifecyclestage |
Énumération | lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer, evangelist, subscriber |
createdate |
DateTime | Généré automatiquement |
lastmodifieddate |
DateTime | Généré automatiquement |
Obtenir un contact
Récupérer un contact par ID :
const getContact = async (contactId) => {
const response = await hubspotRequest(`/crm/v3/objects/contacts/${contactId}`);
return response;
};
// Utilisation
const contact = await getContact('12345');
console.log(`${contact.properties.firstname} ${contact.properties.lastname}`);
console.log(`E-mail : ${contact.properties.email}`);
Recherche de contacts
Recherche avec filtres :
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;
};
// Utilisation - Trouver des contacts dans une entreprise spécifique
const results = await searchContacts({
filterGroups: [
{
filters: [
{
propertyName: 'company',
operator: 'EQ',
value: 'Acme Corp'
}
]
}
]
});
results.results.forEach(contact => {
console.log(`${contact.properties.email}`);
});
Opérateurs de filtre de recherche
| Opérateur | Description | Exemple |
|---|---|---|
EQ |
Égal à | company EQ 'Acme' |
NEQ |
Non égal à | lifecyclestage NEQ 'subscriber' |
CONTAINS_TOKEN |
Contient | email CONTAINS_TOKEN 'gmail' |
NOT_CONTAINS_TOKEN |
Ne contient pas | email NOT_CONTAINS_TOKEN 'test' |
GT |
Supérieur à | createdate GT '2026-01-01' |
LT |
Inférieur à | createdate LT '2026-12-31' |
GTE |
Supérieur ou égal | deal_amount GTE 10000 |
LTE |
Inférieur ou égal | deal_amount LTE 50000 |
HAS_PROPERTY |
A une valeur | phone HAS_PROPERTY |
NOT_HAS_PROPERTY |
Valeur manquante | phone NOT_HAS_PROPERTY |
Création d'une entreprise
Créer un enregistrement d'entreprise :
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;
};
// Utilisation
const company = await createCompany({
name: 'Acme Corporation',
domain: 'acme.com',
industry: 'Technology',
employees: 500,
revenue: 50000000,
city: 'San Francisco',
state: 'CA',
country: 'USA'
});
Association d'objets
Lier les contacts aux entreprises :
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 vers Entreprise
}
]
})
}
);
return response;
};
// Utilisation
await associateContactWithCompany('12345', '67890');
Types d'association
| Association | ID de type | Direction |
|---|---|---|
| Contact → Entreprise | 1 | Le contact est associé à l'entreprise |
| Entreprise → Contact | 1 | L'entreprise a un contact associé |
| Transaction → Contact | 3 | La transaction est associée au contact |
| Transaction → Entreprise | 5 | La transaction est associée à l'entreprise |
| Ticket → Contact | 16 | Le ticket est associé au contact |
| Ticket → Entreprise | 15 | Le ticket est associé à l'entreprise |
Création d'une transaction
Créer une opportunité de vente :
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;
};
// Utilisation
const deal = await createDeal({
name: 'Acme Corp - Licence Entreprise',
amount: 50000,
stage: 'qualification',
closeDate: '2026-06-30',
type: 'newbusiness',
description: 'Abonnement annuel entreprise'
});
// Associer avec l'entreprise et le 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 }] }) }
);
Étapes de transaction (pipeline par défaut)
| Étape | Valeur interne |
|---|---|
| Rendez-vous programmés | appointmentscheduled |
| Qualifié pour acheter | qualifiedtobuy |
| Présentation programmée | presentationscheduled |
| Décideur convaincu | decisionmakerboughtin |
| Contrat envoyé | contractsent |
| Fermée gagnée | closedwon |
| Fermée perdue | closedlost |
Webhooks
Configuration des Webhooks
Configurez des webhooks pour des notifications en temps réel :
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 // Facultatif : filtrer par changement de propriété
})
});
return response;
};
// Utilisation
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 créé : ${webhook.id}`);
Types d'événements de webhook
| Type d'événement | Déclencheur |
|---|---|
contact.creation |
Nouveau contact créé |
contact.propertyChange |
Propriété du contact mise à jour |
contact.deletion |
Contact supprimé |
company.creation |
Nouvelle entreprise créée |
company.propertyChange |
Propriété de l'entreprise mise à jour |
deal.creation |
Nouvelle transaction créée |
deal.stageChange |
Étape de la transaction modifiée |
deal.propertyChange |
Propriété de la transaction mise à jour |
ticket.creation |
Nouveau ticket créé |
ticket.propertyChange |
Propriété du ticket mise à jour |
Gestion des 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);
// Vérifier la signature du webhook
const isValid = verifyWebhookSignature(payload, signature, process.env.HUBSPOT_CLIENT_SECRET);
if (!isValid) {
console.error('Signature de webhook invalide');
return res.status(401).send('Non autorisé');
}
const events = req.body;
for (const event of events) {
console.log(`Événement : ${event.eventType}`);
console.log(`Objet : ${event.objectType} - ${event.objectId}`);
console.log(`Propriété : ${event.propertyName}`);
console.log(`Valeur : ${event.propertyValue}`);
// Acheminer vers le gestionnaire approprié
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')
);
}
Limitation de débit
Comprendre les limites de débit
HubSpot applique des limites de débit basées sur le niveau d'abonnement :
| Niveau | Requêtes/Seconde | Requêtes/Jour |
|---|---|---|
| Gratuit/Starter | 100 | 100 000 |
| Professionnel | 200 | 500 000 |
| Entreprise | 400 | 1 000 000 |
Dépasser les limites entraîne des réponses HTTP 429 (Trop de requêtes).
En-têtes de limitation de débit
| En-tête | Description |
|---|---|
X-HubSpot-RateLimit-Second-Limit |
Nb max. de requêtes par seconde |
X-HubSpot-RateLimit-Second-Remaining |
Nb de requêtes restantes cette seconde |
X-HubSpot-RateLimit-Second-Reset |
Secondes avant la réinitialisation de la limite par seconde |
X-HubSpot-RateLimit-Daily-Limit |
Nb max. de requêtes par jour |
X-HubSpot-RateLimit-Daily-Remaining |
Nb de requêtes restantes aujourd'hui |
X-HubSpot-RateLimit-Daily-Reset |
Secondes avant la réinitialisation de la limite quotidienne |
Mise en œuvre de la gestion de la limitation de débit
const makeRateLimitedRequest = async (endpoint, options = {}, maxRetries = 3) => {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await hubspotRequest(endpoint, options);
// Informations de journalisation sur la limite de débit
const remaining = response.headers.get('X-HubSpot-RateLimit-Second-Remaining');
if (remaining < 10) {
console.warn(`Faible limite de débit restante : ${remaining}`);
}
return response;
} catch (error) {
if (error.message.includes('429') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000;
console.log(`Limite de débit atteinte. Nouvelle tentative dans ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
throw error;
}
}
}
};
// Classe de limitation de débit
class HubSpotRateLimiter {
constructor(requestsPerSecond = 90) { // Rester sous la limite
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;
}
}
Liste de contrôle pour le déploiement en production
Avant de passer en direct :
- [ ] Utilisez l'authentification par application privée ou OAuth 2.0
- [ ] Stockez les jetons en toute sécurité (base de données chiffrée)
- [ ] Implémentez un rafraîchissement automatique des jetons
- [ ] Mettez en place une limitation de débit et une mise en file d'attente des requêtes
- [ ] Configurez le point de terminaison du webhook avec HTTPS
- [ ] Implémentez une gestion complète des erreurs
- [ ] Ajoutez une journalisation pour tous les appels API
- [ ] Surveillez l'utilisation de la limite de débit
- [ ] Créez un guide d'exécution pour les problèmes courants
Cas d'utilisation concrets
Synchronisation CRM
Une entreprise SaaS synchronise les données clients :
- Défi : Saisie manuelle des données entre l'application et HubSpot
- Solution : Synchronisation en temps réel via des webhooks et l'API
- Résultat : Aucune saisie manuelle, 100% de précision des données
Routage des prospects
Une agence de marketing automatise la distribution des prospects :
- Défi : Temps de réponse lents aux prospects
- Solution : Routage des prospects déclenché par webhook vers les commerciaux
- Résultat : Temps de réponse de 5 minutes, augmentation de 40% des conversions
Conclusion
L'API HubSpot offre des capacités complètes en matière de CRM et d'automatisation marketing. Points clés à retenir :
- Utilisez OAuth 2.0 pour les applications multi-locataires, les applications privées pour les intégrations internes
- Les limites de débit varient selon le niveau (100-400 requêtes/seconde)
- Les webhooks permettent une synchronisation en temps réel
- Les objets CRM prennent en charge les associations et les propriétés personnalisées
- Apidog simplifie les tests d'API et la collaboration en équipe
Section FAQ
Comment s'authentifier avec l'API HubSpot ?
Utilisez OAuth 2.0 pour les applications multi-locataires ou les applications privées pour les intégrations à portail unique. L'authentification par clé API est obsolète.
Quelles sont les limites de débit de HubSpot ?
Les limites de débit vont de 100 requêtes/seconde (Gratuit) à 400 requêtes/seconde (Entreprise), avec des limites quotidiennes de 100 000 à 1 million de requêtes.
Comment créer un contact dans HubSpot ?
POST sur /crm/v3/objects/contacts avec les propriétés, y compris l'e-mail, le prénom, le nom de famille et tous les champs personnalisés.
Puis-je créer des propriétés personnalisées ?
Oui, utilisez l'API Propriétés pour créer des champs personnalisés pour tout type d'objet.
Comment fonctionnent les webhooks dans HubSpot ?
Configurez les webhooks dans les paramètres de votre application. HubSpot envoie des requêtes POST à votre point de terminaison lorsque des événements spécifiés se produisent.
