DEV Community

Cover image for Cómo Utilizar la API de Hootsuite
Roobia
Roobia

Posted on • Originally published at apidog.com

Cómo Utilizar la API de Hootsuite

Resumen

La API de Hootsuite permitía integraciones programáticas para la gestión de redes sociales. Usaba OAuth 2.0, endpoints RESTful para perfiles, publicaciones, analítica y gestión de equipos, con límites de tasa de 50-200 solicitudes/minuto según el plan. Esta guía cubre autenticación, programación de publicaciones, obtención de análisis, gestión de equipos y estrategias de integración en producción.

Nota: Hootsuite desaprobó su API pública en 2024. Aquí verás alternativas: integraciones con socios, webhooks y APIs de terceros con funcionalidades equivalentes.

Prueba Apidog hoy

Introducción

Hootsuite gestiona más de 30 millones de cuentas en más de 200.000 empresas de 175+ países. Si desarrollas herramientas para social media, marketing o dashboards de análisis, la integración con APIs de redes sociales es clave para acceder a esta audiencia empresarial.

El reto: gestores de redes sociales con más de 20 cuentas pierden entre 25 y 35 horas semanales en tareas manuales. Una buena integración de APIs puede automatizar publicación, monitoreo, análisis de sentimiento y reporting.

Estado de la API de Hootsuite y alternativas

Situación actual de la API

Desde 2024, la API pública de Hootsuite está desaprobada. Opciones de integración:

Enfoque Descripción Ideal para
APIs de Plataformas Nativas Integración directa con Facebook, Twitter, LinkedIn, etc. Control total, soluciones personalizadas
Audiense Inteligencia de audiencia propiedad de Hootsuite Análisis de audiencia
HeyOrca API de programación de redes sociales Calendarios de contenido
API de Buffer Gestión de redes sociales Equipos pequeños
API de Sprout Social Gestión social empresarial Grandes organizaciones
API de Agorapulse CRM de redes sociales Gestión de la comunidad

Enfoque recomendado: APIs de plataformas nativas

La integración directa con las APIs oficiales de cada red social ofrece máxima flexibilidad y control. Ejemplo de principales opciones:

Plataforma Nombre de la API Características clave
Facebook/Instagram Graph API Publicaciones, insights, comentarios
Twitter/X API v2 Tweets, análisis, streams
LinkedIn Marketing API Publicaciones, páginas de empresa, anuncios
Pinterest API v5 Pines, tableros, análisis
TikTok Display API Videos, información del usuario
YouTube Data API Videos, listas de reproducción, análisis

Primeros pasos: Autenticación multiplataforma

Paso 1: Registrar aplicaciones de desarrollador

Crea cuentas de desarrollador en cada plataforma y almacena las credenciales de forma segura:

// Store credentials securely
const SOCIAL_CREDENTIALS = {
  facebook: {
    appId: process.env.FB_APP_ID,
    appSecret: process.env.FB_APP_SECRET,
    redirectUri: process.env.FB_REDIRECT_URI
  },
  twitter: {
    apiKey: process.env.TWITTER_API_KEY,
    apiSecret: process.env.TWITTER_API_SECRET,
    redirectUri: process.env.TWITTER_REDIRECT_URI
  },
  linkedin: {
    clientId: process.env.LINKEDIN_CLIENT_ID,
    clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
    redirectUri: process.env.LINKEDIN_REDIRECT_URI
  },
  instagram: {
    appId: process.env.FB_APP_ID, // Uses Facebook Login
    appSecret: process.env.FB_APP_SECRET
  }
};
Enter fullscreen mode Exit fullscreen mode

Paso 2: Implementar el flujo OAuth 2.0

Centraliza el manejo de OAuth para varias plataformas:

const getAuthUrl = (platform, state) => {
  const configs = {
    facebook: {
      url: 'https://www.facebook.com/v18.0/dialog/oauth',
      params: {
        client_id: SOCIAL_CREDENTIALS.facebook.appId,
        redirect_uri: SOCIAL_CREDENTIALS.facebook.redirectUri,
        scope: 'pages_manage_posts,pages_read_engagement,instagram_basic,instagram_content_publish',
        state
      }
    },
    twitter: {
      url: 'https://twitter.com/i/oauth2/authorize',
      params: {
        client_id: SOCIAL_CREDENTIALS.twitter.apiKey,
        redirect_uri: SOCIAL_CREDENTIALS.twitter.redirectUri,
        scope: 'tweet.read tweet.write users.read offline.access',
        state,
        response_type: 'code'
      }
    },
    linkedin: {
      url: 'https://www.linkedin.com/oauth/v2/authorization',
      params: {
        client_id: SOCIAL_CREDENTIALS.linkedin.clientId,
        redirect_uri: SOCIAL_CREDENTIALS.linkedin.redirectUri,
        scope: 'w_member_social r_basicprofile',
        state,
        response_type: 'code'
      }
    }
  };

  const config = configs[platform];
  const params = new URLSearchParams(config.params);
  return `${config.url}?${params.toString()}`;
};

// Handle OAuth callback
const handleOAuthCallback = async (platform, code) => {
  const tokenEndpoints = {
    facebook: 'https://graph.facebook.com/v18.0/oauth/access_token',
    twitter: 'https://api.twitter.com/2/oauth2/token',
    linkedin: 'https://www.linkedin.com/oauth/v2/accessToken'
  };

  const response = await fetch(tokenEndpoints[platform], {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      client_id: SOCIAL_CREDENTIALS[platform].apiKey || SOCIAL_CREDENTIALS[platform].appId || SOCIAL_CREDENTIALS[platform].clientId,
      client_secret: SOCIAL_CREDENTIALS[platform].appSecret || SOCIAL_CREDENTIALS[platform].apiSecret,
      redirect_uri: SOCIAL_CREDENTIALS[platform].redirectUri,
      code,
      grant_type: 'authorization_code'
    })
  });

  return response.json();
};
Enter fullscreen mode Exit fullscreen mode

Paso 3: Almacenar tokens de forma segura

Ejemplo de esquema para guardar tokens:

// Database schema for social tokens
const SocialToken = {
  userId: 'user_123',
  platform: 'facebook',
  accessToken: 'encrypted_token_here',
  refreshToken: 'encrypted_refresh_token',
  tokenExpiry: Date.now() + 5183999, // 60 days
  scopes: ['pages_manage_posts', 'pages_read_engagement'],
  pageId: 'page_456', // For Facebook/Instagram
  pageName: 'My Business Page'
};
Enter fullscreen mode Exit fullscreen mode

Programación y publicación de publicaciones

Creando una publicación multiplataforma

Envía publicaciones a varias plataformas en paralelo:

const createSocialPost = async (postData) => {
  const results = {};

  // Facebook Page Post
  if (postData.platforms.includes('facebook')) {
    results.facebook = await postToFacebook({
      pageId: postData.facebookPageId,
      message: postData.message,
      link: postData.link,
      photo: postData.photo
    });
  }

  // Twitter Post
  if (postData.platforms.includes('twitter')) {
    results.twitter = await postToTwitter({
      text: postData.message,
      media: postData.photo
    });
  }

  // LinkedIn Post
  if (postData.platforms.includes('linkedin')) {
    results.linkedin = await postToLinkedIn({
      authorUrn: postData.linkedinAuthorUrn,
      text: postData.message,
      contentUrl: postData.link
    });
  }

  // Instagram Post
  if (postData.platforms.includes('instagram')) {
    results.instagram = await postToInstagram({
      igAccountId: postData.igAccountId,
      imageUrl: postData.photo,
      caption: postData.message
    });
  }

  return results;
};

// Facebook posting
const postToFacebook = async (postData) => {
  const token = await getFacebookPageToken(postData.pageId);

  const params = new URLSearchParams({
    message: postData.message,
    access_token: token
  });

  if (postData.link) {
    params.append('link', postData.link);
  }

  if (postData.photo) {
    params.append('photo', postData.photo);
  }

  const response = await fetch(
    `https://graph.facebook.com/v18.0/${postData.pageId}/feed?${params.toString()}`,
    { method: 'POST' }
  );

  return response.json();
};

// Twitter posting
const postToTwitter = async (postData) => {
  const token = await getTwitterToken();

  let mediaIds = [];
  if (postData.media) {
    // Upload media first
    const mediaUpload = await uploadTwitterMedia(postData.media, token);
    mediaIds = [mediaUpload.media_id_string];
  }

  const response = await fetch('https://api.twitter.com/2/tweets', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      text: postData.text,
      media: mediaIds.length > 0 ? { media_ids: mediaIds } : undefined
    })
  });

  return response.json();
};

// LinkedIn posting
const postToLinkedIn = async (postData) => {
  const token = await getLinkedInToken();

  const post = {
    author: postData.authorUrn,
    lifecycleState: 'PUBLISHED',
    specificContent: {
      'com.linkedin.ugc.ShareContent': {
        shareCommentary: {
          text: postData.text
        },
        shareMediaCategory: postData.contentUrl ? 'ARTICLE' : 'NONE',
        media: postData.contentUrl ? [{
          status: 'READY',
          media: postData.contentUrn,
          description: { text: postData.text }
        }] : []
      }
    },
    visibility: {
      'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC'
    }
  };

  const response = await fetch('https://api.linkedin.com/v2/ugcPosts', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
      'X-Restli-Protocol-Version': '2.0.0'
    },
    body: JSON.stringify(post)
  });

  return response.json();
};

// Instagram posting
const postToInstagram = async (postData) => {
  const token = await getInstagramToken();

  // Step 1: Create media container
  const containerResponse = await fetch(
    `https://graph.facebook.com/v18.0/${postData.igAccountId}/media`,
    {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${token}` },
      body: JSON.stringify({
        image_url: postData.imageUrl,
        caption: postData.caption
      })
    }
  );

  const container = await containerResponse.json();

  // Step 2: Publish
  const publishResponse = await fetch(
    `https://graph.facebook.com/v18.0/${postData.igAccountId}/media_publish`,
    {
      method: 'POST',
      headers: { 'Authorization': `Bearer ${token}` },
      body: JSON.stringify({ creation_id: container.id })
    }
  );

  return publishResponse.json();
};
Enter fullscreen mode Exit fullscreen mode

Programación de publicaciones

Utiliza una base de datos y una cola de trabajos para programar publicaciones:

const schedulePost = async (postData, scheduledTime) => {
  // Store in database for later execution
  const scheduledPost = await db.scheduledPosts.create({
    message: postData.message,
    platforms: postData.platforms,
    scheduledTime: scheduledTime,
    status: 'pending',
    media: postData.media,
    link: postData.link
  });

  // Set up job queue
  await jobQueue.add('publish-social-post', {
    postId: scheduledPost.id
  }, {
    delay: scheduledTime - Date.now()
  });

  return scheduledPost;
};

// Job processor
jobQueue.process('publish-social-post', async (job) => {
  const post = await db.scheduledPosts.findById(job.data.postId);

  try {
    const result = await createSocialPost(post);

    await db.scheduledPosts.update(post.id, {
      status: 'published',
      publishedAt: new Date(),
      results: result
    });

    return result;
  } catch (error) {
    await db.scheduledPosts.update(post.id, {
      status: 'failed',
      error: error.message
    });

    throw error;
  }
});
Enter fullscreen mode Exit fullscreen mode

Análisis e informes

Obtención de análisis multiplataforma

Agrega métricas de cada red social y centraliza los resultados:

const getSocialAnalytics = async (accountId, dateRange) => {
  const analytics = {
    facebook: await getFacebookAnalytics(accountId.facebook, dateRange),
    twitter: await getTwitterAnalytics(accountId.twitter, dateRange),
    linkedin: await getLinkedInAnalytics(accountId.linkedin, dateRange),
    instagram: await getInstagramAnalytics(accountId.instagram, dateRange)
  };

  // Aggregate metrics
  const totals = {
    impressions: sum(analytics, 'impressions'),
    engagement: sum(analytics, 'engagement'),
    clicks: sum(analytics, 'clicks'),
    shares: sum(analytics, 'shares'),
    comments: sum(analytics, 'comments'),
    newFollowers: sum(analytics, 'newFollowers')
  };

  return { analytics, totals };
};

// Facebook Insights
const getFacebookAnalytics = async (pageId, dateRange) => {
  const token = await getFacebookPageToken(pageId);

  const metrics = [
    'page_impressions_unique',
    'page_engaged_users',
    'page_post_engagements',
    'page_clicks',
    'page_fan_adds'
  ];

  const params = new URLSearchParams({
    metric: metrics.join(','),
    since: dateRange.from,
    until: dateRange.until,
    access_token: token
  });

  const response = await fetch(
    `https://graph.facebook.com/v18.0/${pageId}/insights?${params.toString()}`
  );

  return response.json();
};

// Twitter Analytics
const getTwitterAnalytics = async (userId, dateRange) => {
  const token = await getTwitterToken();

  const response = await fetch(
    `https://api.twitter.com/2/users/${userId}/metrics/private`,
    {
      headers: { 'Authorization': `Bearer ${token}` }
    }
  );

  return response.json();
};

// LinkedIn Analytics
const getLinkedInAnalytics = async (organizationId, dateRange) => {
  const token = await getLinkedInToken();

  const response = await fetch(
    `https://api.linkedin.com/v2/organizationalEntityFollowerStatistics?q=organizationalEntity&organizationalEntity=${organizationId}`,
    {
      headers: { 'Authorization': `Bearer ${token}` }
    }
  );

  return response.json();
};

// Instagram Insights
const getInstagramAnalytics = async (igAccountId, dateRange) => {
  const token = await getInstagramToken();

  const metrics = [
    'impressions',
    'reach',
    'engagement',
    'profile_views',
    'follower_count'
  ];

  const params = new URLSearchParams({
    metric: metrics.join(','),
    period: 'day',
    since: dateRange.from,
    until: dateRange.until
  });

  const response = await fetch(
    `https://graph.facebook.com/v18.0/${igAccountId}/insights?${params.toString()}`,
    {
      headers: { 'Authorization': `Bearer ${token}` }
    }
  );

  return response.json();
};

function sum(analytics, metric) {
  return Object.values(analytics).reduce((total, platform) => {
    return total + (platform.data?.[metric] || 0);
  }, 0);
}
Enter fullscreen mode Exit fullscreen mode

Gestión de equipos

Control de acceso basado en roles

Define permisos y roles en tu sistema:

const TEAM_ROLES = {
  ADMIN: 'admin',
  MANAGER: 'manager',
  CONTRIBUTOR: 'contributor',
  VIEWER: 'viewer'
};

const ROLE_PERMISSIONS = {
  [TEAM_ROLES.ADMIN]: ['create', 'read', 'update', 'delete', 'manage_team', 'billing'],
  [TEAM_ROLES.MANAGER]: ['create', 'read', 'update', 'approve_posts'],
  [TEAM_ROLES.CONTRIBUTOR]: ['create', 'read'],
  [TEAM_ROLES.VIEWER]: ['read']
};

const checkPermission = (userRole, requiredPermission) => {
  const permissions = ROLE_PERMISSIONS[userRole] || [];
  return permissions.includes(requiredPermission);
};
Enter fullscreen mode Exit fullscreen mode

Limitación de tasa

Límites de tasa de la plataforma

Plataforma Límite Ventana
Facebook Graph 200 llamadas Por hora por usuario
Twitter API v2 300 tweets Cada 15 min
LinkedIn 100-500 llamadas Por día
Instagram 200 llamadas Por hora

Implementación del manejo de límites de tasa

Implementa tu propio rate limiter para cada plataforma:

class SocialMediaRateLimiter {
  constructor() {
    this.limits = {
      facebook: { limit: 200, window: 3600000 },
      twitter: { limit: 300, window: 900000 },
      linkedin: { limit: 500, window: 86400000 },
      instagram: { limit: 200, window: 3600000 }
    };
    this.counters = {};
  }

  async request(platform, endpoint, options) {
    await this.waitForCapacity(platform);

    const response = await fetch(endpoint, options);
    this.incrementCounter(platform);

    return response;
  }

  async waitForCapacity(platform) {
    const limit = this.limits[platform];
    const counter = this.counters[platform] || { count: 0, resetTime: Date.now() };

    if (Date.now() > counter.resetTime + limit.window) {
      counter.count = 0;
      counter.resetTime = Date.now();
    }

    if (counter.count >= limit.limit) {
      const waitTime = counter.resetTime + limit.window - Date.now();
      await new Promise(resolve => setTimeout(resolve, waitTime));
    }

    this.counters[platform] = counter;
  }

  incrementCounter(platform) {
    if (!this.counters[platform]) {
      this.counters[platform] = { count: 0, resetTime: Date.now() };
    }
    this.counters[platform].count++;
  }
}
Enter fullscreen mode Exit fullscreen mode

Lista de verificación de implementación en producción

Antes de lanzar, asegúrate de lo siguiente:

  • [ ] Implementar OAuth 2.0 para todas las plataformas
  • [ ] Almacenar tokens de forma segura y cifrada
  • [ ] Configurar la actualización automática de tokens
  • [ ] Implementar limitación de tasa por plataforma
  • [ ] Añadir manejo robusto de errores
  • [ ] Configurar logs para todas las llamadas a la API
  • [ ] Crear flujos de aprobación de publicaciones
  • [ ] Implementar moderación de contenido
  • [ ] Configurar agregación de analítica
  • [ ] Crear mecanismos de publicación de respaldo

Casos de uso en el mundo real

Panel de control de redes sociales

Escenario: Agencia gestiona 50+ cuentas de clientes

Solución: Dashboard central con publicación multiplataforma

Resultado: 60% menos tiempo, marca coherente

Distribución automatizada de contenido

Escenario: Editor quiere compartir artículos nuevos automáticamente

Solución: Automatización para publicar en todas las plataformas

Resultado: Distribución instantánea, triplica tráfico social

Conclusión

Aunque la API pública de Hootsuite ya no está disponible, las APIs nativas ofrecen capacidades completas para gestión y analítica social. Claves de implementación:

  • Implementa OAuth 2.0 por plataforma
  • Los límites de tasa varían considerablemente
  • La publicación unificada exige lógica específica por canal
  • La agregación de analítica multiplica el valor del reporting
  • Apidog optimiza pruebas y colaboración en APIs

Sección de Preguntas Frecuentes

¿Hootsuite todavía tiene una API?

No, Hootsuite desaprobó su API pública en 2024. Usa APIs nativas o plataformas externas como Buffer, Sprout Social o Agorapulse.

¿Cómo publico en múltiples plataformas a la vez?

Implementa OAuth para cada red y crea una función que publique en paralelo usando la API de cada una.

¿Cuáles son los límites de tasa para las APIs de redes sociales?

Varían: Facebook (200/hora), Twitter (300/15min), LinkedIn (100-500/día), Instagram (200/hora).

¿Cómo programo publicaciones?

Guarda los posts en una base de datos con scheduled_time y usa una cola de trabajos (Bull, Agenda) para publicarlos cuando corresponda.

¿Puedo obtener análisis de todas las plataformas?

Sí, cada red social tiene su propia API de analítica. Agrega los datos para un reporte multiplataforma.

Top comments (0)