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
| Method | Path | Description |
| GET | /api/v1/account/me | Current user. |
| PATCH | /api/v1/account/me | Update display_name, company, and role. An empty body returns the current user. |
| DELETE | /api/v1/account/me | Delete the account. Returns 204. |
| GET | /api/v1/account/me/settings | Saved preferences object. Empty object when nothing is stored. |
| PUT | /api/v1/account/me/settings | Replace the preferences object. |
| POST | /api/v1/account/me/password | Body currentPassword and newPassword. Returns 204. |
GET /api/v1/account/me returns:
| Field | Type | Description |
id | number | Account id. |
email | string | Sign-in email. |
created_at | string | When the account was created. |
display_name | string or null | Name shown in the app. |
company | string or null | Company on the profile. |
role | string or null | Role label on the profile. |
picture_url | string or null | Profile image URL. |
given_name | string or null | Given name from the profile or a social sign-in. |
family_name | string or null | Family name. |
locale | string or null | Locale, such as en. |
has_password | boolean | True when the account can sign in with a password. |
email_verified | boolean | True after the address is verified. |
mfa_enabled | boolean | True when an authenticator is turned on. |
plan | string | free, starter, pro, agency, or enterprise. |
API keys
Pro, Agency, and Enterprise. The raw key is returned once, on create.
| Method | Path | Description |
| POST | /api/v1/account/api-keys | Create a key. Body name (string) and scopes (string array). Returns 201. |
| GET | /api/v1/account/api-keys | List 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.yaml | Download the OpenAPI document. |
| GET | /api/v1/account/api-docs/postman-collection.json | Download the Postman collection. |
| Create response field | Type | Description |
id | number | Key id. Use it to revoke the key. |
key | string | Raw key, prefixed ath_. Shown only in this response. |
name | string | Label. Defaults to Default. |
scope | string | Comma-separated scopes that were accepted: cli, api, batch, athenous_post, athenous_ad. |
createdAt | string | When the key was created. |
List items use id, name, scope, createdAt, and lastUsedAt.
Sessions and MFA
| Method | Path | Description |
| GET | /api/v1/account/me/sessions | Sessions. 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/setup | Returns secret and qrCodeUrl. |
| POST | /api/v1/account/me/mfa/verify-setup | Body code, six digits. Returns recoveryCodes. |
| POST | /api/v1/account/me/mfa/recovery-codes/regenerate | Body password. Returns new recoveryCodes. |
| POST | /api/v1/account/me/mfa/disable | Body password. Returns 204. |
Integration interest
| Method | Path | Description |
| GET | /api/v1/account/me/integration-interests | Tools this account asked for. |
| POST | /api/v1/account/me/integration-interests | Body integrationId, a roadmap id such as trello, or suggest_<name>. |
| DELETE | /api/v1/account/me/integration-interests?integrationId= | Remove one interest. |
Authentication
| Header | Used 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: Bearer | Account 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.
| Method | Path | Description |
| GET | /api/v1/connectors | List 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=FACEBOOK | Facebook Pages for a connection. |
| GET | /api/v1/connectors/ad-accounts?platform=META | Ad accounts for publish. Replace META with the ads platform you will use. |
| GET | /api/v1/connectors/snapchat/public-profiles | Snapchat 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.
| Method | Path | Description |
| POST | /api/v1/posts | Create. Body: connectorId, title, body, mediaUrl, scheduledAt, channels, channelSettings. |
| GET | /api/v1/posts | Page 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}/approve | Set approvedAt so a draft can be scheduled. |
| POST | /api/v1/posts/{id}/publish | Publish to the channels on the post. |
| POST | /api/v1/posts/{id}/retry-failed | Retry channels whose last publish failed. |
| DELETE | /api/v1/posts/{id} | Delete. Response deleted and remoteDeleteWarnings. |
| GET | /api/v1/posts/{id}/publications | Per-channel rows. Fields are under Publication. |
| POST | /api/v1/posts/{id}/sync | Pull likes, comments, shares, impressions, views, and clicks. |
| POST | /api/v1/posts/media | Multipart field file. Response field mediaUrl. |
| POST | /api/v1/posts/media/upload-session | Body fileName, contentType, byteSize. Returns uploadUrl, mediaUrl, requiredContentType, expiresInSeconds. |
| GET | /api/v1/posts/{id}/notes | Notes on the post. |
| POST | /api/v1/posts/{id}/notes | Body body. Drafts only. 2000 characters or fewer. |
| DELETE | /api/v1/posts/{id}/notes/{noteId} | Author-only delete. |
| GET | /api/v1/posts/quota/monthly | Monthly post quota for this account. |
| GET | /api/v1/posts/best-times | Query channel, tzOffsetMinutes, top. |
| GET | /api/v1/posts/platforms/threads/recent | Query connectorId, limit, optional after. |
| GET | /api/v1/posts/platforms/tiktok/creator-info | Query connectorId. |
| GET | /api/v1/posts/export | JSON export. Paid plans. |
| POST | /api/v1/posts/import | JSON import. Paid plans. |
| GET | /api/v1/posts/export/csv | CSV export. Paid plans. |
| POST | /api/v1/posts/import/csv | CSV body. Paid plans. |
| GET | /api/v1/posts/import/template | CSV 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=.
| Field | Type | Required | Description |
connectorId | UUID | No | Connection for a single-channel post. For several channels, set connectorId inside each channelSettings entry. |
title | string | No | Short title stored with the post. |
body | string | No | Caption and main text sent to each channel. |
mediaUrl | string | No | HTTPS URL of the image or video. Upload with POST /api/v1/posts/media. |
scheduledAt | string | No | When Athenous should publish. A draft must be approved before it can move to SCHEDULED. |
channels | string[] | No | Channel ids, such as linkedin and facebook. The full list is under Channels. |
channelSettings | string | No | JSON 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.
| Field | Type | Description |
id | UUID | Athenous id of the post. |
connectorId | UUID or null | Single-channel connection, when one was stored. |
userId | string | Account that owns the post. |
title | string | Title stored with the post. |
body | string | Caption and main text. |
mediaUrl | string or null | Hosted image or video URL. |
status | string | One of the post status values. PARTIAL means at least one channel published and at least one failed. |
scheduledAt | string or null | Planned publish time. |
publishedAt | string or null | Time the first channel published. |
createdAt | string | When the post was created. |
updatedAt | string | When the post was last saved. |
approvedAt | string or null | When the draft was approved. Empty until approve. Editing title, body, media, or channels clears it. |
channels | string[] | Channel ids on this post. |
channelSettings | string | The channel options stored as a JSON string. |
publications | object[] or null | One object per channel when includePublications=true, and on publish. |
remoteUpdateWarnings | object[] or null | On PUT, networks that rejected the edit after Athenous saved the post. Each object has channel and message. |
List response
| Field | Type | Description |
content | object[] | Posts on this page. |
totalElements | number | Total posts that match the filter. |
totalPages | number | Number of pages at the requested size. |
| Query | Type | Description |
page | number | Page index, starting at 0. Default 0. |
size | number | 1 to 1000. Default 20. |
status | string | Repeat to filter: status=DRAFT&status=SCHEDULED. |
includePublications | boolean | When true, each post includes publications. Default false. |
Publication
| Field | Type | Description |
id | UUID | Id of this publication row. |
postId | UUID | Post this row belongs to. |
connectorId | UUID | Connection used for this channel. |
channel | string | Channel id, such as linkedin. |
externalId | string or null | Id the network returned for the published item. |
status | string | PENDING, PUBLISHING, PUBLISHED, or FAILED. |
errorMessage | string or null | Why this channel failed. |
publishedAt | string or null | When this channel published. |
syncedAt | string or null | When engagement numbers were last pulled. |
likes | number | Like count from the last sync. |
comments | number | Comment count from the last sync. |
shares | number | Share count from the last sync. |
impressions | number | Impression count from the last sync. |
views | number | View count from the last sync. |
clicks | number | Click 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.
| Field | Type | Description |
deleted | boolean | True when the post was removed from Athenous. |
remoteDeleteWarnings | object[] | 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:
| Field | Type | Required | Description |
connectorId | UUID | Yes to publish | Connection that publishes this channel. |
contentType | string | No | Format for that network. Values are listed per channel. |
privacy | string | No | Who can see the post. On LinkedIn, only_me skips publish. |
tags | string[] | No | Tags sent with the post. |
scheduledAt | string | No | ISO-8601 time for this channel only. Overrides the post scheduledAt. |
media_urls | string[] | No | Extra image or video URLs. Carousel limits depend on the channel. |
| Channel | Extra fields |
facebook | contentType: feed, story, reel, live. feed resolves to text, image, video, carousel, or link carousel. Also page_id. Carousel media_urls is 2 to 10. |
instagram | contentType, page_id. Carousel media_urls is 2 to 10. |
linkedin | contentType post resolves to text, image, or video. Optional author_urn. privacy only_me skips publish. |
threads | contentType, link, alt_text, topic_tag (50 characters or fewer). Carousel media_urls is 2 to 20. |
bluesky | Omit contentType or send auto. link, video_url, media_urls (up to 4 images), alt_text, alt_texts. |
tiktok | tiktok_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. |
reddit | subreddit (name or r/name). contentType: text, image, video, link. |
pinterest | board_id, link, alt_text. Multi-image media_urls is 2 to 5. |
youtube | channel_id, videoTitle (100 characters or fewer), extraAccounts as [{ connectorId, channel_id }]. |
discord | channel_id, forum_channel_id, forum_thread_name, embeds_json, poll_json. |
telegram | chat_id. |
whatsapp | to, an E.164 phone number. |
snap | profile_id, locale, skip_save_to_profile. contentType: story or spotlight. |
mastodon | contentType: text. |
twitter | contentType: 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}.
| Method | Path | Description |
| POST | /api/v1/campaigns | Create. Returns 201. name is required. |
| GET | /api/v1/campaigns | Page. 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-delete | Body {"ids":["…"]}. Response requested and deleted. |
| GET | /api/v1/campaigns/export | JSON export. Paid plans. |
| POST | /api/v1/campaigns/import | JSON import. Paid plans. Runs asynchronously. |
| GET | /api/v1/campaigns/export/csv | CSV export. Paid plans. |
| POST | /api/v1/campaigns/import/csv | CSV body. Paid plans. |
| GET | /api/v1/campaigns/import/template | CSV template. Paid plans. |
| GET | /api/v1/campaigns/import/limits | Import limits for this account. |
| POST | /api/v1/campaigns/openai-bulk-publish | Body connectorId, adAccountId, campaignIds (at least two). |
| POST | /api/v1/campaigns/{id}/openai-bulk-publish | Same publish fields as a normal publish, with platform OPENAI_ADS. |
| POST | /api/v1/campaigns/{id}/publish | Create the campaign on the ad network. |
| POST | /api/v1/campaigns/{id}/sync | Pull campaign metrics. |
| POST | /api/v1/campaigns/{id}/push | Push local changes to the network. |
| GET | /api/v1/campaigns/{id}/publications | Publication rows for the campaign. |
| POST | …/ad-sets | Create an ad set. Returns 201. |
| GET | …/ad-sets | List 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}/publish | Publish the ad set after the campaign. |
| POST | …/ad-sets/{adSetId}/sync | Pull ad set metrics. |
| POST | …/ad-sets/{adSetId}/ads | Create an ad. Returns 201. |
| GET | …/ads | List ads. includePublications=true adds publication rows. |
| PUT | …/ads/{adId} | Update an ad. |
| DELETE | …/ads/{adId} | Delete an ad. Returns 204. |
| POST | …/ads/{adId}/publish | Publish the ad after the ad set. |
| POST | …/ads/{adId}/sync | Pull 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
| Field | Type | Required | Description |
name | string | Yes | 1 to 255 characters. |
description | string | No | Up to 5000 characters. |
platform | string | No | Ad network label, such as META or LINKEDIN. Up to 128 characters. |
objectiveType | string | No | Pinterest objective used at publish: AWARENESS, CONSIDERATION, VIDEO_VIEW, WEB_CONVERSION, CATALOG_SALES, or VIDEO_COMPLETION. Up to 64 characters. |
budgetAllocation | object | No | Map of platform name to amount, for example {"META": 5000, "LINKEDIN": 3000}. |
startAt | string | No | Scheduled start time. |
endAt | string | No | Scheduled end time. |
Campaign response body
| Field | Type | Description |
id | UUID | Athenous id of the campaign. |
userId | string | Account that owns the campaign. |
name | string | Campaign name. |
description | string or null | Description. |
platform | string or null | Ad network stored on the campaign. |
objectiveType | string or null | Stored objective. |
budgetAllocation | object or null | Map of platform name to amount. |
status | string | Campaign status. |
startAt | string or null | Scheduled start time. |
endAt | string or null | Scheduled end time. |
createdAt | string | When the campaign was created. |
updatedAt | string | When the campaign was last saved. |
adSetCount | number or null | Present when includeCounts=true. |
adCount | number or null | Present 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
| Field | Type | Required | Description |
name | string | Yes | 1 to 255 characters. |
description | string | No | Up to 2000 characters. |
status | string | No | Campaign status values. |
dailyBudget | number | No | Daily budget amount. |
startAt | string | No | Start time. |
endAt | string | No | End time. |
targetingJson | string | No | Audience and placement options as a JSON string. Up to 10000 characters. |
objectiveType | string | No | Athenous 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
| Field | Type | Required | Description |
name | string | Yes | 1 to 255 characters. |
headline | string | No | Up to 500 characters. |
bodyText | string | No | Primary text. Up to 5000 characters. |
imageUrl | string | No | Image URL. Up to 2048 characters. |
ctaLabel | string | No | Call-to-action label, such as Learn More. Up to 128 characters. |
ctaLink | string | No | Landing page URL. Up to 2048 characters. |
platformCreativeId | string | No | Platform 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. |
status | string | No | DRAFT, ACTIVE, PAUSED, or ARCHIVED. |
The ad response adds id, adSetId, campaignId, userId, createdAt, updatedAt, and publications when includePublications=true.
Ad publication
| Field | Type | Description |
id | UUID | Id of this publication row. |
adId | UUID | Ad this row belongs to. |
adSetId | UUID | Ad set this row belongs to. |
campaignId | UUID | Campaign this row belongs to. |
platform | string | Network id, such as META. |
externalAdId | string or null | Id the network returned for the ad. |
externalAdSetId | string or null | Id the network returned for the ad set. |
status | string | Publication state on that network. |
metrics | object or null | Numbers from the last sync, such as impressions and clicks. |
lastSyncedAt | string or null | When metrics were last pulled. |
createdAt | string | When this row was created. |
updatedAt | string | When 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.
| Field | Type | Required | Description |
platform | string | Yes | Network to publish to. |
connectorId | UUID | Yes | Ads connection from GET /api/v1/connectors. |
adAccountId | string | No | Platform ad account, such as a Meta act_… id. |
facebookPageId | string | No | Meta page for link ads and page-backed creatives. |
objectiveType | string | No | Campaign or ad set objective. Platform-specific. |
campaignBudgetOptimization | boolean | No | Pinterest campaign budget optimization. |
billableEvent | string | No | Pinterest: CLICKTHROUGH or IMPRESSION. |
dailyBudgetOverride | number | No | Override the ad set daily budget on this publish. |
pinId | string | No | Pinterest ad: existing pin id. |
linkedinOffsiteDeliveryEnabled | boolean | No | LinkedIn Audience Network. |
linkedinPoliticalIntent | string | No | POLITICAL, NOT_POLITICAL, or NOT_DECLARED. |
defaultTargetingCountries | string | No | Comma-separated ISO country codes, such as US,DE. |
Sync uses only platform and connectorId, both required.
Errors
| Code | Meaning |
401 | Missing or invalid key, or no session on an account route. |
403 | Plan 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. |
404 | Unknown id. |
409 | Quota, duplicate publish, or a plan limit. |
422 | Invalid body, channelSettings, or a missing connectorId. |
429 | Rate limit. Retry after the Retry-After header. |
502 | The external network rejected the publish. Read publications[].errorMessage. |