# Instagram API: Posting Reference

Instagram is supported via the Instagram Graph API. Connected accounts must be Instagram Business or Creator accounts linked to a Facebook Page. Personal Instagram accounts are not supported by Meta's API and cannot be connected.

**Channel ID:** `instagram`

## Supported content types

| Type | Supported | Notes |
|------|-----------|-------|
| Feed post | ✅ | Single image, video, or carousel (up to 10 items) |
| Story | ✅ | Requires media (image or video). Up to 10 slides per post, each published as its own story in order |
| Reel | ✅ | Requires a vertical video |

## Minimal example

```bash
curl -X POST https://api.omnisocials.com/v1/posts/create \
  -H "Authorization: Bearer $OMNISOCIALS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": { "default": "Golden hour 🌅" },
    "accounts": ["your-instagram-account-id"],
    "media_urls": ["https://example.com/sunset.jpg"]
  }'
```

Instagram posts always require media. A post with no media returns `400`.

## Instagram options

Set these under the `instagram` key on the post body:

| Field | Type | Description |
|-------|------|-------------|
| `instagram.first_comment` | string | Text auto-posted as the first comment right after the post publishes (max 2200 characters). Common for keeping hashtags out of the caption. Works on feed posts and reels; not posted for stories. |
| `instagram.share_to_feed` | boolean | Reel only. Also display the reel on the profile grid. |
| `instagram.thumbnail_type` | string | Video thumbnail (reels and single-video feed posts): `from-video` or `from-library`. The top-level [`video_cover`](/creating-posts#video-covers) does the same for every platform at once. |
| `instagram.thumb_offset` | number | Timestamp in milliseconds, used when `thumbnail_type` is `from-video`. |
| `instagram.cover_url` | string | URL of a custom cover image, used when `thumbnail_type` is `from-library`. |

## Instagram-only post options

These are set as **top-level** fields on the post body (not under the `instagram` key) and are ignored by other platforms:

| Field | Type | Description |
|-------|------|-------------|
| `location_id` | string | Facebook Place/Page ID to tag the post's location. Single-image and carousel feed posts only. Find a Place ID with `GET /locations/search`. |
| `collaborators` | array of strings | Up to 3 public Instagram usernames invited as co-authors (the "Collab" feature). Image, carousel, and reel posts; not stories. |
| `user_tags` | array | Tag public accounts at positions on a photo: `[{"username": "name", "x": 0.5, "y": 0.8, "image_index": 0}]`. `x`/`y` are 0–1 from the top-left. Photos only, not video, reels, or stories. |

## Media requirements

| Media | Requirement |
|-------|-------------|
| Feed image | JPEG or PNG, 320 to 1440 px wide, aspect ratio between 9:16 (portrait) and 1.91:1 (landscape) |
| Feed video | MP4 or MOV, up to 15 minutes, max 300 MB |
| Story image | JPEG or PNG, aspect ratio 9:16 recommended |
| Story video | MP4, up to 60 seconds per slide, max 300 MB |
| Reel video | MP4, vertical 9:16, up to 15 minutes, max 300 MB |
| Carousel | 2 to 10 items; images and videos can be mixed in one carousel |

Content that violates aspect ratio or duration is rejected at publish time with a `400` error referencing the Instagram Graph API response.

## Alt text

Pass an accessibility description per media item by using an object entry instead of a bare string (works in `media_urls` and `media_ids`). It is sent as Instagram's `alt_text` on image posts and on each image slide of a carousel; every slide carries its own description. Reels and Stories don't support alt text (an Instagram limitation), and Instagram caps descriptions at 1,000 characters, so longer values are clamped at publish time:

```json
{
  "content": { "default": "Morning ride before work" },
  "accounts": ["your-instagram-account-id"],
  "media_urls": [
    { "url": "https://example.com/bike.jpg", "alt": "A red bicycle leaning against a brick wall" }
  ]
}
```

The same `alt` also delivers to Mastodon, Bluesky, X, Pinterest, and LinkedIn (images), so one description covers a cross-platform post.

## Character and hashtag limits

Captions cap at 2,200 characters. Instagram also rejects any caption or comment carrying more than **30 hashtags** ("Too many hashtags"); OmniSocials validates this at create/schedule time, including hashtags added via `first_comment` or a hashtag set.

## Limitations

- Personal (non-business) Instagram accounts cannot be connected
- Tagging users in media requires each tagged user to be a public account
- Instagram does not allow scheduling posts further than 75 days in advance

## Testing

In test mode (`omsk_test_*` keys), Instagram posts are validated but never sent to Meta. The response shape matches production so your integration can be built and tested without risking real posts.
