@apicity/meta

中文文档
0.27.0发布于 2026/11/06MITTypeScriptES Module近 30 天下载 3766

由Instagram Graph API提供商通过公-URL流(graph.instagram.com)发布回路.

中文使用文档
文档目录13 节
  1. 安装
  2. 快速启动
  3. 设置
  4. 1. 配置您的 Meta App
  5. 2. 验证Instagram账户类型
  6. 3. 薄荷长期使用
  7. 4. 使用标志
  8. 现实世界的例子:通过公共-URL流发布一个 Reel
  9. API 参考
  10. 集装箱
  11. 媒体
  12. 媒体
  13. 许可证

@apicity/ meta (中文(简体) )

npm dependencies TypeScript

由Instagram Graph API提供商通过公-URL流(graph.instagram.com)发布回路.

运行时间依赖性 :

  • zod@^4.6.6 - 请求在每一个 POST 端点上附加计划 .schema

安装

npm install @apicity/meta
# or
pnpm add @apicity/meta

快速启动

import { createMeta } from "@apicity/meta";

const meta = createMeta({ accessToken: process.env.IG_ACCESS_TOKEN! });

设置

Instagram 需要一个 长寿(60天)用户访问符 从 Instagram 登录 OAuth 流. 符号绑定在一个特定的Instagram上 企业或创建者账户;个人账户没有方案 出版访问.

1. 配置您的 Meta App

打开 开发者. facebook.com创建一个 新应用( 类型 : 商业,然后添加 内存 产品. 选择 使用 Instagram 登录的 API 设置 并配置 :

  • 有效的 OAuth 重定向 URI : http://127.0.0.1:8765/callback
  • 权限 : instagram_business_basic + 键 instagram_business_content_publish
  • 保存该 Instagram 应用程序标识 财务报告和审定财务报表 Instagram 应用程序机密

2. 验证Instagram账户类型

你所出版的Instagram帐户一定是 商业 或者说 创建者 (不是个人的). 在Instagram 移动应用程序下切换 设置 ~ 账户类型和工具. 不需要Facebook页面链接 用于Instagram登录流.

3. 薄荷长期使用

将脚本另存为 mint-ig-token.mjs 运行它:

IG_CLIENT_ID=<your-instagram-app-id> \
IG_CLIENT_SECRET=<your-instagram-app-secret> \
  node mint-ig-token.mjs

它打印一个授权 URL —— 打开, 点击 允许和帮助者 抓取方向 127.0.0.1:8765代码交换 然后升级为长寿(60天)的标志 指纹 { access_token, user_id, expires_in }保存两个 access_token 财务报告和审定财务报表 user_id - 你们两个都要给API打电话.

mint-ig-token.mjs — 0 Dep OAuth 2.0 帮助器
import http from "node:http";
import crypto from "node:crypto";

const CLIENT_ID = process.env.IG_CLIENT_ID;
const CLIENT_SECRET = process.env.IG_CLIENT_SECRET;
const REDIRECT = "http://127.0.0.1:8765/callback";
const SCOPES = [
  "instagram_business_basic",
  "instagram_business_content_publish",
].join(",");

if (!CLIENT_ID || !CLIENT_SECRET) {
  console.error("Set IG_CLIENT_ID and IG_CLIENT_SECRET");
  process.exit(1);
}

const state = crypto.randomBytes(16).toString("hex");
const authURL = new URL("https://www.instagram.com/oauth/authorize");
authURL.searchParams.set("client_id", CLIENT_ID);
authURL.searchParams.set("redirect_uri", REDIRECT);
authURL.searchParams.set("response_type", "code");
authURL.searchParams.set("scope", SCOPES);
authURL.searchParams.set("state", state);

console.log("Open this URL and click Allow:\n" + authURL.toString());

const server = http.createServer(async (req, res) => {
  const url = new URL(req.url, REDIRECT);
  if (!url.pathname.startsWith("/callback")) {
    res.writeHead(404).end();
    return;
  }
  const code = url.searchParams.get("code");
  if (!code || url.searchParams.get("state") !== state) {
    res.writeHead(400).end("bad state");
    server.close();
    process.exit(1);
  }

  // 1. short-lived
  const shortRes = await fetch(
    "https://api.instagram.com/oauth/access_token",
    {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: new URLSearchParams({
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
        grant_type: "authorization_code",
        redirect_uri: REDIRECT,
        code,
      }),
    }
  );
  const short = await shortRes.json();

  // 2. long-lived (60-day)
  const longURL = new URL("https://graph.instagram.com/access_token");
  longURL.searchParams.set("grant_type", "ig_exchange_token");
  longURL.searchParams.set("client_secret", CLIENT_SECRET);
  longURL.searchParams.set("access_token", short.access_token);
  const longRes = await fetch(longURL);
  const long = await longRes.json();

  console.log(JSON.stringify({
    access_token: long.access_token,
    expires_in: long.expires_in,
    user_id: short.user_id,
  }, null, 2));
  res.writeHead(200).end("Authorized — check your terminal.");
  server.close();
});

server.listen(8765, "127.0.0.1");

4. 使用标志

import { createMeta } from "@apicity/meta";

const meta = createMeta({ accessToken: process.env.IG_ACCESS_TOKEN! });
const igUserId = process.env.IG_USER_ID!;

// Public-URL flow: host the mp4 somewhere (e.g. via @apicity/free-media-upload) and
// pass its URL. Meta GETs the video and processes it asynchronously.
const container = await meta.post.v25.media(igUserId, {
  media_type: "REELS",
  video_url: "https://example.com/clip.mp4",
  caption: "hello from @apicity/meta",
});

// Poll until the container is ready.
let status = "IN_PROGRESS";
while (status === "IN_PROGRESS") {
  await new Promise((r) => setTimeout(r, 5000));
  const s = await meta.get.v25.container(container.id, {
    fields: "status_code,status",
  });
  status = s.status_code ?? "FINISHED";
}

// Publish.
const post = await meta.post.v25.mediaPublish(igUserId, {
  creation_id: container.id,
});
console.log(post.id);

现实世界的例子:通过公共-URL流发布一个 Reel

Instagram的Graph API不直接取视频字节——Meta需要一个 它可以公开达到的 URL GET 并同步转码。 这个 链条下方片段 @apicity/free-media-upload (盒式公共托管,免费+0) auth)为 @apicity/meta 着陆a mp4 在磁盘作为出版的里尔, 镜像 tests/integration/ig-post-video.test.ts 步为正. 收件箱上传反弹 tests/recordings/free_2578706139/· ; iG呼叫登陆 tests/recordings/ig_*/post-video_*/recording.har 商业/创建账户 IG_ACCESS_TOKEN 被记录.

import { readFileSync } from "node:fs";
import { createMeta } from "@apicity/meta";
import { createFreeMediaUpload } from "@apicity/free-media-upload";

const meta = createMeta({ accessToken: process.env.IG_ACCESS_TOKEN! });
const igUserId = process.env.IG_USER_ID!; // 17-digit numeric, e.g. "17841471234567890"

// 1. Host the mp4 publicly. catbox.moe is auth-free and persistent —
//    Meta's transcode worker will fetch this URL once during step 2,
//    so any host that returns the bytes within ~30s works (S3 presigned
//    URL, R2, your own CDN). @apicity/free-media-upload wraps the multipart upload
//    and returns the resolved file URL as a string.
const bytes = readFileSync("./jump.mp4");
const blob = new Blob([bytes], { type: "video/mp4" });

const freeMediaUpload = createFreeMediaUpload({});
const videoUrl = await freeMediaUpload.catbox.upload({
  file: blob,
  filename: "jump.mp4",
});
console.log(videoUrl);
// → "https://files.catbox.moe/nn9sei.mp4"
//   catbox returns a permanent public URL of the form
//   `https://files.catbox.moe/<6char>.<ext>` — that's what Meta will GET.

// 2. Create a media container. For Reels you must pass `media_type:
//    "REELS"` (NOT "VIDEO" — that's the legacy IGTV path Meta
//    deprecated in 2024). The container is a server-side handle: Meta
//    queues the transcode against `video_url` and returns its id
//    immediately. Containers expire 24h after creation if you don't
//    publish them.
const container = await meta.post.v25.media(igUserId, {
  media_type: "REELS",
  video_url: videoUrl,
  caption: "jump #reels",
});
console.log(container.id);
// → "17889012345678901" (17-digit container id)

// 3. Poll the container's status_code until it leaves IN_PROGRESS.
//    The state machine is IN_PROGRESS → FINISHED on success;
//    FINISHED is the only state media_publish accepts. ERROR and
//    EXPIRED are terminal failure states; PUBLISHED is what you'll
//    see if you re-poll AFTER calling media_publish. The `fields`
//    query param is required — by default the GET only returns `id`.
let statusCode: string = "IN_PROGRESS";
while (statusCode === "IN_PROGRESS") {
  await new Promise((r) => setTimeout(r, 5000));
  const s = await meta.get.v25.container(container.id, {
    fields: "status_code,status",
  });
  statusCode = s.status_code ?? "FINISHED";
  if (statusCode === "ERROR" || statusCode === "EXPIRED") {
    throw new Error(`container ${container.id} ${statusCode}: ${s.status}`);
  }
}
// statusCode → "FINISHED"
// s.status   → "Finished: Media is ready to be published."

// 4. Publish. media_publish takes the container id (NOT the media url)
//    and returns the new post's id — that's the permanent ig_id you'd
//    use to construct an https://www.instagram.com/reel/<shortcode>/
//    URL or to query insights later via Graph API.
const post = await meta.post.v25.mediaPublish(igUserId, {
  creation_id: container.id,
});
console.log(post.id);
// → "17912345678901234" (17-digit post id, distinct from container id)

页:1

  • Meta 需要一个 商业 或者说 创建者 Instagram 账户加 a 已核准 instagram_business_content_publish个人 账户获取 190 ("无效 OAuth 访问符") 实用有效的符号 .
  • video_url 必须能够从 Meta 的 IP 中达到并服务 Content-Type: video/mp4。常见的获取chas:在 转码器拉出,主机需要 User-Agent,而CDNs则 重定向到另一个来源 。 {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}三边都躲过了.
  • Reel本身必须满足Meta的Reel限制——9:16方面, 3–90s 持续时间 QQ 1GB,H.264视频,AAC音频. 表面错配为 status_code: "ERROR" 在民意调查中, 与人可读 原因在其中 status (例如:. "Error: The video is too short.") (中文(简体) ).
  • 集装箱和货站的使用 独立 17个数字的ID. 集装箱编号 - 你只需要它去投票和随后的投票 media_publish creation_id。后 ID 是永久的并存活下来 用户删除该帖.
  • 丢出错误 MetaError 与 status (HTTP代码), body (解说) 元错误信封),和一个可选 code。Meta 的两个错误形状 —— 说 error.error_user_msg 用于用户的验证和 error.message 对于其他的一切, 都浮出水面 MetaError.message,所以一个 单人 try/catch 自然读取.

API 参考

3个端点跨出3个组. 每个方法都映射出上游的 URL 路径 .

集装箱

GET meta.v25.container

GET https://graph.instagram.com/v25.0/{containerId}{query}

成本等级 : cheap

上游区域

const res = await meta.get.v25.container({ /* ... */ });

资料来源: packages/provider/meta/src/meta.ts

媒体

POST meta.v25.media

POST https://graph.instagram.com/v25.0/{igUserId}/media

成本等级 : cheap

上游区域

const res = await meta.post.v25.media({ /* ... */ });

资料来源: packages/provider/meta/src/meta.ts

媒体

POST meta.v25.mediaPublish

POST https://graph.instagram.com/v25.0/{igUserId}/media_publish

成本等级 : cheap

上游区域

const res = await meta.post.v25.mediaPublish({ /* ... */ });

资料来源: packages/provider/meta/src/meta.ts

部分 辣点 单相机.

许可证

麻省理工学院——见 自由主义。 。 。 .

版本与发布记录

@apicity/meta 共发布 90 个版本,首次发布于 2026/05/10;过去 12 个月发布了 90 个版本。

版本发布日期距今
0.27.0latest2026/11/06今天
0.26.02026/11/051 天前
0.25.02026/11/033 天前
0.24.12026/11/024 天前
0.24.02026/11/014 天前
0.23.12026/11/015 天前

关于 @apicity/meta 的常见问题

@apicity/meta 是什么?

由Instagram Graph API提供商通过公-URL流(graph.instagram.com)发布回路.

如何安装 @apicity/meta?

在项目目录运行 npm install @apicity/meta,或使用 pnpm add / yarn add / bun add。

@apicity/meta 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@apicity/meta 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@apicity/meta 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@apicity/meta 可以商用吗?

@apicity/meta 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@apicity/meta 的下载量有多少?

根据 npm 官方下载统计,@apicity/meta 在 2026/10/06 – 2026/11/04 这 30 天内被下载 3,766 次,日均约 126 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@apicity/meta 还在维护吗?

最新版本 0.27.0 发布于 2026/11/06(今天);至今共发布 90 个版本,其中过去 12 个月发布了 90 个;首次发布于 2026/05/10。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。