發佈 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 | 是 | 目標平台:facebook、instagram、threads、x、youtube |
post.target |
Object | 是 | 目標設定 |
post.target.targetType |
String | 是 | 與 platform 相同 |
各平台行為
支援類型:
| 媒體數量 | 類型 | 範例 |
|---|---|---|
| 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"
}
}
}
支援類型:
| 媒體數量 | 類型 | 範例 |
|---|---|---|
| 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"] |
注意:
- 所有文字會在 280 字之內自動截斷
- 每則最多 4 張圖片
- 圖片與影片不能混用
- 影片最大 512MB、最長 2 分 20 秒
純文字範例:
{
"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 要求
- 可公開存取:不得需要登入
- 直接指向檔案:非 HTML 頁面
- 使用 HTTPS
- 狀態碼 200:確保檔案存在
- 正確 Content-Type:如
image/jpeg、video/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 秒):
- 至發佈歷史確認是否成功
- 若 5 分鐘後仍無結果可重試
監控與日誌
取得發佈歷史
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
最佳實務
- 發佈前驗證:確認帳號 ID、媒體 URL
- 妥善處理錯誤:加入重試與異常處理
- 尊重速率限制:避免在短時間大量呼叫
- 測試媒體 URL:確保可公開存取
- 詳實紀錄日誌:保留成功與失敗資訊
- 使用環境變數:不要硬編 API Key
- 設定適當的 timeout:避免永遠等待
- 批次發佈時適度間隔:避免觸發平台限制