DEV Community

Cover image for วิธีใช้ Hootsuite API อย่างไร
Thanawat Wongchai
Thanawat Wongchai

Posted on • Originally published at apidog.com

วิธีใช้ Hootsuite API อย่างไร

โดยสรุป

Hootsuite API ช่วยให้นักพัฒนาสามารถรวมระบบเข้ากับเวิร์กโฟลว์การจัดการโซเชียลมีเดียได้โดยใช้โปรแกรม โดยใช้การยืนยันตัวตน OAuth 2.0, ปลายทาง RESTful สำหรับโปรไฟล์ โพสต์ การวิเคราะห์ และการจัดการทีม พร้อมกับข้อจำกัดอัตราการเรียกใช้งานที่ 50-200 คำขอต่อนาที ขึ้นอยู่กับแผนบริการ คู่มือนี้ครอบคลุมการตั้งค่าการยืนยันตัวตน การตั้งเวลาโพสต์ การดึงข้อมูลวิเคราะห์ การจัดการทีม และกลยุทธ์การรวมระบบสำหรับการใช้งานจริง

ทดลองใช้ Apidog วันนี้

หมายเหตุ: Hootsuite ได้ยกเลิก Public API ของตนเองแล้วตั้งแต่ปี 2024 คู่มือนี้ครอบคลุมแนวทางทางเลือกอื่น ๆ รวมถึงการผสานรวมพันธมิตรของ Hootsuite, Webhooks และ API การจัดการโซเชียลมีเดียของบุคคลที่สามที่ให้ฟังก์ชันการทำงานที่คล้ายกัน

บทนำ

Hootsuite จัดการบัญชีโซเชียลมีเดียมากกว่า 30 ล้านบัญชีสำหรับธุรกิจกว่า 200,000 แห่งในกว่า 175 ประเทศ สำหรับนักพัฒนาที่สร้างเครื่องมือโซเชียลมีเดีย แพลตฟอร์มการตลาด หรือแดชบอร์ดการวิเคราะห์ การผสานรวม API โซเชียลมีเดียเป็นสิ่งจำเป็นในการเข้าถึงกลุ่มธุรกิจขนาดใหญ่นี้

นี่คือความจริง: ผู้จัดการโซเชียลมีเดียที่ดูแลบัญชีมากกว่า 20 บัญชีสูญเสียเวลา 25-35 ชั่วโมงต่อสัปดาห์ไปกับการโพสต์ด้วยตนเอง การติดตามการมีส่วนร่วม และการสร้างรายงาน การผสานรวม API โซเชียลมีเดียที่ดีจะช่วยให้การเผยแพร่เนื้อหา การตรวจสอบการมีส่วนร่วม การวิเคราะห์ความรู้สึก และการรายงานประสิทธิภาพเป็นไปโดยอัตโนมัติ

สถานะ API ของ Hootsuite และทางเลือกอื่น

สถานการณ์ API ปัจจุบัน

ตั้งแต่ปี 2024 เป็นต้นไป Hootsuite ได้ยกเลิกการใช้งาน Public API ของตนเองแล้ว ตัวเลือกสำหรับการเชื่อมต่อมีดังนี้:

แนวทาง คำอธิบาย เหมาะสมที่สุดสำหรับ
Native Platform APIs การเชื่อมต่อโดยตรงกับ Facebook, Twitter, LinkedIn เป็นต้น ควบคุมได้เต็มที่, โซลูชั่นที่ปรับแต่งเอง
Audiense ระบบวิเคราะห์ข้อมูลผู้ชม (Audience Intelligence) ที่เป็นของ Hootsuite การวิเคราะห์กลุ่มเป้าหมาย
HeyOrca API สำหรับการตั้งเวลาโพสต์โซเชียลมีเดีย ปฏิทินเนื้อหา
Buffer API การจัดการโซเชียลมีเดีย ทีมขนาดเล็ก
Sprout Social API การจัดการโซเชียลมีเดียระดับองค์กร องค์กรขนาดใหญ่
Agorapulse API CRM สำหรับโซเชียลมีเดีย การจัดการชุมชน

แนวทางที่แนะนำ: Native Platform APIs

สำหรับกรณีการใช้งานส่วนใหญ่ การเชื่อมต่อโดยตรงกับแพลตฟอร์มโซเชียลจะให้ความยืดหยุ่นสูงสุด:

แพลตฟอร์ม ชื่อ API คุณสมบัติหลัก
Facebook/Instagram Graph API โพสต์, ข้อมูลเชิงลึก, ความคิดเห็น
Twitter/X API v2 ทวีต, การวิเคราะห์, สตรีม
LinkedIn Marketing API โพสต์, เพจบริษัท, โฆษณา
Pinterest API v5 พิน, บอร์ด, การวิเคราะห์
TikTok Display API วิดีโอ, ข้อมูลผู้ใช้
YouTube Data API วิดีโอ, เพลย์ลิสต์, การวิเคราะห์

เริ่มต้นใช้งาน: การยืนยันตัวตนสำหรับหลายแพลตฟอร์ม

ขั้นตอนที่ 1: ลงทะเบียนแอปพลิเคชันสำหรับนักพัฒนา

สร้างบัญชีนักพัฒนาสำหรับแต่ละแพลตฟอร์มและจัดเก็บ credentials อย่างปลอดภัย:

// 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

ขั้นตอนที่ 2: ใช้กระบวนการ OAuth 2.0

สร้างฟังก์ชันรวมสำหรับขอ Authorization และแลกเปลี่ยน Access Token:

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

ขั้นตอนที่ 3: จัดเก็บโทเค็นอย่างปลอดภัย

จัดเก็บ Access Token อย่างปลอดภัย พร้อมข้อมูลการหมดอายุและ Scope:

// 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

การตั้งเวลาและเผยแพร่โพสต์

การสร้างโพสต์สำหรับหลายแพลตฟอร์ม

เผยแพร่โพสต์พร้อมกันบนหลายแพลตฟอร์มด้วยฟังก์ชันเดียว:

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

การตั้งเวลาโพสต์

บันทึกโพสต์ลงฐานข้อมูลและใช้ job queue เพื่อเผยแพร่:

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

การวิเคราะห์และรายงาน

การดึงข้อมูลวิเคราะห์ข้ามแพลตฟอร์ม

รวบรวมข้อมูลวิเคราะห์จากแต่ละแพลตฟอร์มแล้วรวมผลลัพธ์:

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

การจัดการทีม

การควบคุมการเข้าถึงตามบทบาท (Role-Based Access Control)

กำหนด Role และ Permission สำหรับแต่ละสมาชิกในทีม:

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

การจำกัดอัตราการเรียกใช้งาน (Rate Limiting)

การจำกัดอัตราการเรียกใช้งานของแต่ละแพลตฟอร์ม

แพลตฟอร์ม ขีดจำกัด ช่วงเวลา
Facebook Graph 200 ครั้ง ต่อชั่วโมงต่อผู้ใช้
Twitter API v2 300 ทวีต ต่อ 15 นาที
LinkedIn 100-500 ครั้ง ต่อวัน
Instagram 200 ครั้ง ต่อชั่วโมง

การใช้งานการจัดการการจำกัดอัตราการเรียกใช้งาน

สร้าง Rate Limiter สำหรับแต่ละแพลตฟอร์ม:

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

รายการตรวจสอบการปรับใช้งานจริง

ก่อนใช้งานจริง ตรวจสอบให้แน่ใจว่าได้ดำเนินการดังนี้:

  • [ ] ใช้งาน OAuth 2.0 สำหรับทุกแพลตฟอร์ม
  • [ ] จัดเก็บโทเค็นอย่างปลอดภัยด้วยการเข้ารหัส
  • [ ] ตั้งค่าการรีเฟรชโทเค็นอัตโนมัติ
  • [ ] ใช้งานการจำกัดอัตราการเรียกใช้งานของแต่ละแพลตฟอร์ม
  • [ ] เพิ่มการจัดการข้อผิดพลาดที่ครอบคลุม
  • [ ] ตั้งค่าการบันทึก (logging) สำหรับการเรียกใช้ API ทั้งหมด
  • [ ] สร้างขั้นตอนการอนุมัติโพสต์
  • [ ] ใช้งานการดูแลเนื้อหา (content moderation)
  • [ ] ตั้งค่าการรวมข้อมูลวิเคราะห์
  • [ ] สร้างกลไกการโพสต์สำรอง

กรณีศึกษาจริง

แดชบอร์ดโซเชียลมีเดีย

เอเจนซี่การตลาดสร้างแดชบอร์ดรวม:

  • ความท้าทาย: การจัดการบัญชีลูกค้ากว่า 50 บัญชีในหลายแพลตฟอร์ม
  • โซลูชั่น: แดชบอร์ดกลางพร้อมการโพสต์ข้ามแพลตฟอร์ม
  • ผลลัพธ์: ประหยัดเวลาได้ 60%, การแสดงตนของแบรนด์ที่สอดคล้องกัน

การเผยแพร่เนื้อหาอัตโนมัติ

ผู้เผยแพร่ข่าวสารทำให้การแชร์บทความเป็นไปโดยอัตโนมัติ:

  • ความท้าทาย: การแชร์เนื้อหาใหม่ด้วยตนเอง
  • โซลูชั่น: โพสต์บทความใหม่ทั้งหมดไปยังทุกแพลตฟอร์มโดยอัตโนมัติ
  • ผลลัพธ์: การเผยแพร่ทันที, การเข้าชมจากโซเชียลเพิ่มขึ้น 3 เท่า

บทสรุป

แม้ว่า Public API ของ Hootsuite จะถูกยกเลิกไปแล้ว แต่ Native Platform APIs ยังคงมอบความสามารถในการจัดการโซเชียลมีเดียที่ครอบคลุม ประเด็นสำคัญที่ควรทราบ:

  • ใช้งาน OAuth 2.0 แยกกันสำหรับแต่ละแพลตฟอร์ม
  • ข้อจำกัดอัตราการเรียกใช้งานแตกต่างกันอย่างมากในแต่ละแพลตฟอร์ม
  • การโพสต์แบบรวมศูนย์ต้องใช้การใช้งานเฉพาะแพลตฟอร์ม
  • การรวมข้อมูลวิเคราะห์ให้ข้อมูลเชิงลึกข้ามแพลตฟอร์ม
  • Apidog ช่วยให้การทดสอบ API และการทำงานร่วมกันเป็นทีมมีประสิทธิภาพยิ่งขึ้น

คำถามที่พบบ่อย

Hootsuite ยังมี API อยู่หรือไม่?

Hootsuite ได้ยกเลิก Public API ของตนเองในปี 2024 คุณควรใช้ Native Platform APIs หรือแพลตฟอร์มจัดการอื่น ๆ เช่น Buffer, Sprout Social หรือ Agorapulse แทน

ฉันจะโพสต์ไปยังหลายแพลตฟอร์มพร้อมกันได้อย่างไร?

ใช้งาน OAuth สำหรับแต่ละแพลตฟอร์ม และสร้างฟังก์ชันการโพสต์แบบรวมศูนย์ที่เรียกใช้ API ของแต่ละแพลตฟอร์มพร้อมกัน

ข้อจำกัดอัตราการเรียกใช้งานสำหรับ API โซเชียลมีเดียคืออะไร?

ข้อจำกัดแตกต่างกันไป: Facebook (200/ชั่วโมง), Twitter (300/15 นาที), LinkedIn (100-500/วัน), Instagram (200/ชั่วโมง)

ฉันจะตั้งเวลาโพสต์ได้อย่างไร?

จัดเก็บโพสต์ไว้ในฐานข้อมูลพร้อม scheduled_time จากนั้นใช้คิวงาน (เช่น Bull, Agenda) เพื่อเผยแพร่ตามเวลาที่กำหนด

ฉันสามารถรับข้อมูลวิเคราะห์จากทุกแพลตฟอร์มได้หรือไม่?

ได้ แต่ละแพลตฟอร์มมี API สำหรับข้อมูลวิเคราะห์ รวบรวมข้อมูลเพื่อจัดทำรายงานข้ามแพลตฟอร์ม

Top comments (0)