# @apicity/meta 中文文档

规范地址：https://npmcn.com/package/%40apicity/meta/

版本：0\.27\.0
npm 元数据更新时间：2026-11-06T09:56:20.231Z
中文译文更新时间：2026-11-06T10:14:31.076Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40apicity/meta
原文对照：https://npmcn.com/package/%40apicity/meta/?lang=original
项目主页：https://github.com/justintanner/apicity#readme
源码仓库：https://github.com/justintanner/apicity

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：55.2 kB，27 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=18\.0\.0
- 运行依赖：1 个
- 首次发布：2026-05-10T07:46:56.164Z
- 最新版本发布：2026-11-06T09:56:20.039Z
- 近 30 天下载量：3766（2026-10-06 至 2026-11-04，npm 官方下载统计）
- 已发布版本数：90（近 12 个月 90 个）

## 中文简介

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

## 安装

```sh
npm install @apicity/meta
```

也可以使用：`pnpm add @apicity/meta`、`yarn add @apicity/meta`、`bun add @apicity/meta`

## README

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

[![npm](https://img.shields.io/npm/v/@apicity/meta?color=cb0000)](https://www.npmjs.com/package/@apicity/meta) [![dependencies](https://img.shields.io/badge/dependencies-1-blue)](https://npmcn.com/package/%40apicity/meta/package.json) [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?logo=typescript&logoColor=white)](https://npmcn.com/package/%40apicity/meta/tsconfig.json)

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

运行时间依赖性 :

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

## 安装

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

## 快速启动

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

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

## 设置

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

### 1\. 配置您的 Meta App

打开 [开发者. facebook.com](https://developers.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` 运行它:

```bash
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 帮助器

```javascript
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\. 使用标志

```typescript
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`](https://npmcn.com/tests/integration/ig-post-video.test.ts) 步为正. 收件箱上传反弹 [`tests/recordings/free_2578706139/`](https://npmcn.com/tests/recordings/free_2578706139/)· ; iG呼叫登陆 `tests/recordings/ig_*/post-video_*/recording.har` 商业/创建账户 `IG_ACCESS_TOKEN` 被记录.

```typescript
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`

[上游区域](https://developers.facebook.com/docs/instagram-platform/reference/ig-container/)

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

资料来源: [`packages/provider/meta/src/meta.ts`](https://npmcn.com/package/%40apicity/meta/src/meta.ts)

### 媒体

`POST` **`meta.v25.media`**

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

成本等级 : `cheap`

[上游区域](https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media/)

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

资料来源: [`packages/provider/meta/src/meta.ts`](https://npmcn.com/package/%40apicity/meta/src/meta.ts)

### 媒体

`POST` **`meta.v25.mediaPublish`**

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

成本等级 : `cheap`

[上游区域](https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media_publish/)

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

资料来源: [`packages/provider/meta/src/meta.ts`](https://npmcn.com/package/%40apicity/meta/src/meta.ts)

部分 [辣点](https://github.com/justintanner/apicity) 单相机.

## 许可证

麻省理工学院——见 [自由主义](https://npmcn.com/package/%40apicity/meta/LICENSE)。 。 。 .

## 常见问题

### @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 和提交记录判断。

