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. id is 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.

HTTPstatusMeaning
200completeEvery attempted provider succeeded. errors is empty.
207partialAt 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:

json
{
  "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 platform and accountId to keep responses small.
Result cap10,000
The maximum number of records in one collection response. Exceeding it truncates the array and adds a result_limit entry to errors, which makes the response 207.
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.url pull; 4 GiB for a multipart upload to POST /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_exhausted with 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. PUT and DELETE on 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, and async_upload are rejected with 422 unsupported_feature.