DEV Community

Cover image for Comment utiliser l'API HubSpot en 2026
Antoine Laurent
Antoine Laurent

Posted on • Originally published at apidog.com

Comment utiliser l'API HubSpot en 2026

En bref

API HubSpot permet aux développeurs de s'intégrer en profondeur avec les hubs CRM, marketing, ventes et services HubSpot. Elle propose l'authentification OAuth 2.0 ou via applications privées, des points de terminaison RESTful pour contacts, entreprises, transactions, tickets, etc., et des limites de débit adaptées à votre abonnement. Ce guide détaille la configuration de l'authentification, les principaux endpoints, la gestion des webhooks et les bonnes pratiques pour un déploiement fiable.

Essayez Apidog dès aujourd'hui

Introduction

HubSpot gère plus de 194 000 comptes clients et des milliards d'enregistrements CRM. Pour automatiser la synchronisation CRM, l'automatisation marketing ou la gestion des ventes, l'intégration native de l'API HubSpot s'impose pour toucher plus de 7 millions d'utilisateurs.

À retenir : les entreprises perdent 15 à 20 heures/semaine en saisie manuelle entre systèmes. Un connecteur robuste via l'API HubSpot automatise la synchronisation des contacts, la gestion des transactions, l'automatisation marketing et le reporting inter-applications.

💡 Apidog simplifie les tests d'intégration d'API : testez vos endpoints HubSpot, validez les flux OAuth, inspectez les payloads webhooks et déboguez l'authentification dans un seul workspace. Importez vos specs, simulez des réponses et partagez vos scénarios de tests en équipe.

Qu'est-ce que l'API HubSpot ?

HubSpot expose une API RESTful pour gérer vos données CRM et automatiser vos processus marketing. Vous pouvez manipuler :

  • Contacts, entreprises, transactions, tickets, objets personnalisés
  • E-mails marketing, landing pages
  • Pipelines de vente, séquences
  • Tickets et conversations de support
  • Analytics et reporting
  • Workflows d'automatisation
  • Fichiers et assets

Fonctionnalités clés

Fonctionnalité Description
Conception RESTful Méthodes HTTP standard, réponses JSON
OAuth 2.0 + Applications privées Authentification flexible
Webhooks Notifications temps réel sur les changements d'objets
Limitation de débit Dépend du plan (100-400 req/sec)
Objets CRM Standard et personnalisés
Associations Lier des objets (contact-entreprise, etc.)
Propriétés Champs personnalisés par objet
API de recherche Filtrage & tri complexes

Vue d'ensemble de l'architecture de l'API

API REST versionnées, base URL :

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

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 Automatisation v4 Actuelle OAuth 2.0, Application privée Inscription au workflow
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 : la clé API HubSpot est dépréciée. Utilisez OAuth 2.0 ou une application privée.

Premiers pas : Configuration de l'authentification

Étape 1 : Créer un compte développeur HubSpot

  1. Allez sur le Portail développeur HubSpot
  2. Connectez-vous ou créez un compte
  3. Naviguez vers Applications dans votre dashboard développeur
  4. Cliquez sur Créer une application

Étape 2 : Choisir la méthode d'authentification

Méthode Pour qui ? Sécurité
OAuth 2.0 Apps multi-locataires, publiques Élevée (user-based)
Application privée Intégrations internes, single portal Élevée (portal-based)

Étape 3 : Configurer une application privée (recommandé pour usage interne)

  1. Allez dans Paramètres > Intégrations > Applications privées
  2. Cliquez sur Créer une application privée
  3. Sélectionnez les scopes nécessaires :
contacts
crm.objects.companies
crm.objects.deals
crm.objects.tickets
automation
webhooks
Enter fullscreen mode Exit fullscreen mode
  1. Générez un jeton d'accès et stockez-le dans un fichier .env :
# .env
HUBSPOT_ACCESS_TOKEN="pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
HUBSPOT_PORTAL_ID="12345678"
Enter fullscreen mode Exit fullscreen mode

Étape 4 : Configurer OAuth 2.0 (pour apps multi-locataires)

Configurez vos paramètres OAuth :

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;

// Générer 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()}`;
};
Enter fullscreen mode Exit fullscreen mode

Étape 5 : Échanger le code contre un jeton d'accès

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
  };
};

// Gestion du callback OAuth
app.get('/oauth/callback', async (req, res) => {
  const { code, state } = req.query;

  try {
    const tokens = await exchangeCodeForToken(code);

    // Stockez les tokens
    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');
  }
});
Enter fullscreen mode Exit fullscreen mode

Étape 6 : Rafraîchir le jeton d'accès

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,
    expiresIn: data.expires_in
  };
};

// Middleware pour garantir un token valide
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;
};
Enter fullscreen mode Exit fullscreen mode

Étape 7 : Effectuer des appels API authentifiés

Centralisez vos appels :

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();
};

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

Travailler avec les objets CRM

Création d'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}`);
Enter fullscreen mode Exit fullscreen mode

Propriétés des contacts

Propriété Type Description
email String E-mail principal (identifiant unique)
firstname String Prénom
lastname String Nom de famille
phone String Numéro de téléphone
company String Nom de l'entreprise
website String URL du site web
lifecyclestage Enum lead, marketingqualifiedlead, salesqualifiedlead, opportunity, customer, evangelist, subscriber
createdate DateTime Généré automatiquement
lastmodifieddate DateTime Généré automatiquement

Obtenir un contact

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}`);
Enter fullscreen mode Exit fullscreen mode

Recherche de contacts

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;
};

// Exemple : trouver tous les contacts d'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}`);
});
Enter fullscreen mode Exit fullscreen mode

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

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'
});
Enter fullscreen mode Exit fullscreen mode

Association d'objets

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');
Enter fullscreen mode Exit fullscreen mode

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

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 }] }) }
);
Enter fullscreen mode Exit fullscreen mode

É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

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 // Optionnel
    })
  });

  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}`);
Enter fullscreen mode Exit fullscreen mode

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 à jour
ticket.creation Nouveau ticket créé
ticket.propertyChange Propriété du ticket à 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}`);

    // Dispatcher selon l'événement
    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

Limitation de débit

Comprendre les limites de débit

Niveau Requêtes/seconde Requêtes/jour
Gratuit/Starter 100 100 000
Professionnel 200 500 000
Entreprise 400 1 000 000

Un dépassement déclenche des réponses HTTP 429 (Too Many Requests).

En-têtes de limitation de débit

En-tête Description
X-HubSpot-RateLimit-Second-Limit Limite max/seconde
X-HubSpot-RateLimit-Second-Remaining Requêtes restantes cette seconde
X-HubSpot-RateLimit-Second-Reset Secondes avant réinitialisation
X-HubSpot-RateLimit-Daily-Limit Limite max/jour
X-HubSpot-RateLimit-Daily-Remaining Requêtes restantes aujourd'hui
X-HubSpot-RateLimit-Daily-Reset Secondes avant réinitialisation journalière

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);

      // Journalisation de 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;
      }
    }
  }
};

// Rate limiter simple
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;
  }
}
Enter fullscreen mode Exit fullscreen mode

Liste de contrôle pour le déploiement en production

Avant de passer en production, vérifiez les points suivants :

  • [ ] Authentification via application privée ou OAuth 2.0
  • [ ] Stockage sécurisé des jetons (base de données chiffrée)
  • [ ] Rafraîchissement automatique des jetons
  • [ ] Gestion de la limitation de débit et file d'attente des requêtes
  • [ ] Point de terminaison Webhook en HTTPS
  • [ ] Gestion complète des erreurs
  • [ ] Journalisation des appels API
  • [ ] Surveillance de l'utilisation des limites de débit
  • [ ] Documentation sur la résolution des problèmes courants

Cas d'utilisation concrets

Synchronisation CRM

  • Défi : Saisie manuelle des données entre l'app et HubSpot
  • Solution : Synchronisation temps réel via webhooks + API
  • Résultat : Plus de saisie manuelle, données 100% synchronisées

Routage des prospects

  • Défi : Temps de réponse lent sur les nouveaux leads
  • Solution : Webhooks pour router les prospects automatiquement vers les bons commerciaux
  • Résultat : Temps de réponse réduit à 5 min, +40% de conversions

Conclusion

L'API HubSpot propose une plateforme robuste pour la gestion CRM et l'automatisation marketing. À retenir :

  • Utilisez OAuth 2.0 pour les apps multi-locataires, l'application privée pour les usages internes
  • Les limites de débit varient selon le plan (100-400 req/sec)
  • Les webhooks garantissent la synchronisation temps réel
  • Les objets CRM supportent associations et champs personnalisés
  • Apidog facilite les tests d'API et la collaboration

Section FAQ

Comment s'authentifier avec l'API HubSpot ?

Utilisez OAuth 2.0 pour les applications multi-locataires, ou application privée pour les intégrations internes. La clé API est obsolète.

Quelles sont les limites de débit de HubSpot ?

De 100 req/sec (Gratuit) à 400 req/sec (Entreprise), et de 100 000 à 1 million de requêtes quotidiennes.

Comment créer un contact dans HubSpot ?

POST sur /crm/v3/objects/contacts avec les propriétés (email, prénom, nom, etc.).

Puis-je créer des propriétés personnalisées ?

Oui. Utilisez l'API Propriétés pour ajouter des champs sur tout type d'objet.

Comment fonctionnent les webhooks dans HubSpot ?

Configurez-les dans votre app : HubSpot enverra des POST vers votre endpoint sur les événements configurés.

Top comments (0)