發佈 API

發佈 API(POST /v2/posts)是 Boring 的核心端點,可將內容發佈到 Facebook、Instagram、Threads、X(Twitter)與 YouTube。

Endpoint

POST https://boring.aiagent-me.com/v2/posts

驗證

請求標頭必須附上:

boring-api-key: boring_xxxxxxxxxxxxx

取得金鑰的方式請參考 API 金鑰

請求格式

Headers

POST /v2/posts HTTP/1.1
Host: boring.aiagent-me.com
Content-Type: application/json
boring-api-key: boring_xxxxxxxxxxxxx

Request Body

{
  "post": {
    "accountId": "uuid-string",
    "content": {
      "text": "Post content" | ["Thread post 1", "Thread post 2"],
      "mediaUrls": ["https://example.com/image.jpg"] | "https://example.com/image.jpg",
      "platform": "facebook" | "instagram" | "threads" | "x" | "youtube"
    },
    "target": {
      "targetType": "facebook" | "instagram" | "threads" | "x" | "youtube"
    }
  }
}

參數說明

欄位 型態 必填 說明
post Object 貼文物件
post.accountId String (UUID) 控制台顯示的帳號 ID
post.content Object 內容物件
post.content.text String 或 Array 視平台而定 貼文文字/標題。Instagram、Facebook 必填。Threads 若為串文可使用字串陣列。
post.content.mediaUrls Array 或 String 視平台而定 媒體 URL。Instagram 必填。可為單一字串或陣列。
post.content.platform String 目標平台:facebookinstagramthreadsxyoutube
post.target Object 目標設定
post.target.targetType String platform 相同

各平台行為

Facebook

支援類型

媒體數量 類型 範例
0 純文字 mediaUrls: []
1 張 單張照片 mediaUrls: ["image.jpg"]
2-10 張 相簿 mediaUrls: ["img1.jpg", "img2.jpg", ...]
1 支 影片 mediaUrls: ["video.mp4"]

範例

{
  "post": {
    "accountId": "fb-page-account-id",
    "content": {
      "text": "Check out our new product line!",
      "mediaUrls": [
        "https://cdn.example.com/product1.jpg",
        "https://cdn.example.com/product2.jpg",
        "https://cdn.example.com/product3.jpg"
      ],
      "platform": "facebook"
    },
    "target": {
      "targetType": "facebook"
    }
  }
}

Instagram

支援類型

媒體數量 類型 範例
1 張 單張照片 mediaUrls: ["image.jpg"]
2-10 張 輪播 mediaUrls: ["img1.jpg", ...]
1 支 Reels mediaUrls: ["video.mp4"]

注意:Instagram 一定要有媒體,不支援純文字。

範例

{
  "post": {
    "accountId": "instagram-account-id",
    "content": {
      "text": "New collection is here! Swipe to see all colors 🎨\n\n#fashion #style #newcollection",
      "mediaUrls": [
        "https://cdn.example.com/color1.jpg",
        "https://cdn.example.com/color2.jpg",
        "https://cdn.example.com/color3.jpg"
      ],
      "platform": "instagram"
    },
    "target": {
      "targetType": "instagram"
    }
  }
}

Threads

支援類型

媒體數量 類型 說明
0 純文字 text 為字串即可
1 單張圖片 mediaUrls 包含 1 張
2-20 輪播 mediaUrls 包含多張圖片
1 影片 mediaUrls 為影片 URL
任意 長篇串文 text 使用字串陣列,每個元素一則貼文

串文範例

{
  "post": {
    "accountId": "threads-account-id",
    "content": {
      "text": [
        "🧵 Let me explain how our API works (thread)",
        "First, you sign in with Google and connect your social accounts.",
        "Then, you generate an API key from the Settings page.",
        "Finally, you make POST requests to /v2/posts with your content.",
        "It's that simple! Questions? Reply below 👇"
      ],
      "mediaUrls": [],
      "platform": "threads"
    },
    "target": {
      "targetType": "threads"
    }
  }
}

X(Twitter)

支援類型

媒體數量 類型 範例
0 純文字推文 text: "Tweet", mediaUrls: []
1-4 圖片推文 mediaUrls: ["img1.jpg", "img2.jpg"]
1 影片推文 mediaUrls: ["video.mp4"]

注意

純文字範例

{
  "post": {
    "accountId": "x-account-id",
    "content": {
      "text": "Hello from Boring API! 🚀 This is a text-only tweet. #API #Automation",
      "mediaUrls": [],
      "platform": "x"
    },
    "target": {
      "targetType": "x"
    }
  }
}

YouTube

將影片與中繼資料一併上傳。mediaUrls[0] 必須為影片;mediaUrls[1](可選)為自訂縮圖。標題與描述透過 text 欄位以 \n\n 分隔。詳細請參考 YouTube 發佈

回應格式

成功回應

{
  "success": true,
  "message": "Post published successfully",
  "data": {
    "post_submission_id": "uuid-string",
    "post_id": "platform-specific-id",
    "post_type": "text|photo|album|carousel|video|reels|thread",
    "platform": "facebook|instagram|threads|x|youtube",
    "page_name": "Account name or username",
    "published_at": "2025-01-20T10:30:00Z",

    "photo_count": 3,
    "item_count": 5,
    "thread_count": 7,
    "post_ids": ["id1", "id2"]
  }
}

欄位說明

欄位 說明
post_submission_id 此次提交的 UUID,可作為去重依據
post_id 平台生成的貼文 ID
post_type 貼文類型
platform 平台名稱
page_name 帳號名稱或使用者名稱
published_at ISO 8601 時間戳記
photo_count Facebook 相簿的照片數
item_count Instagram 輪播張數
thread_count Threads 串文貼文數
post_ids Threads 串文所有貼文 ID

錯誤回應

{
  "success": false,
  "error": "InvalidAccountId",
  "message": "Account not found or does not belong to this user"
}

完整錯誤代碼請見 錯誤代碼

媒體 URL 格式

支援格式

圖片:JPG、JPEG、PNG、WEBP(Threads)

影片:MP4、MOV

URL 要求

  1. 可公開存取:不得需要登入
  2. 直接指向檔案:非 HTML 頁面
  3. 使用 HTTPS
  4. 狀態碼 200:確保檔案存在
  5. 正確 Content-Type:如 image/jpegvideo/mp4

良好範例

https://cdn.example.com/images/photo.jpg
https://storage.googleapis.com/bucket/video.mp4
https://s3.amazonaws.com/bucket/image.png

不良範例

http://example.com/photo.jpg         (非 HTTPS)
https://example.com/photos/123       (非直接檔案)
https://example.com/photo.jpg?auth=token  (需驗證)
file:///local/path/photo.jpg         (本機路徑)

單一字串 vs. 陣列

API 同時接受以下格式:

"mediaUrls": "https://example.com/photo.jpg"
"mediaUrls": ["https://example.com/photo.jpg"]

建議統一使用陣列,方便處理多媒體。

程式範例

Python

import requests
import os

API_URL = "https://boring.aiagent-me.com/v2/posts"
API_KEY = os.environ["BORING_API_KEY"]

def publish_post(account_id, platform, text, media_urls=None):
    """發佈貼文"""
    media_urls = media_urls or []

    headers = {
        "boring-api-key": API_KEY,
        "Content-Type": "application/json"
    }

    data = {
        "post": {
            "accountId": account_id,
            "content": {
                "text": text,
                "mediaUrls": media_urls,
                "platform": platform
            },
            "target": {
                "targetType": platform
            }
        }
    }

    response = requests.post(API_URL, headers=headers, json=data)
    return response.json()

# 範例
result = publish_post(
    account_id="your-account-id",
    platform="instagram",
    text="Check out this amazing sunset! 🌅 #photography",
    media_urls=["https://example.com/sunset.jpg"]
)

if result["success"]:
    print(f"Published! Post ID: {result['data']['post_id']}")
else:
    print(f"Error: {result['message']}")

JavaScript(Node.js)

const axios = require('axios');

const API_URL = "https://boring.aiagent-me.com/v2/posts";
const API_KEY = process.env.BORING_API_KEY;

async function publishPost(accountId, platform, text, mediaUrls = []) {
  const headers = {
    "boring-api-key": API_KEY,
    "Content-Type": "application/json"
  };

  const data = {
    post: {
      accountId,
      content: {
        text,
        mediaUrls,
        platform
      },
      target: {
        targetType: platform
      }
    }
  };

  const response = await axios.post(API_URL, data, { headers });
  return response.data;
}

publishPost("your-account-id", "threads", "Just launched our new feature! 🚀")
  .then(result => console.log(result));

cURL

curl -X POST https://boring.aiagent-me.com/v2/posts \
  -H "boring-api-key: $BORING_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "post": {
      "accountId": "your-account-id",
      "content": {
        "text": "Hello from cURL!",
        "mediaUrls": ["https://example.com/image.jpg"],
        "platform": "facebook"
      },
      "target": {
        "targetType": "facebook"
      }
    }
  }'

進階用法

同步多平台發佈

def cross_platform_publish(accounts, text, media_urls):
    """一次發佈到多個平台"""
    results = {}

    for platform, account_id in accounts.items():
        adapted_text = adapt_content(text, platform)
        adapted_media = adapt_media(media_urls, platform)

        results[platform] = publish_post(
            account_id=account_id,
            platform=platform,
            text=adapted_text,
            media_urls=adapted_media
        )

    return results

指數回退重試

import time

def publish_with_retry(account_id, platform, text, media_urls, max_retries=3):
    for attempt in range(max_retries):
        result = publish_post(account_id, platform, text, media_urls)

        if result["success"]:
            return result

        if result.get("error") in ["RateLimitExceeded", "ServiceUnavailable"]:
            wait = 2 ** attempt
            time.sleep(wait)
            continue

        return result

    return {"success": False, "error": "MaxRetriesExceeded"}

批次發佈

def batch_publish(posts):
    results = []

    for i, post in enumerate(posts):
        results.append(publish_post(**post))

        if i < len(posts) - 1:
            time.sleep(2)  # 控制請求速率

    return results

非同步處理

部分貼文需要額外處理時間:

類型 處理時間
純文字 即時
單張照片 1-5 秒
輪播 5-15 秒
影片 30 秒 - 3 分鐘
Threads 串文 每貼文 10-30 秒

若請求逾時(影片預設 300 秒):

監控與日誌

取得發佈歷史

def get_recent_posts(limit=20):
    url = f"https://boring.aiagent-me.com/api/published-posts?limit={limit}"
    response = requests.get(url, cookies=session_cookies)
    return response.json()

注意:此端點需控制台 Session,無法使用 API Key。

紀錄請求

import logging

logger = logging.getLogger(__name__)

def publish_post_logged(account_id, platform, text, media_urls):
    logger.info(f"Publishing to {platform} (account: {account_id})")
    logger.debug(f"Text preview: {text[:100]}")
    logger.debug(f"Media URLs: {media_urls}")

    result = publish_post(account_id, platform, text, media_urls)

    if result["success"]:
        logger.info(f"Success! Post ID: {result['data']['post_id']}")
    else:
        logger.error(f"Failed: {result['message']}")

    return result

最佳實務

  1. 發佈前驗證:確認帳號 ID、媒體 URL
  2. 妥善處理錯誤:加入重試與異常處理
  3. 尊重速率限制:避免在短時間大量呼叫
  4. 測試媒體 URL:確保可公開存取
  5. 詳實紀錄日誌:保留成功與失敗資訊
  6. 使用環境變數:不要硬編 API Key
  7. 設定適當的 timeout:避免永遠等待
  8. 批次發佈時適度間隔:避免觸發平台限制

下一步