Skill: socialecho-net/social-media-autopilot-api (version 1.3.0)

--- name: socialecho-social-media-management-agent description: SocialEcho social media management API skill for querying team, accounts, articles, reports, upload URL, Reddit communities, Pinterest boards, and publishing posts using team API key. Use for integration checks and data pulls. --- # SocialEcho Social Media Management Agent Use this skill to call SocialEcho external APIs with a team API key. ## Prerequisites 1. Sign up / sign in at `https://app.socialecho.net/`. 2. Create a team. 3. In Team Management, create an API key. 4. Use explicit CLI options for auth/runtime (do not auto-read env vars): - `--api-key` (required) - `--base-url` (optional, default `https://api.socialecho.net`) - `--team-id` (optional; maps to `X-Team-Id` when set) - `--lang` (optional, default `zh_CN`) ## Setup ```bash cd socialecho-skills npm ci ``` Runtime requirement: Node.js `>=18` ## Commands 查询与报表（OpenAPI 约定为 **GET + JSON body**，脚本用 Node 原生 `http(s)` 发送）： ```bash ./team.js --api-key YOUR_KEY ./account.js --api-key YOUR_KEY --page 1 --type 1 ./article.js --api-key YOUR_KEY --page 1 --account-ids 41,42 ./report.js --api-key YOUR_KEY --start-date 2026-01-01 --end-date 2026-03-24 --time-type 1 --group day --account-ids 41,42 ``` 上传与发布相关： ```bash ./upload-url.js --api-key YOUR_KEY --content-type image/png # 或: --content_type video/mp4 ./reddit-communities.js --api-key YOUR_KEY --account-id 163751 ./pinterest-boards.js --api-key YOUR_KEY --account-id 163751 ./publish-article.js --api-key YOUR_KEY --payload ./publish-payload.example.json ``` `GET /v1/upload/url` 的 body **必须** 包含 `content_type`（与实际上传文件一致的 MIME）。**允许值**为：`image/jpeg`、`image/jpg`、`image/png`、`image/gif`、`image/webp`、`image/bmp`；`video/mp4`、`video/avi`、`video/mov`、`video/wmv`、`video/flv`、`video/webm`、`video/mkv`、`video/3gp`、`video/quicktime`（详见 `socialEchoApidocs_cn.md` 第 5.5 节；脚本内常量 `upload-url.js` 的 `CONTENT_TYPE_ENUM` 同步校验）。 `publish-article` 的请求体字段以仓库内 `openapi.json` / `openapi.yaml` 中 `POST /v1/publish/article` 为准；请准备完整 JSON 文件并通过 `--payload` 传入。 ## Platform publish limits (copy, media, formats) 各平台文案长度、媒体数量、格式与尺寸等**发布前校验规则**见同目录 Markdown（与帮助中心内容对齐，供集成与运营参考）： | File | Language | | --- | --- | | `platform-publish-limits_cn.md` | Chinese | | `platform-publish-limits_en.md` | English | **CLI：在终端打印全文到 stdout（便于管道保存或查阅）：** ```bash node ./platform-limits.js node ./platform-limits.js --lang en # 若已全局安装本包： # socialecho-platform-limits # socialecho-platform-limits --lang en ``` ## Notes - 成功判定：HTTP `200` 且响应 JSON 的 `code` 为 `200` 或 `0`（与当前对外接口约定一致）。 - 外部 API 限流：单 Key 建议不超过 **120 次/分钟**；循环调用请加节流与退避。 - 规范文件：仓库根目录同步的 `默认模块.openapi.json` 与 skill 内 `openapi.json` / `openapi.yaml` 内容一致（便于 Clawhub / GitHub 与 Agent 阅读）。

path: account.js
content: #!/usr/bin/env node
import { buildRequestOptions, callJsonGet, parseArgs, printAndExit } from "./client.js";

const args = parseArgs(process.argv);
const options = buildRequestOptions(args);

const page = Number(args.page ?? 1);
const type = Number(args.type ?? 1);

const result = await callJsonGet("/v1/account", { page, type }, options);
printAndExit(result);


path: article.js
content: #!/usr/bin/env node
import { buildRequestOptions, callJsonGet, parseArgs, parseCsvIdsToArray, printAndExit } from "./client.js";

const args = parseArgs(process.argv);
const options = buildRequestOptions(args);
const page = Number(args.page ?? 1);
const accountIds = parseCsvIdsToArray(args["account-ids"]);

const body = { page };
if (accountIds) body.account_ids = accountIds;

const result = await callJsonGet("/v1/article", body, options);

printAndExit(result);


path: client.js
content: #!/usr/bin/env node

import http from "node:http";
import https from "node:https";
import { URL } from "node:url";

export function parseArgs(argv) {
  const args = {};
  for (let i = 2; i < argv.length; i += 1) {
    const [REDACTED_SECRET];
    if (!token.startsWith("--")) continue;
    const key = token.slice(2);
    const next = argv[i + 1];
    if (!next || next.startsWith("--")) {
      args[key] = true;
      continue;
    }
    args[key] = next;
    i += 1;
  }
  return args;
}

export function getOption(args, name, required = true, fallback = "") {
  const v = args[name] ?? fallback;
  if (required && !v) {
    throw new Error(`Missing option: --${name}`);
  }
  return v;
}

export function buildRequestOptions(args) {
  return {
    [REDACTED_SECRET], "api-key"),
    baseUrl: getOption(args, "base-url", false, "https://api.socialecho.net").replace(/\/+$/, ""),
    teamId: getOption(args, "team-id", false, ""),
    lang: getOption(args, "lang", false, "zh_CN")
  };
}

export function buildHeaders(options) {
  const { apiKey, teamId, lang } = options;
  const headers = {
    Authorization: `Bearer ${apiKey}`,
    "X-Lang": lang
  };
  if (teamId) headers["X-Team-Id"] = String(teamId);
  return headers;
}

function requestJson(method, urlString, bodyObject, baseHeaders) {
  return new Promise((resolve, reject) => {
    const u = new URL(urlString);
    const isHttps = u.protocol === "https:";
    const lib = isHttps ? https : http;
    const bodyBuf = Buffer.from(JSON.stringify(bodyObject ?? {}), "utf8");
    const headers = {
      ...baseHeaders,
      "Content-Type": "application/json",
      "Content-Length": String(bodyBuf.length)
    };
    const opts = {
      hostname: u.hostname,
      port: u.port || (isHttps ? 443 : 80),
      path: `${u.pathname}${u.search}`,
      method,
      headers
    };
    const req = lib.request(opts, (res) => {
      const chunks = [];
      res.on("data", (c) => chunks.push(c));
      res.on("end", () => {
        const text = Buffer.concat(chunks).toString("utf8");
        let body;
        try {
          body = text ? JSON.parse(text) : {};
        } catch {
          body = { parse_error: true, raw: text };
        }
        resolve({ status: res.statusCode ?? 0, body });
      });
    });
    req.on("error", reject);
    if (method === "GET" || method === "POST") {
      req.write(bodyBuf);
    }
    req.end();
  });
}

export function isBusinessOk(body) {
  const c = body?.code;
  return c === 200 || c === 0;
}

/**
 * OpenAPI 导出为 GET + JSON body；浏览器 fetch 对 GET body 支持差，故用 node:http(s) 发送。
 */
export async function callJsonGet(path, body, options) {
  const { baseUrl } = options;
  const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
  const h = buildHeaders(options);
  const { status, body: respBody } = await requestJson("GET", url, body, h);
  const ok = status === 200 && isBusinessOk(respBody);
  return { ok, status, body: respBody, url };
}

export async function callJsonPost(path, body, options) {
  const { baseUrl } = options;
  const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
  const h = buildHeaders(options);
  const { status, body: respBody } = await requestJson("POST", url, body, h);
  const ok = status === 200 && isBusinessOk(respBody);
  return { ok, status, body: respBody, url };
}

/** @deprecated 与 callJsonGet 相同，保留旧名 */
export async function callApi(path, params = {}, options) {
  return callJsonGet(path, params, options);
}

export function parseCsvIds(raw) {
  if (!raw) return "";
  return String(raw)
    .split(",")
    .map((x) => x.trim())
    .filter(Boolean)
    .join(",");
}

export function parseCsvIdsToArray(raw) {
  if (!raw) return undefined;
  const arr = String(raw)
    .split(",")
    .map((x) => x.trim())
    .filter(Boolean)
    .map((x) => Number(x))
    .filter((n) => !Number.isNaN(n));
  return arr.length ? arr : undefined;
}

export function printAndExit(result) {
  console.log(JSON.stringify(result, null, 2));
  if (!result.ok) process.exit(1);
}


path: openapi.json
content: {
  "openapi": "3.0.1",
  "info": {
    "title": "SocialEcho 对外 API",
    "description": "对外 HTTP 接口模块。认证：HTTP Bearer（Team API Key）。成功判定：HTTP 200 且响应 JSON 的 code 为 0 或 200（以服务端为准）。单 Key 建议不超过 120 请求/分钟。",
    "version": "1.1.0"
  },
  "servers": [
    {
      "url": "https://api.socialecho.net",
      "description": "Production"
    }
  ],
  "tags": [],
  "paths": {
    "/v1/upload/url": {
      "get": {
        "summary": "获取OSS上传的URL",
        "deprecated": false,
        "description": "",
        "tags": [],
        "parameters": [
          {
            "name": "X-Lang",
            "in": "header",
            "description": "zh_CN/en",
            "example": "zh_CN",
            "schema": {
              "type": "string",
              "default": "zh_CN"
            }
          },
          {
            "name": "X-Team-Id",
            "in": "header",
            "description": "",
            "example": "{{TeamId}}",
            "schema": {
              "type": "integer",
              "default": "{{TeamId}}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {}
              },
              "examples": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/reddit/communities": {
      "get": {
        "summary": "获取Reddit的社区",
        "deprecated": false,
        "description": "",
        "tags": [],
        "parameters": [
          {
            "name": "X-Lang",
            "in": "header",
            "description": "zh_CN/en",
            "example": "zh_CN",
            "schema": {
              "type": "string",
              "default": "zh_CN"
            }
          },
          {
            "name": "X-Team-Id",
            "in": "header",
            "description": "",
            "example": "{{TeamId}}",
            "schema": {
              "type": "integer",
              "default": "{{TeamId}}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account_id": {
                    "type": "integer",
                    "title": "帐户ID"
                  }
                },
                "required": [
                  "account_id"
                ]
              },
              "examples": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/pinterest/boards": {
      "get": {
        "summary": "获取Pinterest的图版",
        "deprecated": false,
        "description": "",
        "tags": [],
        "parameters": [
          {
            "name": "X-Lang",
            "in": "header",
            "description": "zh_CN/en",
            "example": "zh_CN",
            "schema": {
              "type": "string",
              "default": "zh_CN"
            }
          },
          {
            "name": "X-Team-Id",
            "in": "header",
            "description": "",
            "example": "{{TeamId}}",
            "schema": {
              "type": "integer",
              "default": "{{TeamId}}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account_id": {
                    "type": "integer",
                    "title": "帐户ID"
                  }
                },
                "required": [
                  "account_id"
                ]
              },
              "examples": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/publish/article": {
      "post": {
        "summary": " 发布贴文",
        "deprecated": false,
        "description": "",
        "tags": [],
        "parameters": [
          {
            "name": "X-Lang",
            "in": "header",
            "description": "zh_CN/en",
            "example": "zh_CN",
            "schema": {
              "type": "string",
              "default": "zh_CN"
            }
          },
          {
            "name": "X-Team-Id",
            "in": "header",
            "description": "",
            "example": "{{TeamId}}",
            "schema": {
              "type": "integer",
              "default": "{{TeamId}}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "account_id": {
                    "type": "integer",
                    "title": "帐户ID"
                  },
                  "type": {
                    "type": "string",
                    "title": "发布类型",
                    "description": "Facebook(reels,post,stories) \nYouTube(shorts,video) \nInstagram(reels,post,stories)\nX(short_post,long_post) \nLinkedIn(post)\nTikTok(video,photo)\nPinterest(post)\nReddit(text,link,media)"
                  },
                  "status": {
                    "type": "integer",
                    "title": "状态",
                    "description": "0=草稿,1=发布"
                  },
                  "scheduled_at": {
                    "type": "string",
                    "title": "定时时间",
                    "description": "如果状态=1,定时时间可以填写，如果不填写，是立马发布"
                  },
                  "comment": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "title": "评论"
                  },
                  "content": {
                    "type": "string",
                    "title": "内容"
                  },
                  "extra": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string",
                        "description": "YouTube标题,Pinterest标题,Reddit标题"
                      },
                      "category_id": {
                        "type": "string",
                        "description": "Pinterest图版ID,Reddit社区ID"
                      },
                      "link": {
                        "type": "string",
                        "description": "Reddit链接地址,Pinterest链接地址"
                      },
                      "draft": {
                        "type": "boolean",
                        "description": "TikTok是否保存草稿草稿（true=是,false=否）"
                      },
                      "flair": {
                        "type": "object",
                        "properties": {
                          "uuid": {
                            "type": "string",
                            "description": "Reddit标识唯一ID"
                          },
                          "title": {
                            "type": "string",
                            "description": "Reddit标识标题"
                          }
                        },
                        "required": [
                          "uuid",
                          "title"
                        ],
                        "description": "Reddit标识"
                      }
                    },
                    "title": "扩展属性",
                    "description": "不同的平台有不同扩展",
                    "required": [
                      "title",
                      "link",
                      "category_id",
                      "draft",
                      "flair"
                    ]
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "url": {
                          "type": "string",
                          "title": "文件地址",
                          "description": "需要先上传到OSS后获取的URL地址"
                        }
                      },
                      "required": [
                        "url"
                      ]
                    },
                    "title": "附件"
                  }
                },
                "required": [
                  "account_id",
                  "type",
                  "status",
                  "comment",
                  "extra",
                  "attachments"
                ]
              },
              "examples": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/team": {
      "get": {
        "summary": "团队信息",
        "deprecated": false,
        "description": "",
        "tags": [],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {}
              },
              "examples": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "integer"
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "data"
                  ]
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account": {
      "get": {
        "summary": "社媒账号列表",
        "deprecated": false,
        "description": "",
        "tags": [],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "page": {
                    "type": "integer",
                    "title": "页码",
                    "description": "如果没有传，则默认获取所有数据"
                  },
                  "type": {
                    "type": "integer",
                    "title": "类型",
                    "description": "1=授权帐户,2=竞品帐户"
                  }
                }
              },
              "examples": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "integer"
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "data"
                  ]
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/article": {
      "get": {
        "summary": "贴文列表",
        "deprecated": false,
        "description": "",
        "tags": [],
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "page": {
                    "type": "integer",
                    "title": "页码",
                    "description": "如果没有传，则默认获取所有数据"
                  },
                  "account_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    },
                    "title": "帐户"
                  }
                }
              },
              "examples": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "integer"
                    },
                    "message": {
                      "type": "string"
                    },
                    "data": {
                      "type": "object",
                      "properties": {}
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "data"
                  ]
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/report": {
      "get": {
        "summary": "数据报告",
        "deprecated": false,
        "description": "",
        "tags": [],
        "parameters": [
          {
            "name": "X-Lang",
            "in": "header",
            "description": "zh_CN/en",
            "example": "zh_CN",
            "schema": {
              "type": "string",
              "default": "zh_CN"
            }
          },
          {
            "name": "X-Team-Id",
            "in": "header",
            "description": "",
            "example": "{{TeamId}}",
            "schema": {
              "type": "integer",
              "default": "{{TeamId}}"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "start_date": {
                    "type": "string",
                    "title": "开始日期"
                  },
                  "end_date": {
                    "type": "string",
                    "title": "结束日期"
                  },
                  "account_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    },
                    "title": "帐户ID"
                  },
                  "time_type": {
                    "type": "integer",
                    "title": "时间类型",
                    "description": "1=日期内新增贴文，2=历史全部贴文"
                  },
                  "group": {
                    "type": "string",
                    "title": "分组",
                    "description": "不传表示获取总量,day=每一天，app=应用, account=帐户"
                  }
                },
                "required": [
                  "start_date",
                  "end_date",
                  "time_type"
                ]
              },
              "examples": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {}
                }
              }
            },
            "headers": {}
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {},
    "responses": {},
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer"
      }
    }
  },
  "security": [
    {
      "bearer": []
    }
  ]
}

path: openapi.yaml
content: openapi: 3.0.1
info:
  title: SocialEcho 对外 API
  description: 对外 HTTP 接口模块。认证：HTTP Bearer（Team API Key）。成功判定：HTTP 200 且响应 JSON 的
    code 为 0 或 200（以服务端为准）。单 Key 建议不超过 120 请求/分钟。
  version: 1.1.0
servers:
- url: https://api.socialecho.net
  description: Production
tags: []
paths:
  /v1/upload/url:
    get:
      summary: 获取OSS上传的URL
      deprecated: false
      description: ''
      tags: []
      parameters:
      - name: X-Lang
        in: header
        description: zh_CN/en
        example: zh_CN
        schema:
          type: string
          default: zh_CN
      - name: X-Team-Id
        in: header
        description: ''
        example: '{{TeamId}}'
        schema:
          type: integer
          default: '{{TeamId}}'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties: {}
            examples: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties: {}
          headers: {}
      security:
      - bearer: []
  /v1/reddit/communities:
    get:
      summary: 获取Reddit的社区
      deprecated: false
      description: ''
      tags: []
      parameters:
      - name: X-Lang
        in: header
        description: zh_CN/en
        example: zh_CN
        schema:
          type: string
          default: zh_CN
      - name: X-Team-Id
        in: header
        description: ''
        example: '{{TeamId}}'
        schema:
          type: integer
          default: '{{TeamId}}'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                account_id:
                  type: integer
                  title: 帐户ID
              required:
              - account_id
            examples: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties: {}
          headers: {}
      security:
      - bearer: []
  /v1/pinterest/boards:
    get:
      summary: 获取Pinterest的图版
      deprecated: false
      description: ''
      tags: []
      parameters:
      - name: X-Lang
        in: header
        description: zh_CN/en
        example: zh_CN
        schema:
          type: string
          default: zh_CN
      - name: X-Team-Id
        in: header
        description: ''
        example: '{{TeamId}}'
        schema:
          type: integer
          default: '{{TeamId}}'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                account_id:
                  type: integer
                  title: 帐户ID
              required:
              - account_id
            examples: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties: {}
          headers: {}
      security:
      - bearer: []
  /v1/publish/article:
    post:
      summary: ' 发布贴文'
      deprecated: false
      description: ''
      tags: []
      parameters:
      - name: X-Lang
        in: header
        description: zh_CN/en
        example: zh_CN
        schema:
          type: string
          default: zh_CN
      - name: X-Team-Id
        in: header
        description: ''
        example: '{{TeamId}}'
        schema:
          type: integer
          default: '{{TeamId}}'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                account_id:
                  type: integer
                  title: 帐户ID
                type:
                  type: string
                  title: 发布类型
                  description: "Facebook(reels,post,stories) \nYouTube(shorts,video)\
                    \ \nInstagram(reels,post,stories)\nX(short_post,long_post) \n\
                    LinkedIn(post)\nTikTok(video,photo)\nPinterest(post)\nReddit(text,link,media)"
                status:
                  type: integer
                  title: 状态
                  description: 0=草稿,1=发布
                scheduled_at:
                  type: string
                  title: 定时时间
                  description: 如果状态=1,定时时间可以填写，如果不填写，是立马发布
                comment:
                  type: array
                  items:
                    type: string
                  title: 评论
                content:
                  type: string
                  title: 内容
                extra:
                  type: object
                  properties:
                    title:
                      type: string
                      description: YouTube标题,Pinterest标题,Reddit标题
                    category_id:
                      type: string
                      description: Pinterest图版ID,Reddit社区ID
                    link:
                      type: string
                      description: Reddit链接地址,Pinterest链接地址
                    draft:
                      type: boolean
                      description: TikTok是否保存草稿草稿（true=是,false=否）
                    flair:
                      type: object
                      properties:
                        uuid:
                          type: string
                          description: Reddit标识唯一ID
                        title:
                          type: string
                          description: Reddit标识标题
                      required:
                      - uuid
                      - title
                      description: Reddit标识
                  title: 扩展属性
                  description: 不同的平台有不同扩展
                  required:
                  - title
                  - link
                  - category_id
                  - draft
                  - flair
                attachments:
                  type: array
                  items:
                    type: object
                    properties:
                      url:
                        type: string
                        title: 文件地址
                        description: 需要先上传到OSS后获取的URL地址
                    required:
                    - url
                  title: 附件
              required:
              - account_id
              - type
              - status
              - comment
              - extra
              - attachments
            examples: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties: {}
          headers: {}
      security:
      - bearer: []
  /v1/team:
    get:
      summary: 团队信息
      deprecated: false
      description: ''
      tags: []
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties: {}
            examples: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                  message:
                    type: string
                  data:
                    type: object
                    properties: {}
                required:
                - code
                - message
                - data
          headers: {}
      security:
      - bearer: []
  /v1/account:
    get:
      summary: 社媒账号列表
      deprecated: false
      description: ''
      tags: []
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                page:
                  type: integer
                  title: 页码
                  description: 如果没有传，则默认获取所有数据
                type:
                  type: integer
                  title: 类型
                  description: 1=授权帐户,2=竞品帐户
            examples: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                  message:
                    type: string
                  data:
                    type: object
                    properties: {}
                required:
                - code
                - message
                - data
          headers: {}
      security:
      - bearer: []
  /v1/article:
    get:
      summary: 贴文列表
      deprecated: false
      description: ''
      tags: []
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                page:
                  type: integer
                  title: 页码
                  description: 如果没有传，则默认获取所有数据
                account_ids:
                  type: array
                  items:
                    type: integer
                  title: 帐户
            examples: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties:
                  code:
                    type: integer
                  message:
                    type: string
                  data:
                    type: object
                    properties: {}
                required:
                - code
                - message
                - data
          headers: {}
      security:
      - bearer: []
  /v1/report:
    get:
      summary: 数据报告
      deprecated: false
      description: ''
      tags: []
      parameters:
      - name: X-Lang
        in: header
        description: zh_CN/en
        example: zh_CN
        schema:
          type: string
          default: zh_CN
      - name: X-Team-Id
        in: header
        description: ''
        example: '{{TeamId}}'
        schema:
          type: integer
          default: '{{TeamId}}'
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                start_date:
                  type: string
                  title: 开始日期
                end_date:
                  type: string
                  title: 结束日期
                account_ids:
                  type: array
                  items:
                    type: integer
                  title: 帐户ID
                time_type:
                  type: integer
                  title: 时间类型
                  description: 1=日期内新增贴文，2=历史全部贴文
                group:
                  type: string
                  title: 分组
                  description: 不传表示获取总量,day=每一天，app=应用, account=帐户
              required:
              - start_date
              - end_date
              - time_type
            examples: {}
      responses:
        '200':
          description: ''
          content:
            application/json:
              schema:
                type: object
                properties: {}
          headers: {}
      security:
      - bearer: []
components:
  schemas: {}
  responses: {}
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
security:
- bearer: []


path: package.json
content: {
  "name": "socialecho-social-media-management-agent",
  "version": "1.3.0",
  "type": "module",
  "description": "CLI helpers for SocialEcho social media management external APIs",
  "author": "SocialEcho Team",
  "license": "MIT",
  "engines": {
    "node": ">=18"
  },
  "bin": {
    "socialecho-team": "./team.js",
    "socialecho-account": "./account.js",
    "socialecho-article": "./article.js",
    "socialecho-report": "./report.js",
    "socialecho-upload-url": "./upload-url.js",
    "socialecho-reddit-communities": "./reddit-communities.js",
    "socialecho-pinterest-boards": "./pinterest-boards.js",
    "socialecho-publish-article": "./publish-article.js",
    "socialecho-platform-limits": "./platform-limits.js"
  },
  "files": [
    "SKILL.md",
    "_meta.json",
    "openapi.yaml",
    "openapi.json",
    "client.js",
    "team.js",
    "account.js",
    "article.js",
    "report.js",
    "upload-url.js",
    "reddit-communities.js",
    "pinterest-boards.js",
    "publish-article.js",
    "platform-limits.js",
    "platform-publish-limits_cn.md",
    "platform-publish-limits_en.md",
    "publish-payload.example.json",
    ".env.example"
  ],
  "scripts": {
    "team": "node ./team.js",
    "account": "node ./account.js",
    "article": "node ./article.js",
    "report": "node ./report.js",
    "upload-url": "node ./upload-url.js",
    "reddit-communities": "node ./reddit-communities.js",
    "pinterest-boards": "node ./pinterest-boards.js",
    "publish-article": "node ./publish-article.js",
    "platform-limits": "node ./platform-limits.js",
    "platform-limits:en": "node ./platform-limits.js --lang en"
  },
  "dependencies": {}
}


path: pinterest-boards.js
content: #!/usr/bin/env node
import { buildRequestOptions, callJsonGet, getOption, parseArgs, printAndExit } from "./client.js";

const args = parseArgs(process.argv);
const options = buildRequestOptions(args);
const accountId = Number(getOption(args, "account-id"));
if (Number.isNaN(accountId)) {
  console.error("Usage: ./pinterest-boards.js --api-key <key> --account-id <int> [--base-url ...] [--team-id ...] [--lang ...]");
  process.exit(2);
}

const result = await callJsonGet("/v1/pinterest/boards", { account_id: accountId }, options);
printAndExit(result);


path: platform-limits.js
content: #!/usr/bin/env node
/**
 * Print SocialEcho platform publish limits (reference markdown) to stdout.
 * Usage: node platform-limits.js [--lang cn|en] [--help]
 */
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

function parseArgs(argv) {
  const args = {};
  for (let i = 2; i < argv.length; i += 1) {
    const [REDACTED_SECRET];
    if (!token.startsWith("--")) continue;
    const key = token.slice(2);
    const next = argv[i + 1];
    if (!next || next.startsWith("--")) {
      args[key] = true;
      continue;
    }
    args[key] = next;
    i += 1;
  }
  return args;
}

const args = parseArgs(process.argv);

if (args.help) {
  console.log(`Usage: node platform-limits.js [--lang cn|en]

Print platform publish limits (copy, media, formats) to stdout.

  --lang cn   Chinese (default)
  --lang en   English
`);
  process.exit(0);
}

const raw = String(args.lang ?? "cn").toLowerCase();
const lang = raw === "en" ? "en" : "cn";
const fileName =
  lang === "en" ? "platform-publish-limits_en.md" : "platform-publish-limits_cn.md";
const filePath = path.join(__dirname, fileName);

if (!fs.existsSync(filePath)) {
  console.error(`Missing bundled doc: ${fileName}`);
  process.exit(1);
}

process.stdout.write(fs.readFileSync(filePath, "utf8"));


path: platform-publish-limits_cn.md
content: # 各平台发布内容与格式限制（参考）

本文说明在 SocialEcho 中通过 **发布 API / 发布流程** 对接各社媒平台时，系统侧校验的**文案、媒体数量、格式与尺寸**等规则摘要，便于研发与运营提前准备素材。

> **提示**
>
> - 下列限制基于当前产品内嵌规则整理；**各平台官方政策可能随时调整**，若与平台后台或发布失败提示不一致，**以平台方及系统实时提示为准**。
> - 文中「竖版」指**建议或强制竖屏素材**（通常理解为宽度 < 高度），具体见各小节。

---

## 术语说明


| 用语      | 含义                                                  |
| ------- | --------------------------------------------------- |
| 内容长度    | 正文字符数上限（含空格等，以系统校验为准）                               |
| 标题长度    | 标题字符数限制；「不限」表示未单独设硬性上限（仍可能受平台约束）                    |
| 媒体数量    | 单次发布可包含的图片 / GIF / 视频数量及合计规则                        |
| 混发规则    | 多种媒体类型同时发布时是否允许及组合限制                                |
| 纯文案     | 是否允许不带任何图片/视频/GIF 的发布                               |
| 需要分类 ID | 是否必须选择图版、社区等平台侧「分类/版块」类标识（如 Pinterest 图版、Reddit 社区） |
| 必须竖版    | 是否为竖屏素材要求（宽 < 高）                                    |


---

## Facebook

### Regular（常规动态 / `post` 等）


| 项目      | 限制                               |
| ------- | -------------------------------- |
| 内容长度    | 最大 2200 字符                       |
| 标题长度    | 不限                               |
| 最少标题/内容 | 不限                               |
| 媒体数量    | 图片最多 10；GIF 最多 1；视频最多 1；总个数不限    |
| 混发规则    | 图片 + GIF **不允许**；图片 + 视频 **不允许** |
| 纯文案     | **允许**                           |
| 图片格式    | jpg / jpeg / png / gif           |
| 视频格式    | mp4 / mov                        |
| 图片大小    | 静态图 8MB；GIF 8MB                  |
| 视频大小    | 最大 1GB                           |
| 视频时长    | 1 秒～20 分钟                        |
| 视频分辨率   | 不限                               |
| 图片分辨率   | 不限                               |
| 帧率      | 23～60 fps                        |
| 视频/图片比例 | 不限                               |
| 评论长度    | 不限                               |
| 需要分类 ID | 否                                |
| 必须竖版    | 否                                |


### Reels


| 项目      | 限制                             |
| ------- | ------------------------------ |
| 内容长度    | 最大 2200 字符                     |
| 标题长度    | 不限                             |
| 最少标题/内容 | 不限                             |
| 媒体数量    | **仅视频 1**（图片 0、GIF 0，合计 1 个媒体） |
| 混发规则    | 图片 + GIF 不允许；图片 + 视频不允许        |
| 纯文案     | **不允许**（必须带视频）                 |
| 图片格式    | 不适用（仅视频）                       |
| 视频格式    | mp4 / mov                      |
| 视频大小    | 最大 1GB                         |
| 视频时长    | **3 秒～90 秒**                   |
| 视频分辨率   | 宽 ≥ 540，**高 ≥ 960**            |
| 帧率      | 23～60 fps                      |
| 视频/图片比例 | 不限                             |
| 评论长度    | 不限                             |
| 需要分类 ID | 否                              |
| 必须竖版    | **是**                          |


---

## X（原 Twitter）


| 项目      | 限制                                    |
| ------- | ------------------------------------- |
| 内容长度    | 最大 **280** 字符                         |
| 标题长度    | 不限                                    |
| 最少标题/内容 | 不限                                    |
| 媒体数量    | 图片最多 4；GIF 最多 1；视频最多 4；**合计最多 4 个媒体** |
| 混发规则    | 图片 + GIF **不允许**；图片 + 视频 **允许**       |
| 纯文案     | **允许**                                |
| 图片格式    | jpg / jpeg / png / gif                |
| 视频格式    | mp4 / mov                             |
| 图片大小    | 静态图 5MB；GIF 15MB                      |
| 视频大小    | 最大 512MB                              |
| 视频时长    | 0.5 秒～140 秒                           |
| 视频分辨率   | 宽 ≥ 32，高 ≥ 32                         |
| 图片分辨率   | 宽 4～8192，高 4～8192                     |
| 帧率      | 23～60 fps                             |
| 视频宽高比   | 0.333～3                               |
| 图片比例    | 不限                                    |
| 评论长度    | 不限                                    |
| 需要分类 ID | 否                                     |
| 必须竖版    | 否                                     |


---

## Instagram


| 项目      | 限制                         |
| ------- | -------------------------- |
| 内容长度    | 最大 2200 字符                 |
| 标题长度    | 不限                         |
| 最少标题/内容 | 不限                         |
| 媒体数量    | 图片最多 10；视频最多 1；GIF 0；总个数不限 |
| 混发规则    | 图片 + GIF 不允许；图片 + 视频不允许    |
| 纯文案     | **不允许**（需要媒体）              |
| 图片格式    | jpg / jpeg / png           |
| 视频格式    | mp4                        |
| 图片大小    | 静态图 8MB；GIF 按规则 8MB        |
| 视频大小    | 最大 300MB                   |
| 视频时长    | 3 秒～15 分钟                  |
| 视频分辨率   | 宽 1～1920，高不限               |
| 图片分辨率   | 宽 320～1440，高不限             |
| 帧率      | 23～60 fps                  |
| 视频宽高比   | 0.01～10                    |
| 图片宽高比   | 0.8～1.91                   |
| 评论长度    | 不限                         |
| 需要分类 ID | 否                          |
| 必须竖版    | 否                          |


---

## LinkedIn


| 项目      | 限制                                            |
| ------- | --------------------------------------------- |
| 内容长度    | 最大 3000 字符                                    |
| 标题长度    | 不限                                            |
| 最少标题/内容 | 不限                                            |
| 媒体数量    | 图片最多 20；GIF 最多 20；图片+GIF 合计最多 20；视频最多 1；总个数不限 |
| 混发规则    | 图片 + GIF **允许**；图片 + 视频 **不允许**               |
| 纯文案     | **允许**                                        |
| 图片格式    | jpg / png / gif                               |
| 视频格式    | mp4                                           |
| 图片大小    | 静态图 5MB；GIF 8MB                               |
| 视频大小    | 最大 500MB                                      |
| 视频时长    | 3 秒～10 分钟                                     |
| 视频分辨率   | 宽 1～4096，高 1～2304                             |
| 图片分辨率   | 不限                                            |
| 帧率      | 10～60 fps                                     |
| 视频宽高比   | 0.416～2.4                                     |
| 图片比例    | 不限                                            |
| 评论长度    | 不限                                            |
| 需要分类 ID | 否                                             |
| 必须竖版    | 否                                             |


---

## TikTok


| 项目      | 限制                         |
| ------- | -------------------------- |
| 内容长度    | 最大 4000 字符                 |
| 标题长度    | 不限                         |
| 最少标题/内容 | 不限                         |
| 媒体数量    | 视频最多 1；图片最多 35；GIF 0；总个数不限 |
| 混发规则    | 图片 + GIF 不允许；图片 + 视频不允许    |
| 纯文案     | **不允许**（需要媒体）              |
| 图片格式    | jpg / jpeg / webp          |
| 视频格式    | mp4 / mov                  |
| 图片大小    | 静态图 20MB；**不支持 GIF**       |
| 视频大小    | 最大 1GB                     |
| 视频时长    | 3 秒～10 分钟                  |
| 视频分辨率   | 宽 ≥ 360，**高 ≥ 360**        |
| 图片分辨率   | 不限                         |
| 帧率      | 23～60 fps                  |
| 视频/图片比例 | 不限                         |
| 评论长度    | 最大 **150** 字符              |
| 需要分类 ID | 否                          |
| 必须竖版    | 否                          |


---

## YouTube

### Regular（长视频 / `video`）


| 项目      | 限制                      |
| ------- | ----------------------- |
| 内容长度    | 最大 5000 字符              |
| 标题长度    | **1～100** 字符            |
| 最少内容    | **至少 1** 字符             |
| 媒体数量    | **仅视频 1**（图片 0、GIF 0）   |
| 混发规则    | 图片 + GIF 不允许；图片 + 视频不允许 |
| 纯文案     | **不允许**（必须带视频）          |
| 视频格式    | mp4 / mov               |
| 视频大小    | 最大 2GB                  |
| 视频时长    | 1 秒～**12 小时**           |
| 视频分辨率   | 不限                      |
| 帧率      | 不限                      |
| 视频/图片比例 | 不限                      |
| 评论长度    | 不限                      |
| 需要分类 ID | 否                       |
| 必须竖版    | 否                       |


### Shorts


| 项目      | 限制                  |
| ------- | ------------------- |
| 内容长度    | 最大 5000 字符          |
| 标题长度    | 1～100 字符            |
| 最少内容    | 至少 1 字符             |
| 媒体数量    | 仅视频 1               |
| 混发规则    | 同常规视频               |
| 纯文案     | 不允许（必须带视频）          |
| 视频格式    | mp4 / mov           |
| 视频大小    | 最大 2GB              |
| 视频时长    | **1 秒～60 秒**        |
| 视频分辨率   | 宽 ≥ 480，**高 ≥ 480** |
| 帧率      | 不限                  |
| 视频/图片比例 | 不限                  |
| 评论长度    | 不限                  |
| 需要分类 ID | 否                   |
| 必须竖版    | **是**               |


---

## Pinterest


| 项目      | 限制                                    |
| ------- | ------------------------------------- |
| 内容长度    | 最大 500 字符                             |
| 标题长度    | 最大 100 字符                             |
| 最少标题/内容 | 不限                                    |
| 媒体数量    | 图片最多 1；GIF 最多 1；视频最多 1；总个数不限（按类型互斥使用） |
| 混发规则    | 图片 + GIF 不允许；图片 + 视频不允许               |
| 纯文案     | **不允许**（需要媒体）                         |
| 图片格式    | jpg / jpeg / png                      |
| 视频格式    | mp4                                   |
| 图片大小    | 静态图 20MB；GIF 不限                       |
| 视频大小    | 最大 2GB                                |
| 视频时长    | 4 秒～15 分钟                             |
| 视频分辨率   | 不限                                    |
| 图片分辨率   | 宽 ≥ 200，**高 ≥ 300**                   |
| 帧率      | 不限                                    |
| 视频/图片比例 | 不限                                    |
| 评论长度    | 不限                                    |
| 需要分类 ID | **是**（需选择图版等）                         |
| 必须竖版    | 否                                     |


---

## 文档信息

- **来源**：内部规则文件《平台限制》整理。
- **用途**：帮助中心、对外集成说明、运营素材规范参考。
- **更新**：规则变更时请同步修订本文并与产品/发布链路校验一致。



path: platform-publish-limits_en.md
content: # Platform publish limits: copy, media, and formats (reference)

This article summarizes the **text, media counts, file formats, and dimensions** enforced by SocialEcho when you publish through the **Publish API / publish flows** to each social network. Use it to prepare assets in advance for engineering and operations.

> **Notes**
>
> - Limits below reflect the rules embedded in the current product. **Official platform policies may change at any time.** If anything disagrees with the platform console or an error message from a failed publish, **follow the platform and the live in-product message.**
> - **Portrait / vertical** means recommended or required **vertical assets** (typically **width less than height**). See each section for details.

---

## Glossary

| Term | Meaning |
| --- | --- |
| Body length | Maximum length of the main text (including spaces, per system validation). |
| Title length | Character limit for the title; “no fixed cap” means no separate hard limit in this table (platform rules may still apply). |
| Media counts | How many images / GIFs / videos a single publish may include, and combined rules. |
| Mixed media rules | Whether multiple media types may be combined, and which combinations are forbidden. |
| Text-only | Whether a publish **without** any image, video, or GIF is allowed. |
| Category / placement ID required | Whether you must pick a platform-side category or placement (e.g. Pinterest board, Reddit community). |
| Portrait required | Whether vertical (portrait) creative is required (**width &lt; height**). |

---

## Facebook

### Regular feed (`post`, etc.)

| Field | Limit |
| --- | --- |
| Body length | Max **2200** characters |
| Title length | No fixed cap in this table |
| Minimum title / body | No fixed minimum |
| Media counts | Images up to **10**; GIFs up to **1**; videos up to **1**; total attachments uncapped |
| Mixed media | Image + GIF **not** allowed; image + video **not** allowed |
| Text-only | **Allowed** |
| Image formats | jpg / jpeg / png / gif |
| Video formats | mp4 / mov |
| Image file size | Static **8MB**; GIF **8MB** |
| Video file size | Max **1GB** |
| Video duration | **1s–20 minutes** |
| Video resolution | No fixed limit |
| Image resolution | No fixed limit |
| Frame rate | **23–60** fps |
| Aspect ratio (video/image) | No fixed limit |
| Comment length | No fixed cap in this table |
| Category / placement ID | **No** |
| Portrait required | **No** |

### Reels

| Field | Limit |
| --- | --- |
| Body length | Max **2200** characters |
| Title length | No fixed cap in this table |
| Minimum title / body | No fixed minimum |
| Media counts | **Video only, 1 file** (0 images, 0 GIFs, **1** media item total) |
| Mixed media | Image + GIF not allowed; image + video not allowed |
| Text-only | **Not** allowed (video required) |
| Image formats | N/A (video only) |
| Video formats | mp4 / mov |
| Video file size | Max **1GB** |
| Video duration | **3s–90s** |
| Video resolution | Width **≥ 540**, height **≥ 960** |
| Frame rate | **23–60** fps |
| Aspect ratio | No fixed limit |
| Comment length | No fixed cap in this table |
| Category / placement ID | **No** |
| Portrait required | **Yes** |

---

## X (formerly Twitter)

| Field | Limit |
| --- | --- |
| Body length | Max **280** characters |
| Title length | No fixed cap in this table |
| Minimum title / body | No fixed minimum |
| Media counts | Images up to **4**; GIFs up to **1**; videos up to **4**; **max 4 media items total** |
| Mixed media | Image + GIF **not** allowed; image + video **allowed** |
| Text-only | **Allowed** |
| Image formats | jpg / jpeg / png / gif |
| Video formats | mp4 / mov |
| Image file size | Static **5MB**; GIF **15MB** |
| Video file size | Max **512MB** |
| Video duration | **0.5s–140s** |
| Video resolution | Width **≥ 32**, height **≥ 32** |
| Image resolution | Width **4–8192**, height **4–8192** |
| Frame rate | **23–60** fps |
| Video aspect ratio | **0.333–3** |
| Image aspect ratio | No fixed limit |
| Comment length | No fixed cap in this table |
| Category / placement ID | **No** |
| Portrait required | **No** |

---

## Instagram

| Field | Limit |
| --- | --- |
| Body length | Max **2200** characters |
| Title length | No fixed cap in this table |
| Minimum title / body | No fixed minimum |
| Media counts | Images up to **10**; videos up to **1**; GIFs **0**; total uncapped |
| Mixed media | Image + GIF not allowed; image + video not allowed |
| Text-only | **Not** allowed (media required) |
| Image formats | jpg / jpeg / png |
| Video formats | mp4 |
| Image file size | Static **8MB**; GIF **8MB** per rules |
| Video file size | Max **300MB** |
| Video duration | **3s–15 minutes** |
| Video resolution | Width **1–1920**, height unrestricted |
| Image resolution | Width **320–1440**, height unrestricted |
| Frame rate | **23–60** fps |
| Video aspect ratio | **0.01–10** |
| Image aspect ratio | **0.8–1.91** |
| Comment length | No fixed cap in this table |
| Category / placement ID | **No** |
| Portrait required | **No** |

---

## LinkedIn

| Field | Limit |
| --- | --- |
| Body length | Max **3000** characters |
| Title length | No fixed cap in this table |
| Minimum title / body | No fixed minimum |
| Media counts | Images up to **20**; GIFs up to **20**; images+GIFs combined up to **20**; videos up to **1**; total uncapped |
| Mixed media | Image + GIF **allowed**; image + video **not** allowed |
| Text-only | **Allowed** |
| Image formats | jpg / png / gif |
| Video formats | mp4 |
| Image file size | Static **5MB**; GIF **8MB** |
| Video file size | Max **500MB** |
| Video duration | **3s–10 minutes** |
| Video resolution | Width **1–4096**, height **1–2304** |
| Image resolution | No fixed limit |
| Frame rate | **10–60** fps |
| Video aspect ratio | **0.416–2.4** |
| Image aspect ratio | No fixed limit |
| Comment length | No fixed cap in this table |
| Category / placement ID | **No** |
| Portrait required | **No** |

---

## TikTok

| Field | Limit |
| --- | --- |
| Body length | Max **4000** characters |
| Title length | No fixed cap in this table |
| Minimum title / body | No fixed minimum |
| Media counts | Videos up to **1**; images up to **35**; GIFs **0**; total uncapped |
| Mixed media | Image + GIF not allowed; image + video not allowed |
| Text-only | **Not** allowed (media required) |
| Image formats | jpg / jpeg / webp |
| Video formats | mp4 / mov |
| Image file size | Static **20MB**; **GIF not supported** |
| Video file size | Max **1GB** |
| Video duration | **3s–10 minutes** |
| Video resolution | Width **≥ 360**, height **≥ 360** |
| Image resolution | No fixed limit |
| Frame rate | **23–60** fps |
| Aspect ratio (video/image) | No fixed limit |
| Comment length | Max **150** characters |
| Category / placement ID | **No** |
| Portrait required | **No** |

---

## YouTube

### Regular long-form video (`video`)

| Field | Limit |
| --- | --- |
| Body length | Max **5000** characters |
| Title length | **1–100** characters |
| Minimum body | **At least 1** character |
| Media counts | **Video only, 1 file** (0 images, 0 GIFs) |
| Mixed media | Image + GIF not allowed; image + video not allowed |
| Text-only | **Not** allowed (video required) |
| Video formats | mp4 / mov |
| Video file size | Max **2GB** |
| Video duration | **1s–12 hours** |
| Video resolution | No fixed limit |
| Frame rate | No fixed limit |
| Aspect ratio | No fixed limit |
| Comment length | No fixed cap in this table |
| Category / placement ID | **No** |
| Portrait required | **No** |

### Shorts

| Field | Limit |
| --- | --- |
| Body length | Max **5000** characters |
| Title length | **1–100** characters |
| Minimum body | **At least 1** character |
| Media counts | Video only, **1** file |
| Mixed media | Same as long-form |
| Text-only | **Not** allowed (video required) |
| Video formats | mp4 / mov |
| Video file size | Max **2GB** |
| Video duration | **1s–60s** |
| Video resolution | Width **≥ 480**, height **≥ 480** |
| Frame rate | No fixed limit |
| Aspect ratio | No fixed limit |
| Comment length | No fixed cap in this table |
| Category / placement ID | **No** |
| Portrait required | **Yes** |

---

## Pinterest

| Field | Limit |
| --- | --- |
| Body length | Max **500** characters |
| Title length | Max **100** characters |
| Minimum title / body | No fixed minimum |
| Media counts | Images up to **1**; GIFs up to **1**; videos up to **1**; use one type per publish (mutually exclusive) |
| Mixed media | Image + GIF not allowed; image + video not allowed |
| Text-only | **Not** allowed (media required) |
| Image formats | jpg / jpeg / png |
| Video formats | mp4 |
| Image file size | Static **20MB**; GIF no fixed cap in this table |
| Video file size | Max **2GB** |
| Video duration | **4s–15 minutes** |
| Video resolution | No fixed limit |
| Image resolution | Width **≥ 200**, height **≥ 300** |
| Frame rate | No fixed limit |
| Aspect ratio | No fixed limit |
| Comment length | No fixed cap in this table |
| Category / placement ID | **Yes** (e.g. board selection) |
| Portrait required | **No** |

---

## Document meta

- **Source**: Internal rules doc *Platform limits*, edited for help center use.
- **Use**: Help center, integration guides, and creative/production checklists.
- **Maintenance**: Update this file when product rules change, and re-validate against the live publish pipeline.


path: publish-article.js
content: #!/usr/bin/env node
import fs from "node:fs";

import { buildRequestOptions, callJsonPost, getOption, parseArgs, printAndExit } from "./client.js";

const args = parseArgs(process.argv);
const options = buildRequestOptions(args);
const payloadPath = getOption(args, "payload");

let body;
try {
  const raw = fs.readFileSync(payloadPath, "utf8");
  body = JSON.parse(raw);
} catch (e) {
  console.error(`Failed to read/parse --payload JSON file: ${payloadPath}`);
  console.error(e?.message || e);
  process.exit(2);
}

const result = await callJsonPost("/v1/publish/article", body, options);
printAndExit(result);


path: publish-payload.example.json
content: {
  "account_id": 163751,
  "type": "post",
  "status": 0,
  "scheduled_at": "",
  "comment": [],
  "content": "示例内容",
  "extra": {
    "title": "示例标题",
    "category_id": "",
    "link": "",
    "draft": false,
    "flair": {
      "uuid": "replace-with-reddit-flair-uuid",
      "title": "replace-with-reddit-flair-title"
    }
  },
  "attachments": [
    {
      "url": "https://example.com/uploaded-file.jpg"
    }
  ]
}


path: reddit-communities.js
content: #!/usr/bin/env node
import { buildRequestOptions, callJsonGet, getOption, parseArgs, printAndExit } from "./client.js";

const args = parseArgs(process.argv);
const options = buildRequestOptions(args);
const accountId = Number(getOption(args, "account-id"));
if (Number.isNaN(accountId)) {
  console.error("Usage: ./reddit-communities.js --api-key <key> --account-id <int> [--base-url ...] [--team-id ...] [--lang ...]");
  process.exit(2);
}

const result = await callJsonGet("/v1/reddit/communities", { account_id: accountId }, options);
printAndExit(result);


path: report.js
content: #!/usr/bin/env node
import { buildRequestOptions, callJsonGet, parseArgs, parseCsvIdsToArray, printAndExit } from "./client.js";

const args = parseArgs(process.argv);
const options = buildRequestOptions(args);

if (!args["start-date"] || !args["end-date"]) {
  console.error(
    "Usage: ./report.js --api-key <key> --start-date YYYY-MM-DD --end-date YYYY-MM-DD [--base-url https://api.socialecho.net] [--team-id 123] [--lang zh_CN] [--time-type 1] [--group day] [--account-ids 1,2]"
  );
  process.exit(2);
}

const accountIds = parseCsvIdsToArray(args["account-ids"]);
const body = {
  start_date: args["start-date"],
  end_date: args["end-date"],
  time_type: Number(args["time-type"] ?? 1),
  group: args.group ?? ""
};
if (accountIds) body.account_ids = accountIds;

const result = await callJsonGet("/v1/report", body, options);

printAndExit(result);


path: team.js
content: #!/usr/bin/env node
import { buildRequestOptions, callJsonGet, parseArgs, printAndExit } from "./client.js";

const args = parseArgs(process.argv);
const options = buildRequestOptions(args);
const result = await callJsonGet("/v1/team", {}, options);
printAndExit(result);


path: upload-url.js
content: #!/usr/bin/env node
/**
 * GET /v1/upload/url — body 必填 content_type（MIME），与 socialEchoApidocs_cn.md §5.5 一致。
 */
import {
  buildRequestOptions,
  callJsonGet,
  parseArgs,
  printAndExit
} from "./client.js";

export const CONTENT_TYPE_ENUM = [
  "image/jpeg",
  "image/jpg",
  "image/png",
  "image/gif",
  "image/webp",
  "image/bmp",
  "video/mp4",
  "video/avi",
  "video/mov",
  "video/wmv",
  "video/flv",
  "video/webm",
  "video/mkv",
  "video/3gp",
  "video/quicktime"
];

const args = parseArgs(process.argv);

const contentType = String(
  args["content-type"] || args.content_type || ""
).trim();
if (!contentType) {
  console.error(
    "Usage: ./upload-url.js --api-key <key> --content-type <mime> [--base-url ...] [--team-id ...] [--lang zh_CN]\n" +
      "mime must be one of: " +
      CONTENT_TYPE_ENUM.join(", ")
  );
  process.exit(2);
}

if (!CONTENT_TYPE_ENUM.includes(contentType)) {
  console.error("Invalid --content-type. Allowed values:\n" + CONTENT_TYPE_ENUM.join("\n"));
  process.exit(2);
}

const options = buildRequestOptions(args);
const result = await callJsonGet(
  "/v1/upload/url",
  { content_type: contentType },
  options
);
printAndExit(result);


path: _meta.json
content: ownerId: socialecho-net
slug: socialecho-social-media-management-agent
version: 1.3.0

Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)