mcpField
S
social-media-api-best-practices

social-media-api-best-practices

github.com/zernio-dev开发369 次安装13 周前更新
代码仓库

关于 social-media-api-best-practices

面向开发社交媒体发布集成的参考技能,涵盖指南所列 API 的平台认证、速率限制、媒体上传与错误处理。

主要功能

  1. 01平台专属 OAuth 与令牌处理
  2. 02速率限制策略与重试模式
  3. 03媒体大小和格式要求
  4. 04分块及可恢复的视频上传
  5. 05平台错误处理与特殊规则

适用场景

  1. 01实现社交 API 认证
  2. 02按速率限制规划发布队列
  3. 03可靠上传大型视频文件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zernio-dev/social-media-api-best-practices --skill social-media-api-best-practices

安装后在 Claude Code 中输入/social-media-api-best-practices确认已加载。

附带文件README.md

详细介绍

社交媒体 API 最佳实践

经过实战验证的模式,源自跨 13 个平台安排 100 多万个帖子。


1. 身份验证模式

OAuth 2.0 平台

Instagram(Meta Graph API)

  • 两步令牌交换:短期(1 小时)→ 长期(60 天)
  • 使用 auth_type: 'rerequest' 强制重新请求权限
  • 交换端点:ig_exchange_token 授权类型
  • 权限范围:instagram_business_basic、instagram_business_content_publish、instagram_business_manage_messages、instagram_business_manage_comments、instagram_business_manage_insights

Twitter/X(必须使用 PKCE)

  • 必须使用 S256 代码质询的 PKCE
  • 将代码验证器嵌入 state:${state}-cv_${codeVerifier}
  • 权限范围:tweet.read、tweet.write、users.read、offline.access、media.write、dm.read、dm.write
  • 访问令牌:2 小时;刷新令牌:长期有效
function generateCodeVerifier(): string {
  return crypto.randomBytes(32).toString('base64url');
}

function generateCodeChallenge(verifier: string): string {
  return crypto.createHash('sha256').update(verifier).digest('base64url');
}

function extractCodeVerifierFromState(state: string): string {
  const match = state.match(/-cv_(.+)$/);
  return match ? match[1] : '';
}

TikTok

  • API 审核通过要求完全符合 UX 规范
  • 必须显示:privacy_level 选择器、评论/合拍/拼接开关
  • 必须披露商业内容
  • 权限范围:user.info.basic、user.info.profile、user.info.stats、video.publish、video.upload、video.list

LinkedIn

  • 始终包含标头:X-RestLi-Protocol-Version: 2.0.0
  • API 版本标头:LinkedIn-Version: 202511(部分端点使用 202505 或 202401)
  • 访问令牌:60 天;刷新令牌:365 天
  • 支持个人(urn:li:person:)和组织(urn:li:organization:)帖子
  • 个人权限范围:openid、profile、r_basicprofile、email、w_member_social、w_member_social_feed、r_member_postAnalytics、r_member_profileAnalytics、r_1st_connections_size
  • 组织权限范围:w_organization_social、w_organization_social_feed、r_organization_admin、r_organization_social、r_organization_social_feed、r_organization_followers

YouTube

  • 刷新令牌需要 access_type=offline 和 prompt=consent
  • 支持用户频道和品牌账号
  • 权限范围:youtube.upload、youtube、youtube.force-ssl、yt-analytics.readonly

Facebook

  • 公共主页访问令牌与用户令牌分开
  • 通过 /me/accounts 将用户令牌换为公共主页令牌
  • 支持通过 scheduled_publish_time 安排发布
  • 权限范围:pages_manage_posts、pages_show_list、pages_read_engagement、pages_manage_engagement、pages_read_user_content、business_management、pages_messaging

Threads

  • 与 Instagram(Meta)类似,采用两步令牌交换
  • 使用 th_exchange_token 授权类型
  • 权限范围:threads_basic、threads_content_publish、threads_manage_replies、threads_read_replies、threads_manage_insights、threads_delete

Pinterest

  • 使用带刷新令牌的 OAuth 2.0
  • 权限范围:pins:read、pins:write、boards:read、boards:write、user_accounts:read

Google Business Profile

  • Google OAuth 权限范围:business.manage、userinfo.profile、userinfo.email
  • 使用 My Business v4 API

Reddit

  • 使用 duration=permanent 获取长期令牌
  • 严格的 user-agent 要求——Reddit 强制要求使用描述性 user-agent
  • 权限范围:identity、submit、read、mysubreddits、privatemessages、history、edit、vote
const headers = {
  'User-Agent': 'YourApp/1.0 by /u/YourUsername'
};
非 OAuth 平台

Bluesky(AT Protocol)

  • 不使用传统 OAuth——使用 DID(去中心化标识符)
  • 通过 AT Protocol 获取的 accessJwt + refreshJwt 进行身份验证
  • 需要解析 PDS(个人数据服务器)
  • 遇到 ExpiredToken 错误时自动刷新
// Bluesky authentication
const session = await agent.login({
  identifier: 'user.bsky.social',
  password: 'app-password'
});
// session contains: accessJwt, refreshJwt, did, handle

Snapchat

  • 基本身份验证,仅限白名单(需要 Snapchat 开发团队批准)
  • 公共 API 有限,侧重个人资料 API
  • 权限范围:snapchat-profile-api

Telegram

  • 仅支持机器人令牌——无 OAuth 流程
  • 令牌格式:BOT_ID:SECRET
  • 不进行用户身份验证,机器人必须添加到聊天或频道

2. 速率限制策略

平台特定限制
平台主要限制策略
Instagram每个账号每天 100 条帖子排队并分散发布
TikTok每 24 小时最多 5 个待处理上传等待处理完成
Twitter三级限制(应用 + 用户 + 端点)检查这三级限制
LinkedIn一般限制宽松,批量操作限制严格谨慎分批处理
YouTube每个频道有每日上传配额监控配额
Reddit每分钟 60 个请求遵守响应头指示
Threads限流严格快速失败
Twitter 的三级速率限制

Twitter 有三级限制,请全部检查:

  1. 应用级 24 小时限制(限制最严格)
  2. 用户级 24 小时限制
  3. 端点特定速率限制
function parseRateLimitHeaders(headers: Headers) {
  return {
    // Endpoint limits
    remaining: parseInt(headers.get('x-rate-limit-remaining') || '0'),
    reset: parseInt(headers.get('x-rate-limit-reset') || '0'),
    // App-level 24h limits
    appRemaining: parseInt(headers.get('x-app-limit-24hour-remaining') || '0'),
    appReset: parseInt(headers.get('x-app-limit-24hour-reset') || '0'),
    // User-level 24h limits
    userRemaining: parseInt(headers.get('x-user-limit-24hour-remaining') || '0'),
    userReset: parseInt(headers.get('x-user-limit-24hour-reset') || '0'),
  };
}
指数退避模式
async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3,
  baseDelay = 5000
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (!isRetryableError(error) || attempt === maxRetries) throw error;
      const delay = Math.min(baseDelay * Math.pow(2, attempt), 30000);
      await sleep(delay);
    }
  }
  throw new Error('Max retries exceeded');
}

function isRetryableError(error: any): boolean {
  const status = error.status || error.statusCode;
  return [429, 500, 502, 503].includes(status);
}

3. 媒体要求

完整的平台限制
平台图片最大尺寸视频最大尺寸宽高比特殊要求
Instagram8MB快拍 100MB,Reels 300MB信息流 4:5 至 1.91:1,快拍 9:16轮播最多 10 项
TikTok20MB4GB,3s-10min严格要求 9:16照片轮播最多 35 项
Twitter5MB512MB,2min 20s灵活1-4 张图片
LinkedIn8MB5GB灵活图片轮播最多 20 张
YouTube缩略图 2MB256GB首选 16:9支持可恢复上传
Facebook10MB4GB灵活多图最多 10 张
Threads8MB1GB,5min竖屏 9:16轮播最多 10 项
Pinterest32MB2GB灵活需要封面图片
Bluesky1MB50MB,3min灵活AT Protocol
Snapchat20MB500MB9:16AES 加密
Google Business5MBN/A灵活仅限图片
Reddit20MBN/A灵活通过 URL
Telegram10MB50MB灵活限制 4096 个字符
黄金法则:流式传输大型文件

切勿将整个文件加载到内存中:

// BAD - loads entire file into memory
const buffer = await fetch(url).then(r => r.arrayBuffer());

// GOOD - streams directly
const response = await fetch(url);
await uploadToPlatform(response.body); // Pass the stream
有问题的媒体来源

以下主机返回 HTML 或超时,而不是直接提供媒体:

const PROBLEMATIC_HOSTS = [
  'drive.google.com',
  'docs.google.com',
  'dropbox.com',
  'onedrive.live.com',
  '1drv.ms'
];

// Solution: Re-host to your own storage first
if (PROBLEMATIC_HOSTS.some(h => url.includes(h))) {
  url = await reHostToStorage(url);
}
Dropbox URL 修复
function fixDropboxUrl(url: string): string {
  if (url.includes('dropbox.com') && url.includes('dl=0')) {
    return url.replace('dl=0', 'dl=1');
  }
  return url;
}

4. 视频上传模式

分块上传(Twitter)
async function chunkedUpload(videoBuffer: Buffer, mediaType: string) {
  const CHUNK_SIZE = 4 * 1024 * 1024; // 4MB chunks

  // INIT
  const initRes = await api.post('/media/upload', {
    command: 'INIT',
    total_bytes: videoBuffer.length,
    media_type: mediaType
  });
  const mediaId = initRes.media_id_string;

  // APPEND chunks
  for (let i = 0; i < Math.ceil(videoBuffer.length / CHUNK_SIZE); i++) {
    const chunk = videoBuffer.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE);
    await api.post('/media/upload', {
      command: 'APPEND',
      media_id: mediaId,
      media: chunk.toString('base64'),
      segment_index: i
    });
  }

  // FINALIZE
  await api.post('/media/upload', {
    command: 'FINALIZE',
    media_id: mediaId
  });

  // Poll for processing
  await pollProcessingStatus(mediaId);

  return mediaId;
}
可恢复上传(YouTube)

YouTube 支持可恢复上传,以确保大文件上传可靠。使用 youtube-chunked-upload 模块,或实现可恢复上传协议。

轮询处理状态
async function pollProcessingStatus(mediaId: string, maxWaitMs = 300000) {
  const startTime = Date.now();

  while (Date.now() - startTime < maxWaitMs) {
    const status = await api.get(`/media/upload?command=STATUS&media_id=${mediaId}`);

    if (status.processing_info?.state === 'succeeded') return;
    if (status.processing_info?.state === 'failed') {
      throw new Error(status.processing_info.error?.message || 'Processing failed');
    }

    const checkAfter = (status.processing_info?.check_after_secs || 5) * 1000;
    await sleep(checkAfter);
  }

  throw new Error('Processing timeout');
}

5. 错误处理

错误分类
type ErrorType = 'refresh-token' | 'retry' | 'user-error';

function categorizeError(platform: string, error: any): ErrorType {
  if (isTokenError(error)) return 'refresh-token';
  if (isTemporaryError(error)) return 'retry';
  return 'user-error';
}
Instagram 错误代码
代码含义操作
2207001检测到垃圾信息用户错误
2207003媒体下载超时重试
2207004图片过大(>8MB)压缩
2207006未找到媒体用户错误
2207026不支持的视频格式重新编码
2207042超出每天 100 条帖子的限制等待 24 小时
2207050用户受限用户错误
2207051已拦截(但可能已经发布!)验证
2207052媒体获取失败使用直接 URL
Instagram 2207051 特殊情况

Instagram 的反垃圾信息机制有时会返回“已拦截”,但实际上已经发布:

async function handleInstagram2207051(accountId: string) {
  await sleep(5000);

  const recentMedia = await getRecentMedia(accountId);
  const justPosted = recentMedia.find(m =>
    Date.now() - new Date(m.timestamp).getTime() < 60000
  );

  if (justPosted) {
    return { success: true, postId: justPosted.id };
  }
  throw new Error('Post actually failed');
}
TikTok 错误代码
代码含义操作
access_token_invalid令牌已过期刷新
scope_not_authorized缺少权限重新连接
rate_limit_exceeded请求过多重试
spam_risk_*内容被标记用户错误
file_format_check_failed格式无效用户错误
unaudited_client_can_only_post_to_private开发模式仅限私密发布
Twitter/X 错误代码
代码含义操作
invalid_grant令牌已撤销重新连接
usage-capped受到速率限制重试
duplicate相同内容已存在用户错误
186推文过长用户错误
Bluesky 错误代码
代码含义操作
ExpiredTokenJWT 已过期自动刷新
InvalidToken令牌无效重新连接
XRPCNotSupported应用密码不支持私信使用完整身份验证
Reddit 错误代码
代码含义操作
invalid token已过期刷新
not allowed to submitSubreddit 限制用户错误
RATELIMIT操作过快延迟后重试
NO_LINKS不允许链接用户错误
Telegram 错误代码
代码含义操作
message too long超过 4096 个字符截断
chat not found聊天 ID 无效用户错误
bot was blocked用户已屏蔽机器人用户错误

6. 平台特性

JavaScript 大整数 ID(Instagram/Facebook)

Instagram ID 超过 MAX_SAFE_INTEGER(17 位以上):

function safeJsonParse(text: string) {
  // Wrap large integers in quotes before parsing
  const safe = text.replace(/"id"\s*:\s*(\d{15,})/g, '"id":"$1"');
  return JSON.parse(safe);
}
Twitter 字符计数
function countTwitterCharacters(text: string): number {
  let count = 0;

  // URLs always count as 23 characters
  const urlRegex = /https?:\/\/[^\s]+/g;
  const urls = text.match(urlRegex) || [];
  const textWithoutUrls = text.replace(urlRegex, '');

  count += urls.length * 23;

  // Emojis and CJK count as 2
  for (const char of textWithoutUrls) {
    count += char.match(/[\u{1F600}-\u{1F64F}]|[\u4e00-\u9fff]/u) ? 2 : 1;
  }

  return count;
}
Twitter 各等级的字符限制
等级限制
Free280
Premium4,000
Premium+25,000
LinkedIn 文本转义

保留字符:| { } [ ] ( ) < > # \ * _ ~

注意:有意不转义 @,以避免帖子中显示 \@。URN 提及 @[Name](urn:li:person:ID) 和话题标签 #tag 会自动保留。

function escapeLinkedInText(text: string): string {
  // Reserved chars (excluding @ to preserve readability)
  const reserved = /[\|\{\}\[\]\(\)\<\>\#\\\*\_\~]/;

  let output = '';
  let i = 0;

  while (i < text.length) {
    // Preserve URN mentions: @[Display Name](urn:li:person:ID)
    if (text[i] === '@' && text[i + 1] === '[') {
      const closeBracket = text.indexOf(']', i + 2);
      if (closeBracket !== -1 && text.substring(closeBracket + 1, closeBracket + 9) === '(urn:li:') {
        const closeParen = text.indexOf(')', closeBracket + 9);
        if (closeParen !== -1) {
          output += text.substring(i, closeParen + 1);
          i = closeParen + 1;
          continue;
        }
      }
    }

    // Preserve hashtags: #word
    if (text[i] === '#' && /[a-zA-Z0-9_]/.test(text[i + 1] || '')) {
      output += text[i++];
      while (i < text.length && /[a-zA-Z0-9_]/.test(text[i])) {
        output += text[i++];
      }
      continue;
    }

    // Escape reserved characters
    output += reserved.test(text[i]) ? '\\' + text[i] : text[i];
    i++;
  }

  return output;
}
TikTok 用户体验合规要求

TikTok 要求全面符合用户体验规范,才能通过 API 审核:

  • 用户必须手动选择隐私级别(不设默认值)
  • 必须显示评论、合拍和拼接开关
  • 商业内容披露须经用户确认
  • 上传前预览内容
  • 声明明确同意
Reddit 用户代理要求

Reddit 严格要求使用描述性用户代理:

// BAD - will be blocked
headers['User-Agent'] = 'axios/1.0';

// GOOD - descriptive format
headers['User-Agent'] = 'MyApp/1.0 (by /u/developer_username)';
Telegram HTML 子集

仅支持以下 HTML 标签:

<b>, <strong>       
<i>, <em>           
<u>                 
<s>, <strike>, <del>
<code>              
<pre>               
<a href="...">      
<tg-spoiler>        
<blockquote>        

7. 特殊协议

AT 协议(Bluesky)

Bluesky 使用 AT Protocol,而非 OAuth:

关键概念:

  • DID(去中心化标识符):用户的永久 ID(例如,did:plc:abc123)
  • PDS(个人数据服务器):存储用户数据的位置
  • Handle:便于人阅读的名称(例如,user.bsky.social)

富文本 Facet:

提及和链接使用字节偏移量定位:

function createFacets(text: string): Facet[] {
  const facets: Facet[] = [];
  const encoder = new TextEncoder();

  // Find mentions
  const mentionRegex = /@([a-zA-Z0-9.-]+)/g;
  let match;

  while ((match = mentionRegex.exec(text)) !== null) {
    const beforeText = text.slice(0, match.index);
    const byteStart = encoder.encode(beforeText).length;
    const byteEnd = byteStart + encoder.encode(match[0]).length;

    facets.push({
      index: { byteStart, byteEnd },
      features: [{
        $type: 'app.bsky.richtext.facet#mention',
        did: await resolveDid(match[1])
      }]
    });
  }

  return facets;
}
Snapchat AES-256-CBC 加密

Snapchat 要求在上传前加密媒体:

import crypto from 'crypto';

function encryptForSnapchat(buffer: Buffer): {
  encrypted: Buffer;
  key: string;
  iv: string;
} {
  const key = crypto.randomBytes(32); // 256 bits
  const iv = crypto.randomBytes(16);  // 128 bits

  const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
  const encrypted = Buffer.concat([
    cipher.update(buffer),
    cipher.final()
  ]);

  return {
    encrypted,
    key: key.toString('base64'),
    iv: iv.toString('base64')
  };
}

8. 快速调试清单

帖子发布失败时:

  1. 检查令牌是否有效 - 访问令牌是否已过期?
  2. 检查速率限制 - 是否达到每日/每小时限制?
  3. 检查媒体 URL - 平台能否直接获取媒体?
  4. 检查媒体规格 - 格式、大小、尺寸和宽高比是否正确?
  5. 检查 2207051 情况 - 是否在报错后仍然发布成功?
  6. 检查账户状态 - 账户是否受到限制?
  7. 检查 subreddit 规则 -(Reddit)是否允许链接?是否需要 Flair?
  8. 检查用户代理 -(Reddit)是否足够具有描述性?
  9. 检查机器人的权限 -(Telegram)机器人是否已加入聊天并拥有发送权限?

9. 推荐架构

your-app/
├── lib/
│   ├── platforms/
│   │   ├── base.ts           # Abstract base class
│   │   ├── instagram.ts
│   │   ├── tiktok.ts
│   │   ├── twitter.ts
│   │   ├── bluesky.ts        # AT Protocol
│   │   ├── snapchat.ts       # With encryption
│   │   └── ...
│   ├── utils/
│   │   ├── rate-limiter.ts
│   │   ├── media-handler.ts
│   │   ├── error-mapper.ts
│   │   └── encryption.ts     # For Snapchat
│   └── queue/
│       └── scheduler.ts

10. 平台对比矩阵

功能InstagramTikTokTwitterLinkedInYouTubeFacebookThreadsPinterestBlueskySnapchatGBPRedditTelegram
OAuth两步标准PKCE标准Google标准两步基础AT Proto基础Google标准Bot Token
令牌有效期60d2y+2h+refresh365d refresh1y60d+60dN/A自动N/A1y永久N/A
视频是是是是是是是是是是否否是
轮播1035420无1010无4无无图库相册 10
定时发布API草稿否APIAPIAPIAPI否API否否否否
私信是否是是否是否否是是否是是
分析是是是是是是是是否否有限否有限

关于本指南

本指南由 Late 维护,内容基于跨全部 13 个平台安排 超过 100 万篇帖子 时总结出的真实模式编写。

这里记录的每个错误代码、边缘情况和特性都源自生产环境经验。Instagram 的 2207051 陷阱?我们经过数小时的调试才发现它。Snapchat 的加密要求?我们也是吃了苦头才学到的。

正在构建社交媒体应用? 如果你不想自己实现和维护所有这些集成,Late 的 API 可以为你处理复杂事项:通过一次集成,即可完成 OAuth、媒体处理、速率限制、错误处理,以及跨全部 13 个平台的内容安排。


由 Late 维护 - 面向开发者的社交媒体内容安排 API

评论

支持 Markdown