Core concepts
Four ideas explain most of the API's behavior: profiles, accounts, the partial-response contract, and the limits on aggregate reads.
Profiles
A profile is a client boundary — one of your end customers. It owns the provider connections made on its behalf, and an API key is bound to exactly one profile for its whole life.
Profile
iduuid- Adeli's identifier. Use it in every path that takes a profileId.
namestring- Display name, unique within your account. 1–200 characters.
externalIdstring | null- Your own identifier for this customer, so you do not have to store a mapping. Unique within your account when set.
metadataobject | null- Arbitrary JSON you control, up to 16 KiB encoded as UTF-8.
isDefaultboolean- Whether this is the profile new dashboard sessions start on.
Profiles are created and deleted in the dashboard. Deleting one cascades: its connected accounts, cached history, and API keys all go with it.
Accounts
An account is one provider connection inside a profile — an Instagram professional account, a Facebook Page, a TikTok account, or a YouTube channel. A profile holds at most one of each.
Every endpoint that acts on a provider takes accountId, which is Adeli's
UUID. The provider's own identifier comes back as providerId and is
informational; passing it where an accountId is expected returns
400 invalid_request.
Account
accountIduuid- The identifier to use in requests.
idis the same value. platform"instagram" | "tiktok" | "facebook" | "youtube"- Which provider this connection belongs to.
providerIdstring- The provider's identifier — an Instagram user id, TikTok's app-scoped open_id, or a YouTube channel id.
displayNamestring- Human-readable name, such as "@adeli".
displayIdentifierstring- The handle or open_id, without decoration.
connectionStatus"connected" | "expired" | "reconnect_required"- Whether Adeli can still act on this account. Anything but connected means the customer has to authorize again.
Instagram long-lived tokens last roughly 60 days. Adeli refreshes them, and
POST /api/v1/profiles/:profileId/accounts/refresh forces the attempt, but a
connection that has lapsed needs the customer to go back through
the connect flow.
Scoping a request
profileId is optional everywhere it appears.
- Omit it and Adeli uses the key's profile. This is what you want almost always.
- Supply the key's own profile and nothing changes. Useful if your code passes it uniformly.
- Supply any other profile and you get
404 profile_not_found, whether or not that profile exists.
Add accountId to narrow a read to one connection, and platform to narrow it
to one provider.
Partial responses
Aggregate reads — accounts, posts, messages — can touch more than one provider in a single request. One provider failing does not throw away the data from the others.
| HTTP | status | Meaning |
|---|---|---|
200 | complete | Every attempted provider succeeded. errors is empty. |
207 | partial | At least one succeeded and at least one failed. Successful records are still in the response; errors says which account failed and why. |
404 / 422 | — | Every attempted provider failed for the same reason, and that reason has a meaningful status: account_not_found is 404, connection_expired and provider_not_configured are 422. |
502 | — | Every attempted provider failed for mixed or upstream reasons. |
A partial response looks like this:
{
"status": "partial",
"messages": [],
"errors": [
{
"platform": "instagram",
"accountId": "00000000-0000-0000-0000-000000000000",
"code": "provider_error",
"message": "The provider request failed"
}
]
}Treat 207 as success with caveats
A client that only checks response.ok will silently accept 207 and act on
an incomplete list. Check status and inspect errors before you treat an
aggregate read as the whole picture.
Provider error bodies are never passed through. An upstream failure becomes a
sanitized provider_error so that provider credentials and internal URLs cannot
leak into your logs.
Limits
What applies to every request
Paginationnone- Adeli follows each provider's cursors server-side and returns one flat array. There are no cursors in the public API. Filter by
platformandaccountIdto keep responses small. Result cap10,000- The maximum number of records in one collection response. Exceeding it truncates the array and adds a
result_limitentry toerrors, which makes the response207. Rate limitingnone- Not implemented in v1. Do not rely on its absence — be reasonable, and expect limits to arrive before general availability.
Cachingnone- Every response carries
cache-control: no-store. Reads hit the provider. Instagram image8 MiB- Decoded size for a single image or each carousel entry. JPEG, PNG, and WebP are accepted and normalized to JPEG.
TikTok video64 MiB / 1 GB- 64 MiB decoded for base64 JSON bodies; 1 GB for multipart uploads and public URL pulls.
TikTok photo20 MB- Per photo, up to 35 photos, normalized to JPEG at 1080×1920.
YouTube video1 GB / 4 GiB- 1 GB for a public
video.urlpull; 4 GiB for a multipart upload toPOST /api/v1/posts/youtube. Base64 JSON bodies are capped by the request size. YouTube quota10,000 units/day- YouTube's daily quota belongs to Adeli's Google Cloud project and is shared by every connected channel. Reads cost 1 unit, every comment write and video edit 50, and uploads draw on a separate allowance of 100 a day. When the day's quota is spent, YouTube calls return
429 quota_exhaustedwith the reset time, midnight Pacific.
Idempotency and retries
There is no idempotency key on the public API. POST /api/v1/posts and
POST /api/v1/messages are not safe to retry blindly — a retry after a timeout
may publish twice. If a publish times out, list posts or query the TikTok or
YouTube status endpoint to find out what actually happened before trying again.
Reads are safe to retry.
What is not here
- Post editing and deletion.
PUTandDELETEon posts are deliberately not part of v1. - Outbound webhooks. Adeli does not call your server. Poll instead.
- WhatsApp free-form replies and connecting a number. The API sends WhatsApp templates; see Messages.
- Comments. Not exposed through the API.
- Scheduling.
scheduled_date,add_to_queue, andasync_uploadare rejected with422 unsupported_feature.