> ## Documentation Index
> Fetch the complete documentation index at: https://docs.postbreeze.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Instagram

> Schedule Instagram Feed posts, Reels, and carousels via Postbreeze. Business and Creator accounts only.

## Quick reference

| Field                   | Value                                 |
| ----------------------- | ------------------------------------- |
| Caption limit           | 2,200 characters                      |
| Hashtags per post       | 30 max                                |
| Mentions per post       | 20 max                                |
| Carousel size           | 2–10 items                            |
| Mixed media in carousel | ✅ (image + video)                     |
| Video formats           | MP4, MOV                              |
| Image formats           | JPEG, PNG                             |
| Max video size          | 100 MB (Feed), 1 GB (Reels)           |
| Max image size          | 30 MB                                 |
| Reel duration           | 3–90 seconds                          |
| Feed video duration     | 3 seconds – 60 minutes                |
| Aspect ratios           | 4:5 to 1.91:1 (Feed), 9:16 (Reel)     |
| Post types              | Feed (single, carousel), Reel         |
| First comment           | ✅ Auto-posts after publish            |
| Account types           | Business + Creator only (no Personal) |

## Before you start

<Warning>
  Instagram **personal accounts cannot publish via API** — only Business and Creator accounts are eligible. The connect flow detects this and won't complete OAuth for unsupported account types.

  The connected Instagram account must also be linked to a Facebook Page that you administer; that's how Meta authorizes the publish action.
</Warning>

The connected Instagram account must:

* Be a Business or Creator account (not Personal).
* Be linked to a Facebook Page you administer.
* Have granted scopes `instagram_business_basic`, `instagram_business_content_publish`, and `instagram_business_manage_comments` (the latter unlocks first-comment).

The first-comment scope was added in May 2026. Accounts connected before that date have to reconnect to use first-comment — the API surfaces this with the `IG_SCOPE_MISSING` error.

## Quick start

Schedule a single-image Feed post 5 minutes from now using the flat
body shape (`content` + `platforms`). The workspace is inferred from
your API key — no `workspaceId` argument needed. The example uses
`mediaItems` to ingest a public image URL on the fly; see
[Media uploads](/concepts/media-uploads) for the pre-upload alternative.

<CodeGroup>
  ```js Node.js theme={null}
  import Postbreeze from "@postbreeze/node";

  const postbreeze = new Postbreeze({ apiKey: process.env.POSTBREEZE_API_KEY });

  const post = await postbreeze.posts.create({
    content: "Launching the new collection today 🚀",
    scheduledFor: new Date(Date.now() + 5 * 60_000).toISOString(),
    mediaItems: [{ type: "image", url: "https://cdn.example.com/launch.jpg" }],
    platforms: [{ accountId: "soc_instagram_…" }],
  });

  console.log("Scheduled:", post.id);
  ```

  ```python Python theme={null}
  import os
  from datetime import datetime, timedelta, timezone
  import requests

  API = "https://api.postbreeze.ai/api/v1"

  scheduled = (datetime.now(timezone.utc) + timedelta(minutes=5)).isoformat()
  res = requests.post(
      f"{API}/posts",
      headers={"Authorization": f"Bearer {os.environ['POSTBREEZE_API_KEY']}"},
      json={
          "content": "Launching the new collection today 🚀",
          "scheduledFor": scheduled,
          "mediaItems": [{"type": "image", "url": "https://cdn.example.com/launch.jpg"}],
          "platforms": [{"accountId": "soc_instagram_…"}],
      },
  )
  res.raise_for_status()
  print("Scheduled:", res.json())
  ```

  ```bash curl theme={null}
  curl -X POST https://api.postbreeze.ai/api/v1/posts \
    -H "Authorization: Bearer $POSTBREEZE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": "Launching the new collection today 🚀",
      "scheduledFor": "2026-06-02T12:05:00Z",
      "mediaItems": [
        { "type": "image", "url": "https://cdn.example.com/launch.jpg" }
      ],
      "platforms": [{ "accountId": "soc_instagram_…" }]
    }'
  ```
</CodeGroup>

<Tip>
  **Need fine control?** Switch to the nested shape (`caption` +
  `targets`) when you want to set `platformOptions` per-platform, send
  different media per platform, or set a `kind: "REEL"` discriminator —
  see [Full control](#full-control-nested-shape) at the bottom. The
  flat shape's `platformOptions` per-entry handles most cases though —
  see [Platform settings](/concepts/platform-settings#instagram).
</Tip>

## Content types

### Single-image or single-video Feed post

Default for any single-item Feed post. `kind` defaults to `"FEED"` and
exactly one media item is attached.

### Carousel (2–10 items)

Pass 2–10 pre-uploaded `mediaIds` (or 2–10 `mediaItems` for URL
ingest). Instagram crops every slide to the **first slide's aspect
ratio** — so if you start with a 4:5 image and follow it with a 1:1
image, the second gets top + bottom cropped without warning. Postbreeze
surfaces a per-slide warning at compose-time when this is detected.

<CodeGroup>
  ```js Node.js theme={null}
  const post = await postbreeze.posts.create({
    content: "Behind the scenes from the shoot ✨",
    scheduledFor: new Date(Date.now() + 30_000).toISOString(),
    mediaIds: ["med_1", "med_2", "med_3", "med_4"],
    platforms: [{ accountId: "soc_instagram_…" }],
  });
  ```

  ```python Python theme={null}
  res = requests.post(
      f"{API}/posts",
      headers={"Authorization": f"Bearer {os.environ['POSTBREEZE_API_KEY']}"},
      json={
          "content": "Behind the scenes from the shoot ✨",
          "scheduledFor": (datetime.now(timezone.utc) + timedelta(seconds=30)).isoformat(),
          "mediaIds": ["med_1", "med_2", "med_3", "med_4"],
          "platforms": [{"accountId": "soc_instagram_…"}],
      },
  )
  res.raise_for_status()
  ```

  ```bash curl theme={null}
  curl -X POST https://api.postbreeze.ai/api/v1/posts \
    -H "Authorization: Bearer $POSTBREEZE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": "Behind the scenes from the shoot ✨",
      "scheduledFor": "2026-06-02T12:00:30Z",
      "mediaIds": ["med_1","med_2","med_3","med_4"],
      "platforms": [{ "accountId": "soc_instagram_…" }]
    }'
  ```
</CodeGroup>

Prefer URL ingest for a quick test? Swap `mediaIds` for `mediaItems`
with per-item `altText`:

```js theme={null}
await postbreeze.posts.create({
  content: "Behind the scenes from the shoot ✨",
  scheduledFor: new Date(Date.now() + 30_000).toISOString(),
  mediaItems: [
    { type: "image", url: "https://cdn.example.com/shot-1.jpg", altText: "Producer setting up lights" },
    { type: "image", url: "https://cdn.example.com/shot-2.jpg", altText: "Stylist adjusting outfit" },
    { type: "image", url: "https://cdn.example.com/shot-3.jpg" },
    { type: "image", url: "https://cdn.example.com/shot-4.jpg" },
  ],
  platforms: [{ accountId: "soc_instagram_…" }],
});
```

### Reel

Set `kind: "REEL"` via `platformOptions` on the platform entry. The
attached media must be a video between 3–90 seconds, 9:16 aspect ratio.
Reels are also shared to the Feed by default.

<CodeGroup>
  ```js Node.js theme={null}
  const post = await postbreeze.posts.create({
    content: "30-second product demo",
    scheduledFor: new Date(Date.now() + 30_000).toISOString(),
    mediaIds: ["med_video_…"],
    platforms: [{
      accountId: "soc_instagram_…",
      platformOptions: { platform: "INSTAGRAM", kind: "REEL" },
      firstComment: "Drop a 🔥 if you want a longer version!",
    }],
  });
  ```

  ```python Python theme={null}
  res = requests.post(
      f"{API}/posts",
      headers={"Authorization": f"Bearer {os.environ['POSTBREEZE_API_KEY']}"},
      json={
          "content": "30-second product demo",
          "scheduledFor": (datetime.now(timezone.utc) + timedelta(seconds=30)).isoformat(),
          "mediaIds": ["med_video_…"],
          "platforms": [{
              "accountId": "soc_instagram_…",
              "platformOptions": {"platform": "INSTAGRAM", "kind": "REEL"},
              "firstComment": "Drop a 🔥 if you want a longer version!",
          }],
      },
  )
  res.raise_for_status()
  ```

  ```bash curl theme={null}
  curl -X POST https://api.postbreeze.ai/api/v1/posts \
    -H "Authorization: Bearer $POSTBREEZE_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "content": "30-second product demo",
      "scheduledFor": "2026-06-02T12:00:30Z",
      "mediaIds": ["med_video_…"],
      "platforms": [{
        "accountId": "soc_instagram_…",
        "platformOptions": { "platform": "INSTAGRAM", "kind": "REEL" },
        "firstComment": "Drop a 🔥 if you want a longer version!"
      }]
    }'
  ```
</CodeGroup>

## Media requirements

### Images

| Property       | Requirement                         |
| -------------- | ----------------------------------- |
| Max items      | 10 per carousel, 1 for single posts |
| Formats        | JPEG, PNG                           |
| Max file size  | 30 MB                               |
| Aspect ratio   | 4:5 to 1.91:1                       |
| Min resolution | 320 × 320                           |
| Max resolution | 8192 × 8192                         |

### Videos

| Property             | Requirement             |
| -------------------- | ----------------------- |
| Formats              | MP4, MOV                |
| Max file size (Feed) | 100 MB                  |
| Max file size (Reel) | 1 GB                    |
| Min duration         | 3 seconds               |
| Max duration (Feed)  | 60 minutes              |
| Max duration (Reel)  | 90 seconds              |
| Aspect ratio (Feed)  | 4:5 to 1.91:1           |
| Aspect ratio (Reel)  | 9:16                    |
| Codecs               | H.264 + AAC             |
| Resolution (Reel)    | 1080 × 1920 recommended |

Postbreeze itself accepts images (JPG, PNG, GIF, WebP, HEIC, HEIF) and
videos (MP4, MOV, AVI, WebM) up to 5 GB at upload time — the tighter
caps above are Instagram's publish-time limits. See
[Media uploads](/concepts/media-uploads) for the full ingest pipeline.

## Platform-specific fields

| Field      | Type                 | Required            | Description                                                                             |
| ---------- | -------------------- | ------------------- | --------------------------------------------------------------------------------------- |
| `platform` | `"INSTAGRAM"`        | Yes                 | Discriminator.                                                                          |
| `kind`     | `"FEED"` \| `"REEL"` | No (default `FEED`) | Picks the publish surface. Single-video posts can be either; multi-item must be `FEED`. |

Instagram has no other publish-time settings. Use `firstComment` at the
platform-entry level (not inside `platformOptions`) for the
auto-comment. See [Platform settings](/concepts/platform-settings#instagram)
for the full reference.

## First comment

Pass `firstComment` on the platform entry. Postbreeze waits 3 seconds
after the main post lands (Instagram occasionally 4xx's with `media not
available` on faster calls) and then posts the comment as the same
authenticated user.

```js theme={null}
platforms: [{
  accountId: "soc_instagram_…",
  platformOptions: { platform: "INSTAGRAM", kind: "FEED" },
  firstComment: "#launch #behindthescenes #brand",
}]
```

Comment limit: 2,200 characters (same as caption). If the comment call fails (e.g. rate limit), the main post still shows as PUBLISHED and the failure surfaces on `PostTarget.firstCommentError`. Use the retry endpoint to try the comment again.

## Analytics

| Metric                 | Available                     |
| ---------------------- | ----------------------------- |
| Impressions            | ✅                             |
| Reach                  | ✅                             |
| Likes                  | ✅                             |
| Comments               | ✅                             |
| Saves                  | ✅                             |
| Shares                 | ✅                             |
| Profile visits         | ✅                             |
| Follows from post      | ✅                             |
| Video views (Reels)    | ✅                             |
| Avg watch time (Reels) | ✅                             |
| Story-only metrics     | ❌ (Stories not yet supported) |

Refresh cadence: every **14 days** per Meta's Insights guidance.

## Common errors

| Error                      | Meaning                                                    | Fix                                                      |
| -------------------------- | ---------------------------------------------------------- | -------------------------------------------------------- |
| `IG_CONTAINER_EXPIRED`     | The container was created but not published within 24h     | Re-create — Postbreeze does this automatically on retry. |
| `IG_CAROUSEL_SIZE`         | Carousel had fewer than 2 or more than 10 items            | Trim to 2–10.                                            |
| `IG_REEL_REQUIRES_VIDEO`   | `kind: "REEL"` but no video attached                       | Attach a video.                                          |
| `IG_FEED_REQUIRES_IMAGE`   | Single-item Feed post with a non-image                     | Attach an image or set `kind: "REEL"`.                   |
| `IG_MEDIA_NOT_READY`       | Comment call fired before IG finished processing the media | Postbreeze retries with backoff. Resolved automatically. |
| `IG_SCOPE_MISSING`         | Account doesn't have `instagram_business_manage_comments`  | Reconnect the account; the compose UI shows a banner.    |
| `IG_ASPECT_RATIO`          | Image or video outside 4:5–1.91:1                          | Crop/resize before uploading.                            |
| `IG_DURATION_OUT_OF_RANGE` | Reel \< 3s or > 90s                                        | Trim before uploading.                                   |

## What you can't do

* ❌ Publish to Personal Instagram accounts (Business/Creator only)
* ❌ Stories (planned for v2)
* ❌ Tag products or shoppable posts
* ❌ Tag users in caption (you can include `@handle` but no structured tagging)
* ❌ Add location tags (Meta removed this from the API)
* ❌ Schedule via Instagram's native scheduler
* ❌ Edit captions after publish
* ❌ Live video, IGTV

## Full control (nested shape)

The nested shape (`caption` + `targets`) is the long-form alternative
to the flat shape used everywhere else on this page. Reach for it when
you want different captions per platform, different media per platform
in the same request, or just prefer the explicit `socialAccountId` /
`platformOptions` naming.

The two shapes are interchangeable — every example above can be
rewritten in the nested form by renaming `content` → `caption`,
`scheduledFor` → `scheduledAt`, `platforms` → `targets`, and
`accountId` → `socialAccountId`.

```ts SDK theme={null}
await postbreeze.posts.create({
  caption: "Behind the scenes from the shoot ✨",
  scheduledAt: new Date(Date.now() + 30_000).toISOString(),
  mediaIds: ["med_1", "med_2", "med_3"],
  targets: [
    {
      socialAccountId: "soc_instagram_…",
      platformOptions: { platform: "INSTAGRAM", kind: "FEED" },
    },
  ],
});
```
