# Publish to Instagram

> Publish an Instagram image or carousel.

Canonical: <https://www.tryadeli.com/docs/api/posts/publish-instagram>

`POST https://app.tryadeli.com/api/v1/posts`

Authorization: `Bearer <API key>` — see [Authentication](https://www.tryadeli.com/docs/authentication)

Send `application/json` with base64 media. Images may be JPEG, PNG, or WebP, and
each must decode to no more than 8 MiB. Adeli normalizes them to JPEG and stages
them where Instagram can fetch them.

**Body**

| Name             | Type          | Required | Description                                                                                                                                                                 |
| ---------------- | ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `platform`       | `"instagram"` | Yes      |                                                                                                                                                                             |
| `accountId`      | `uuid`        | Yes      | A connected Instagram account in your organization.                                                                                                                         |
| `profileId`      | `uuid`        |          | Optional, because accountId names its profile. If you pass both, they must agree.                                                                                           |
| `caption`        | `string`      |          | Up to 2,200 characters, 30 hashtags, and 20 `@` mentions. Hashtags and mentions are written inline — Instagram reads them from the caption.                                 |
| `image`          | `object`      |          | A single image: `{ contentType, base64, altText?, userTags? }`. Mutually exclusive with `images`.                                                                           |
| `images`         | `object[]`    |          | A carousel of 2 to 10 images, each shaped like `image`. Mutually exclusive with `image`.                                                                                    |
| `image.altText`  | `string`      |          | Up to 1,000 characters of alt text for screen readers. Per image, carousels included.                                                                                       |
| `image.userTags` | `object[]`    |          | Up to 20 people tagged in the image, each `{ username, x, y }`. `x` and `y` are fractions from 0 to 1 measured from the image's top-left corner. A leading `@` is accepted. |
| `collaborators`  | `string[]`    |          | Up to 3 usernames invited as co-authors. Each must accept the invite in Instagram before the post shows them.                                                               |
| `isAiGenerated`  | `boolean`     |          | Adds Instagram's AI-generated label to the post.                                                                                                                            |

A carousel swaps `image` for `images`. Alt text and people tags belong to each
image; the caption, collaborators, and AI label belong to the post:

```json
{
  "platform": "instagram",
  "accountId": "00000000-0000-0000-0000-000000000000",
  "caption": "Three backyard birds with @centralparkbirders #birding #spring",
  "collaborators": ["centralparkbirders"],
  "isAiGenerated": false,
  "images": [
    { "contentType": "image/webp", "base64": "...", "altText": "A cardinal on a snowy branch" },
    { "contentType": "image/png", "base64": "...", "userTags": [{ "username": "birder", "x": 0.4, "y": 0.6 }] },
    { "contentType": "image/jpeg", "base64": "..." }
  ]
}
```

Unknown fields are rejected with `400 invalid_request` rather than ignored. A
tagged person or collaborator Instagram cannot resolve — a private account or a
misspelled username — fails with `422 provider_rejected` and Instagram's own
explanation as the message.

Success returns `201` and the normalized post, with `media` mirroring the staged
images. `engagement` is null on a fresh post — the counts are not available
until Instagram has them.

**Errors** — `400 invalid_request`, `413 invalid_request`,
`415 unsupported_media_type`, `422 invalid_image`, `422 connection_expired`,
`422 provider_not_configured`, `422 provider_rejected`, `404 account_not_found`, `404 profile_not_found`,
`502 provider_error`.

## Request examples

**cURL**

```bash
curl --fail-with-body -X POST "https://app.tryadeli.com/api/v1/posts" \
  -H "Authorization: Bearer $ADELI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "platform": "instagram",
  "accountId": "00000000-0000-0000-0000-000000000000",
  "caption": "Hello",
  "image": {
    "contentType": "image/png",
    "base64": "<base64 PNG>"
  }
}'
```

**JavaScript**

```javascript
const response = await fetch("https://app.tryadeli.com/api/v1/posts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ADELI_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    platform: "instagram",
    accountId: "00000000-0000-0000-0000-000000000000",
    caption: "Hello",
    image: {
      contentType: "image/png",
      base64: "<base64 PNG>",
    },
  }),
});
console.log(await response.json());
```

**Python**

```python
import os

import requests

response = requests.post(
    "https://app.tryadeli.com/api/v1/posts",
    headers={"Authorization": f"Bearer {os.environ['ADELI_API_KEY']}"},
    json={
        "platform": "instagram",
        "accountId": "00000000-0000-0000-0000-000000000000",
        "caption": "Hello",
        "image": {
            "contentType": "image/png",
            "base64": "<base64 PNG>",
        },
    },
)
print(response.json())
```

## Responses

### 201

```json
{
  "id": "instagram_post_00000000-0000-0000-0000-000000000000_17900000000000000",
  "platform": "instagram",
  "accountId": "00000000-0000-0000-0000-000000000000",
  "profileId": "00000000-0000-4000-8000-000000000001",
  "providerId": "17900000000000000",
  "caption": "Hello",
  "media": [{ "type": "IMAGE", "url": "https://...", "thumbnailUrl": "https://..." }],
  "permalink": "https://www.instagram.com/p/...",
  "engagement": { "likes": null, "comments": null },
  "publishedAt": "2026-04-01T12:00:00.000Z"
}
```

### 422

```json
{
  "error": {
    "code": "connection_expired",
    "message": "Instagram connection has expired"
  }
}
```
