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:
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"{
"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.
export PROFILE_ID=00000000-0000-4000-8000-0000000000013. 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"}'{
"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"{
"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.jsonA successful publish returns 201 and the normalized post:
{
"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.