Quickstart

Six steps: create a key, connect your customer's Instagram account, and publish an image. Everything here runs against a real account — there is no sandbox mode, so use an account you do not mind posting to.

You will need an Instagram professional (Business or Creator) account. TikTok and YouTube follow the same shape; the differences are called out in the Posts reference.

1. Create an API key

Sign in at /sign-in with Google. Any Google account works, and your first sign-in creates your account and a default profile.

Open API keys in the dashboard sidebar, or go straight to /settings/api-keys. Give the key a label such as Local testing and select Create key.

The secret is shown once

Copy it immediately — Adeli stores only a hash and cannot show it to you again. Keys are bound to whichever profile was selected when you created them, and deleting a key revokes it instantly.

Save it alongside the base URL:

bash
export ADELI_URL=https://app.tryadeli.com
export ADELI_API_KEY='rk_live_...'

2. Read the profile your key is bound to

Every key is permanently scoped to one profile, and most endpoints default to it. This call tells you which one, and confirms the key works.

curl --fail-with-body "$ADELI_URL/api/v1/profiles" \
-H "Authorization: Bearer $ADELI_API_KEY"
json
{
  "profiles": [
    {
      "id": "00000000-0000-4000-8000-000000000001",
      "name": "Client A",
      "externalId": "customer-123",
      "metadata": {},
      "isDefault": true,
      "createdAt": "2026-09-08T20:00:00.000Z",
      "updatedAt": "2026-09-08T20:00:00.000Z"
    }
  ]
}

Save that id — the next two calls need it.

bash
export PROFILE_ID=00000000-0000-4000-8000-000000000001

3. Start a connection

This returns an authUrl. Open it in your customer's browser — they authorize Instagram directly with Meta, and Adeli never sees their credentials.

curl --fail-with-body -X POST \
"$ADELI_URL/api/v1/profiles/$PROFILE_ID/connect" \
-H "Authorization: Bearer $ADELI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"platform":"instagram","redirectUrl":"https://client.example/callback"}'
json
{
  "id": "00000000-0000-4000-8000-000000000002",
  "profileId": "00000000-0000-4000-8000-000000000001",
  "platform": "instagram",
  "status": "pending_authorization",
  "accountId": null,
  "expiresAt": "2026-09-08T20:10:00.000Z",
  "completedAt": null,
  "error": null,
  "authUrl": "https://www.instagram.com/oauth/authorize?..."
}

redirectUrl must be allowlisted

Its origin has to appear in the server's PUBLIC_API_CONNECT_REDIRECT_ORIGINS allowlist and use HTTPS, except for localhost and 127.0.0.1 during development. An origin that is not on the list returns 400 invalid_redirect_uri. You can omit redirectUrl entirely and just poll instead.

4. Poll until the connection lands

The session expires ten minutes after it is created. Poll it until status is one of the three terminal values: connected, failed, or expired.

curl --fail-with-body \
"$ADELI_URL/api/v1/profiles/$PROFILE_ID/connect/$CONNECTION_SESSION_ID" \
-H "Authorization: Bearer $ADELI_API_KEY"

If you supplied a redirectUrl, the browser comes back to it with connectionSessionId, status, and — on success — accountId in the query string. Treat those as a hint for your UI and poll the session for the authoritative answer.

5. List the connected accounts

curl --fail-with-body "$ADELI_URL/api/v1/accounts?platform=instagram" \
-H "Authorization: Bearer $ADELI_API_KEY"
json
{
  "status": "complete",
  "accounts": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "profileId": "00000000-0000-4000-8000-000000000001",
      "platform": "instagram",
      "providerId": "17841400000000000",
      "displayName": "@adeli",
      "displayIdentifier": "adeli",
      "connectionStatus": "connected"
    }
  ],
  "errors": []
}

Use accountId — Adeli's UUID — in every later request. providerId is informational.

6. Publish an image

Images are sent as base64 and must be JPEG, PNG, or WebP, decoding to no more than 8 MiB. Adeli normalizes them to JPEG before handing them to Instagram.

# Build the request body without putting base64 on the command line.
node -e '
const fs = require("node:fs");
fs.writeFileSync("/tmp/adeli-post.json", JSON.stringify({
platform: "instagram",
accountId: process.env.ACCOUNT_ID,
caption: "Published through Adeli",
image: { contentType: "image/png", base64: fs.readFileSync("./photo.png").toString("base64") },
}));
'

curl --fail-with-body -X POST "$ADELI_URL/api/v1/posts" \
-H "Authorization: Bearer $ADELI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @/tmp/adeli-post.json

A successful publish returns 201 and the normalized post:

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": "Published through Adeli",
  "media": [{ "type": "IMAGE", "url": "https://media.example/staging/key.jpg", "thumbnailUrl": "https://media.example/staging/key.jpg" }],
  "permalink": null,
  "engagement": { "likes": null, "comments": null },
  "publishedAt": "2026-04-01T12:00:00.000Z"
}

Pass images instead of image — an array of 2 to 10 — to publish a carousel.

Where to go next

  • Authentication — key scoping and how to keep keys out of the browser.
  • Core concepts — partial responses, and the limits on every read.
  • Posts — TikTok video, photo, and carousel publishing, and YouTube uploads.
  • Errors — every code the API can return.