AthenousDevelopers

API reference

Base URL https://public-api.athenous.io. Times are ISO-8601 UTC. IDs on posts, campaigns, ad sets, and ads are UUIDs. Account ids are numbers. Channel ids are on Platforms. Walkthroughs are in the guides.

Account

These routes identify the signed-in person. Send the session cookie or Authorization: Bearer. The CLI gets that bearer token from POST /api/v1/auth/cli/token when the key includes cli. Product routes use X-Api-Key instead.

Profile

MethodPathDescription
GET/api/v1/account/meCurrent user.
PATCH/api/v1/account/meUpdate display_name, company, and role. An empty body returns the current user.
DELETE/api/v1/account/meDelete the account. Returns 204.
GET/api/v1/account/me/settingsSaved preferences object. Empty object when nothing is stored.
PUT/api/v1/account/me/settingsReplace the preferences object.
POST/api/v1/account/me/passwordBody currentPassword and newPassword. Returns 204.

GET /api/v1/account/me returns:

FieldTypeDescription
idnumberAccount id.
emailstringSign-in email.
created_atstringWhen the account was created.
display_namestring or nullName shown in the app.
companystring or nullCompany on the profile.
rolestring or nullRole label on the profile.
picture_urlstring or nullProfile image URL.
given_namestring or nullGiven name from the profile or a social sign-in.
family_namestring or nullFamily name.
localestring or nullLocale, such as en.
has_passwordbooleanTrue when the account can sign in with a password.
email_verifiedbooleanTrue after the address is verified.
mfa_enabledbooleanTrue when an authenticator is turned on.
planstringfree, starter, pro, agency, or enterprise.

API keys

Pro, Agency, and Enterprise. The raw key is returned once, on create.

MethodPathDescription
POST/api/v1/account/api-keysCreate a key. Body name (string) and scopes (string array). Returns 201.
GET/api/v1/account/api-keysList keys. The raw key is not included.
DELETE/api/v1/account/api-keys/{id}Revoke a key. Returns 204.
GET/api/v1/account/api-docs/openapi.yamlDownload the OpenAPI document.
GET/api/v1/account/api-docs/postman-collection.jsonDownload the Postman collection.
Create response fieldTypeDescription
idnumberKey id. Use it to revoke the key.
keystringRaw key, prefixed ath_. Shown only in this response.
namestringLabel. Defaults to Default.
scopestringComma-separated scopes that were accepted: cli, api, batch, athenous_post, athenous_ad.
createdAtstringWhen the key was created.

List items use id, name, scope, createdAt, and lastUsedAt.

Sessions and MFA

MethodPathDescription
GET/api/v1/account/me/sessionsSessions. Each item has id, deviceLabel, ipAddress, createdAt, lastActiveAt, and current.
DELETE/api/v1/account/me/sessions/{id}Revoke one session. Returns 204.
GET/api/v1/account/me/mfa/status{"enabled": true|false}.
POST/api/v1/account/me/mfa/setupReturns secret and qrCodeUrl.
POST/api/v1/account/me/mfa/verify-setupBody code, six digits. Returns recoveryCodes.
POST/api/v1/account/me/mfa/recovery-codes/regenerateBody password. Returns new recoveryCodes.
POST/api/v1/account/me/mfa/disableBody password. Returns 204.

Integration interest

MethodPathDescription
GET/api/v1/account/me/integration-interestsTools this account asked for.
POST/api/v1/account/me/integration-interestsBody integrationId, a roadmap id such as trello, or suggest_<name>.
DELETE/api/v1/account/me/integration-interests?integrationId=Remove one interest.

Authentication

HeaderUsed on
X-Api-Key: ath_…Posts, Ads, and Connectors. The key needs api, plus athenous_post or athenous_ad when you want to limit the product.
Authorization: BearerAccount routes, and the CLI after token exchange.
curl -sS -X POST "https://public-api.athenous.io/api/v1/auth/cli/token" \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: $ATHENOUS_API_KEY" \
  -d "{\"apiKey\": \"$ATHENOUS_API_KEY\"}"

The response field is access_token (some clients also accept token). Use it as Authorization: Bearer. A key without cli is rejected. Scheduling a draft before approve returns a 4xx whose detail is Approve this draft before it can be scheduled.

Default rate limit is 100 requests per minute per key. batch raises that to 500.

Connectors

Connections are created in the app. The API lists them so you can copy connectorId.

MethodPathDescription
GET/api/v1/connectorsList connections.
GET/api/v1/connectors/{id}One connection.
PATCH/api/v1/connectors/{id}Update identity fields, such as a Snap public profile id.
GET/api/v1/connectors/pages?platform=FACEBOOKFacebook Pages for a connection.
GET/api/v1/connectors/ad-accounts?platform=METAAd accounts for publish. Replace META with the ads platform you will use.
GET/api/v1/connectors/snapchat/public-profilesSnapchat Public Profiles. Often empty until Snap allowlists the app. A UUID can also be set with PATCH externalId.

Posts

Requires api. A key that includes athenous_ad and omits athenous_post receives 403. Field tables are in Post fields.

MethodPathDescription
POST/api/v1/postsCreate. Body: connectorId, title, body, mediaUrl, scheduledAt, channels, channelSettings.
GET/api/v1/postsPage of posts. Query page, size (1–1000), repeated status, includePublications.
GET/api/v1/posts/{id}One post.
PUT/api/v1/posts/{id}Update. Optional ?status=. A draft must be approved before it can be scheduled.
POST/api/v1/posts/{id}/approveSet approvedAt so a draft can be scheduled.
POST/api/v1/posts/{id}/publishPublish to the channels on the post.
POST/api/v1/posts/{id}/retry-failedRetry channels whose last publish failed.
DELETE/api/v1/posts/{id}Delete. Response deleted and remoteDeleteWarnings.
GET/api/v1/posts/{id}/publicationsPer-channel rows. Fields are under Publication.
POST/api/v1/posts/{id}/syncPull likes, comments, shares, impressions, views, and clicks.
POST/api/v1/posts/mediaMultipart field file. Response field mediaUrl.
POST/api/v1/posts/media/upload-sessionBody fileName, contentType, byteSize. Returns uploadUrl, mediaUrl, requiredContentType, expiresInSeconds.
GET/api/v1/posts/{id}/notesNotes on the post.
POST/api/v1/posts/{id}/notesBody body. Drafts only. 2000 characters or fewer.
DELETE/api/v1/posts/{id}/notes/{noteId}Author-only delete.
GET/api/v1/posts/quota/monthlyMonthly post quota for this account.
GET/api/v1/posts/best-timesQuery channel, tzOffsetMinutes, top.
GET/api/v1/posts/platforms/threads/recentQuery connectorId, limit, optional after.
GET/api/v1/posts/platforms/tiktok/creator-infoQuery connectorId.
GET/api/v1/posts/exportJSON export. Paid plans.
POST/api/v1/posts/importJSON import. Paid plans.
GET/api/v1/posts/export/csvCSV export. Paid plans.
POST/api/v1/posts/import/csvCSV body. Paid plans.
GET/api/v1/posts/import/templateCSV template. Paid plans.

Post status values: DRAFT, SCHEDULED, PUBLISHING, PUBLISHED, PARTIAL, FAILED, CANCELLED. Publication status values: PENDING, PUBLISHING, PUBLISHED, FAILED.

Post fields

Times are ISO-8601 UTC, for example 2026-03-20T14:00:00Z. On update, a field you omit keeps its current value.

Request body

POST /api/v1/posts and PUT /api/v1/posts/{id}. PUT also accepts ?status=.

FieldTypeRequiredDescription
connectorIdUUIDNoConnection for a single-channel post. For several channels, set connectorId inside each channelSettings entry.
titlestringNoShort title stored with the post.
bodystringNoCaption and main text sent to each channel.
mediaUrlstringNoHTTPS URL of the image or video. Upload with POST /api/v1/posts/media.
scheduledAtstringNoWhen Athenous should publish. A draft must be approved before it can move to SCHEDULED.
channelsstring[]NoChannel ids, such as linkedin and facebook. The full list is under Channels.
channelSettingsstringNoJSON object encoded as a string. Keys are channel ids. Each value holds that channel’s connectorId and options.

Response body

Returned by create, get, update, approve, and publish. List returns these objects inside content.

FieldTypeDescription
idUUIDAthenous id of the post.
connectorIdUUID or nullSingle-channel connection, when one was stored.
userIdstringAccount that owns the post.
titlestringTitle stored with the post.
bodystringCaption and main text.
mediaUrlstring or nullHosted image or video URL.
statusstringOne of the post status values. PARTIAL means at least one channel published and at least one failed.
scheduledAtstring or nullPlanned publish time.
publishedAtstring or nullTime the first channel published.
createdAtstringWhen the post was created.
updatedAtstringWhen the post was last saved.
approvedAtstring or nullWhen the draft was approved. Empty until approve. Editing title, body, media, or channels clears it.
channelsstring[]Channel ids on this post.
channelSettingsstringThe channel options stored as a JSON string.
publicationsobject[] or nullOne object per channel when includePublications=true, and on publish.
remoteUpdateWarningsobject[] or nullOn PUT, networks that rejected the edit after Athenous saved the post. Each object has channel and message.

List response

FieldTypeDescription
contentobject[]Posts on this page.
totalElementsnumberTotal posts that match the filter.
totalPagesnumberNumber of pages at the requested size.
QueryTypeDescription
pagenumberPage index, starting at 0. Default 0.
sizenumber1 to 1000. Default 20.
statusstringRepeat to filter: status=DRAFT&status=SCHEDULED.
includePublicationsbooleanWhen true, each post includes publications. Default false.

Publication

FieldTypeDescription
idUUIDId of this publication row.
postIdUUIDPost this row belongs to.
connectorIdUUIDConnection used for this channel.
channelstringChannel id, such as linkedin.
externalIdstring or nullId the network returned for the published item.
statusstringPENDING, PUBLISHING, PUBLISHED, or FAILED.
errorMessagestring or nullWhy this channel failed.
publishedAtstring or nullWhen this channel published.
syncedAtstring or nullWhen engagement numbers were last pulled.
likesnumberLike count from the last sync.
commentsnumberComment count from the last sync.
sharesnumberShare count from the last sync.
impressionsnumberImpression count from the last sync.
viewsnumberView count from the last sync.
clicksnumberClick count from the last sync.

Delete response

DELETE /api/v1/posts/{id} removes the post in Athenous and reports networks that did not remove the live item.

FieldTypeDescription
deletedbooleanTrue when the post was removed from Athenous.
remoteDeleteWarningsobject[]Each object has channel and message.

Channels and channelSettings

Use these ids in channels and as keys inside channelSettings: facebook, instagram, threads, linkedin, tiktok, twitter, youtube, snap, reddit, pinterest, discord, bluesky, mastodon, telegram, whatsapp.

Shared fields inside every channel object:

FieldTypeRequiredDescription
connectorIdUUIDYes to publishConnection that publishes this channel.
contentTypestringNoFormat for that network. Values are listed per channel.
privacystringNoWho can see the post. On LinkedIn, only_me skips publish.
tagsstring[]NoTags sent with the post.
scheduledAtstringNoISO-8601 time for this channel only. Overrides the post scheduledAt.
media_urlsstring[]NoExtra image or video URLs. Carousel limits depend on the channel.
ChannelExtra fields
facebookcontentType: feed, story, reel, live. feed resolves to text, image, video, carousel, or link carousel. Also page_id. Carousel media_urls is 2 to 10.
instagramcontentType, page_id. Carousel media_urls is 2 to 10.
linkedincontentType post resolves to text, image, or video. Optional author_urn. privacy only_me skips publish.
threadscontentType, link, alt_text, topic_tag (50 characters or fewer). Carousel media_urls is 2 to 20.
blueskyOmit contentType or send auto. link, video_url, media_urls (up to 4 images), alt_text, alt_texts.
tiktoktiktok_privacy_level (required; must match this account’s options; SELF_ONLY is Only me), tiktok_title, tiktok_allow_comment, tiktok_allow_duet, tiktok_allow_stitch (boolean, unset means off), tiktok_video_cover_timestamp_ms (number, milliseconds), tiktok_brand_content_toggle, tiktok_brand_organic_toggle, tiktok_is_aigc.
redditsubreddit (name or r/name). contentType: text, image, video, link.
pinterestboard_id, link, alt_text. Multi-image media_urls is 2 to 5.
youtubechannel_id, videoTitle (100 characters or fewer), extraAccounts as [{ connectorId, channel_id }].
discordchannel_id, forum_channel_id, forum_thread_name, embeds_json, poll_json.
telegramchat_id.
whatsappto, an E.164 phone number.
snapprofile_id, locale, skip_save_to_profile. contentType: story or spotlight.
mastodoncontentType: text.
twittercontentType: text, image, video, poll, thread.
{
  "linkedin": {
    "contentType": "post",
    "connectorId": "550e8400-e29b-41d4-a716-446655440000"
  },
  "tiktok": {
    "connectorId": "660e8400-e29b-41d4-a716-446655440001",
    "tiktok_privacy_level": "SELF_ONLY",
    "tiktok_title": "March update",
    "tiktok_allow_comment": true
  }
}

Ads

Hierarchy is campaign, then ad set, then ad. Publish in that order. A key limited to athenous_post receives 403. Paths below … continue from /api/v1/campaigns/{campaignId}.

MethodPathDescription
POST/api/v1/campaignsCreate. Returns 201. name is required.
GET/api/v1/campaignsPage. Query page, size, includeCounts.
GET/api/v1/campaigns/{id}One campaign.
PUT/api/v1/campaigns/{id}Update. Optional ?status=. Omitted fields keep their current value.
DELETE/api/v1/campaigns/{id}Delete one campaign. Returns 204.
POST/api/v1/campaigns/bulk-deleteBody {"ids":["…"]}. Response requested and deleted.
GET/api/v1/campaigns/exportJSON export. Paid plans.
POST/api/v1/campaigns/importJSON import. Paid plans. Runs asynchronously.
GET/api/v1/campaigns/export/csvCSV export. Paid plans.
POST/api/v1/campaigns/import/csvCSV body. Paid plans.
GET/api/v1/campaigns/import/templateCSV template. Paid plans.
GET/api/v1/campaigns/import/limitsImport limits for this account.
POST/api/v1/campaigns/openai-bulk-publishBody connectorId, adAccountId, campaignIds (at least two).
POST/api/v1/campaigns/{id}/openai-bulk-publishSame publish fields as a normal publish, with platform OPENAI_ADS.
POST/api/v1/campaigns/{id}/publishCreate the campaign on the ad network.
POST/api/v1/campaigns/{id}/syncPull campaign metrics.
POST/api/v1/campaigns/{id}/pushPush local changes to the network.
GET/api/v1/campaigns/{id}/publicationsPublication rows for the campaign.
POST…/ad-setsCreate an ad set. Returns 201.
GET…/ad-setsList ad sets.
GET…/ad-sets/{adSetId}One ad set.
PUT…/ad-sets/{adSetId}Update an ad set.
DELETE…/ad-sets/{adSetId}Delete an ad set. Returns 204.
POST…/ad-sets/{adSetId}/publishPublish the ad set after the campaign.
POST…/ad-sets/{adSetId}/syncPull ad set metrics.
POST…/ad-sets/{adSetId}/adsCreate an ad. Returns 201.
GET…/adsList ads. includePublications=true adds publication rows.
PUT…/ads/{adId}Update an ad.
DELETE…/ads/{adId}Delete an ad. Returns 204.
POST…/ads/{adId}/publishPublish the ad after the ad set.
POST…/ads/{adId}/syncPull ad metrics.

Campaign and ad set status: DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, ARCHIVED. Ad status: DRAFT, ACTIVE, PAUSED, ARCHIVED.

Ad fields

On update, a field you omit keeps its current value. name is required when you create.

Campaign request body

FieldTypeRequiredDescription
namestringYes1 to 255 characters.
descriptionstringNoUp to 5000 characters.
platformstringNoAd network label, such as META or LINKEDIN. Up to 128 characters.
objectiveTypestringNoPinterest objective used at publish: AWARENESS, CONSIDERATION, VIDEO_VIEW, WEB_CONVERSION, CATALOG_SALES, or VIDEO_COMPLETION. Up to 64 characters.
budgetAllocationobjectNoMap of platform name to amount, for example {"META": 5000, "LINKEDIN": 3000}.
startAtstringNoScheduled start time.
endAtstringNoScheduled end time.

Campaign response body

FieldTypeDescription
idUUIDAthenous id of the campaign.
userIdstringAccount that owns the campaign.
namestringCampaign name.
descriptionstring or nullDescription.
platformstring or nullAd network stored on the campaign.
objectiveTypestring or nullStored objective.
budgetAllocationobject or nullMap of platform name to amount.
statusstringCampaign status.
startAtstring or nullScheduled start time.
endAtstring or nullScheduled end time.
createdAtstringWhen the campaign was created.
updatedAtstringWhen the campaign was last saved.
adSetCountnumber or nullPresent when includeCounts=true.
adCountnumber or nullPresent when includeCounts=true.

GET /api/v1/campaigns adds totalConversions (sum of synced conversions for the account), totalActiveCampaigns, and statusCounts (map of each status to a count). Those three are independent of the current page.

Ad set request body

FieldTypeRequiredDescription
namestringYes1 to 255 characters.
descriptionstringNoUp to 2000 characters.
statusstringNoCampaign status values.
dailyBudgetnumberNoDaily budget amount.
startAtstringNoStart time.
endAtstringNoEnd time.
targetingJsonstringNoAudience and placement options as a JSON string. Up to 10000 characters.
objectiveTypestringNoAthenous objective, such as AWARENESS or TRAFFIC. Required when you publish a LinkedIn ad set. Up to 64 characters.

The ad set response adds id, campaignId, userId, createdAt, and updatedAt.

Ad request body

FieldTypeRequiredDescription
namestringYes1 to 255 characters.
headlinestringNoUp to 500 characters.
bodyTextstringNoPrimary text. Up to 5000 characters.
imageUrlstringNoImage URL. Up to 2048 characters.
ctaLabelstringNoCall-to-action label, such as Learn More. Up to 128 characters.
ctaLinkstringNoLanding page URL. Up to 2048 characters.
platformCreativeIdstringNoPlatform creative or asset id. Snapchat stores the creative built at publish. Spotify publish uses a READY audio or video asset id. Up to 255 characters.
statusstringNoDRAFT, ACTIVE, PAUSED, or ARCHIVED.

The ad response adds id, adSetId, campaignId, userId, createdAt, updatedAt, and publications when includePublications=true.

Ad publication

FieldTypeDescription
idUUIDId of this publication row.
adIdUUIDAd this row belongs to.
adSetIdUUIDAd set this row belongs to.
campaignIdUUIDCampaign this row belongs to.
platformstringNetwork id, such as META.
externalAdIdstring or nullId the network returned for the ad.
externalAdSetIdstring or nullId the network returned for the ad set.
statusstringPublication state on that network.
metricsobject or nullNumbers from the last sync, such as impressions and clicks.
lastSyncedAtstring or nullWhen metrics were last pulled.
createdAtstringWhen this row was created.
updatedAtstringWhen this row was last saved.

Publish and sync bodies

Every platform value, and what each network expects, is on the Ads platforms page. Worked requests are on Posts and Ads.

FieldTypeRequiredDescription
platformstringYesNetwork to publish to.
connectorIdUUIDYesAds connection from GET /api/v1/connectors.
adAccountIdstringNoPlatform ad account, such as a Meta act_… id.
facebookPageIdstringNoMeta page for link ads and page-backed creatives.
objectiveTypestringNoCampaign or ad set objective. Platform-specific.
campaignBudgetOptimizationbooleanNoPinterest campaign budget optimization.
billableEventstringNoPinterest: CLICKTHROUGH or IMPRESSION.
dailyBudgetOverridenumberNoOverride the ad set daily budget on this publish.
pinIdstringNoPinterest ad: existing pin id.
linkedinOffsiteDeliveryEnabledbooleanNoLinkedIn Audience Network.
linkedinPoliticalIntentstringNoPOLITICAL, NOT_POLITICAL, or NOT_DECLARED.
defaultTargetingCountriesstringNoComma-separated ISO country codes, such as US,DE.

Sync uses only platform and connectorId, both required.

Errors

CodeMeaning
401Missing or invalid key, or no session on an account route.
403Plan does not include API access, the key lacks api or cli, or the key is limited to the other product. A Posts key on an ads route returns {"detail":"This API key does not include the Ads scope."}. The Posts message names the Posts scope.
404Unknown id.
409Quota, duplicate publish, or a plan limit.
422Invalid body, channelSettings, or a missing connectorId.
429Rate limit. Retry after the Retry-After header.
502The external network rejected the publish. Read publications[].errorMessage.