New Athenous API plan is now available. Explore plan!

Athenous REST API · v1

One key. Fifteen networks. Posts and ads.

Schedule organic posts and run paid campaigns from your backend. Authenticate with X-Api-Key on Pro, Agency, or Enterprise.

  • Basepublic-api.athenous.io
  • Organic15 networks
  • Ads6+ platforms
  • AuthX-Api-Key

OpenAPI YAML and Postman export live in Settings → API keys after you sign in.

GET /api/v1/posts
curl -sS "https://public-api.athenous.io/api/v1/posts?size=5" \
  -H "X-Api-Key: $ATHENOUS_API_KEY"

# 200
# { "content": [{ "id": "…", "status": "DRAFT" }] }

Networks

One API key across organic channels and paid ad platforms already linked in your workspace.

Organic posts · 15 networks

  • LinkedIn
  • Facebook
  • Instagram
  • X
  • Threads
  • TikTok
  • YouTube
  • Reddit
  • Pinterest
  • Bluesky
  • Snapchat
  • Discord
  • Telegram
  • WhatsApp
  • Mastodon

Paid ads

  • Meta
  • LinkedIn
  • TikTok
  • Pinterest
  • Reddit
  • Snapchat

Quickstart

Three steps from zero to a published post.

  1. Connect accounts

    Link profiles in Connections. Copy each connectorId for Posts and Ads.

  2. Create an API key

    In Settings → API keys, enable REST API and copy the key once. Add Posts (athenous_post), Ads (athenous_ad), or both to limit that key to one product.

  3. Create & publish

    POST /api/v1/posts for organic, or campaign → ad set → ad, then POST …/publish.

Authentication

Base URL
https://public-api.athenous.io
Header
X-Api-Key: ath_…
Scope
Enable REST API (api). Add athenous_post for posts, athenous_ad for ads, or both. A key with neither product scope can call both. CLI-only keys are rejected.
Rate limit
100/min per key. 500/min with Batch. Posts, campaigns, and connectors each have their own window.
curl -sS "https://public-api.athenous.io/api/v1/posts?includePublications=true" \
  -H "X-Api-Key: $ATHENOUS_API_KEY"

Connectors API (prerequisite)

List connections to obtain connectorId values used in posts and ads.

MethodPathDescription
GET/api/v1/connectorsList your connections (get connectorId, platform, purpose)
GET/api/v1/connectors/{id}Get one connector
PATCH/api/v1/connectors/{id}Update identity (Snap Posts: set externalId to Public Profile UUID)
GET/api/v1/connectors/pages?platform=FACEBOOKFacebook Pages for a connector
GET/api/v1/connectors/ad-accounts?platform=METAAd accounts for ads publish
GET/api/v1/connectors/snapchat/public-profilesSnapchat Public Profiles (often empty unless Snap allowlists your app)

Posts API — full reference

Base path /api/v1/posts.

MethodPathDescriptionBodyStatus
POST/api/v1/postsCreate postRequest body201
GET/api/v1/postsList posts (page, size, status[], includePublications)—200
GET/api/v1/posts/quota/monthlyMonthly post quota for current user—200
GET/api/v1/posts/platforms/threads/recentRecent Threads media (connectorId, limit)—200
GET/api/v1/posts/platforms/tiktok/creator-infoTikTok creator_info for UI—200
GET/api/v1/posts/{id}Get post by UUID—200
PUT/api/v1/posts/{id}Update post; ?status=DRAFT|SCHEDULED|…Request body200
DELETE/api/v1/posts/{id}Delete post and publications—204
POST/api/v1/posts/{id}/publishPublish to all channels in post—200
POST/api/v1/posts/{id}/syncPull engagement metrics per channel—200
GET/api/v1/posts/{id}/publicationsList per-channel publication rows—200
POST/api/v1/posts/mediaUpload file (multipart field file)multipart200
POST/api/v1/posts/media/upload-sessionSigned GCS direct upload session{ fileName, contentType, byteSize }200
GET/api/v1/posts/exportExport all posts JSON (paid plan)—200
POST/api/v1/posts/importImport posts JSON (paid plan)Request body201
GET/api/v1/posts/export/csvExport CSV (paid plan)—200
POST/api/v1/posts/import/csvImport CSV (paid plan)text/csv body201
GET/api/v1/posts/import/templateCSV template download (paid plan)—200

Supported channels

Use these ids in channels[] and as keys inside channelSettings.

facebookFacebook Pagefeed · story · reel · live · page_id
instagramInstagramBusiness via linked Meta Page
threadsThreadstext · image · video · carousel
linkedinLinkedInpost → text · image · video
tiktokTikTokvideo · Direct Post API
twitterX (Twitter)text · image · video · poll · thread
youtubeYouTubevideo · short · live · channel_id
snapSnapchatstory · spotlight
redditRedditsubreddit required
pinterestPinterestboard_id · pin · idea
discordDiscordchannel_id · forum
blueskyBlueskyauto embed · omit contentType
mastodonMastodontext post
telegramTelegramchat_id
whatsappWhatsAppto (E.164)

channelSettings reference

channelSettings in the request body is a JSON string (stringify before send). Keys are channel ids. Each value is an object. These fields apply inside every channel object.

FieldTypeRequiredDescription
connectorIdUUIDYes to publishConnection that publishes this channel. Copy it from GET /api/v1/connectors.
contentTypestringNoFormat for that network. Allowed values are listed per channel below.
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.

Example object

{
  "facebook": {
    "contentType": "feed",
    "connectorId": "660e8400-e29b-41d4-a716-446655440001",
    "page_id": "123456789012345",
    "privacy": "public",
    "tags": ["product", "launch"],
    "media_urls": [
      "https://storage.example/img1.jpg",
      "https://storage.example/img2.jpg"
    ]
  },
  "linkedin": {
    "contentType": "post",
    "connectorId": "550e8400-e29b-41d4-a716-446655440000",
    "author_urn": "urn:li:organization:12345",
    "privacy": "public"
  },
  "bluesky": {
    "connectorId": "770e8400-e29b-41d4-a716-446655440002"
  },
  "reddit": {
    "contentType": "text",
    "connectorId": "...",
    "subreddit": "test"
  }
}
ChannelFields
allconnectorId (UUID, required for publish), contentType, privacy, tags (string[]), scheduledAt (ISO-8601 override)
facebookcontentType: feed|story|reel|live. feed auto-resolves to text|image|video|carousel|link_carousel. page_id, media_urls (2–10 for carousel)
instagramcontentType, page_id, media_urls (carousel 2–10)
linkedincontentType: post (→ text|image|video). author_urn (optional). only_me skips publish
threadscontentType (auto), link, alt_text, topic_tag (≤50), media_urls (carousel 2–20)
blueskyomit contentType or auto. link, video_url, media_urls (≤4 images), alt_text, alt_texts[]
tiktoktiktok_privacy_level (string, required; must match this account’s options, SELF_ONLY is Only me), tiktok_title (string), 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 (boolean)
redditsubreddit (name or r/name), contentType: text|image|video|link
pinterestboard_id, link, alt_text, media_urls (2–5 multi-image)
youtubechannel_id, videoTitle (≤100), extraAccounts[{ connectorId, channel_id }]
discordchannel_id, forum_channel_id, forum_thread_name, embeds_json, poll_json
telegramchat_id
whatsappto (E.164 phone)
snapprofile_id, locale, skip_save_to_profile, contentType: story|spotlight
mastodoncontentType: text
twittercontentType: text|image|video|poll|thread

Post fields

Every field on a post. Times are ISO-8601 in UTC, for example 2026-03-20T14:00:00Z.

Request body

Sent to POST /api/v1/posts and PUT /api/v1/posts/{id}. On update, a field you omit keeps its current value. PUT also accepts ?status= with one post status.

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

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.
statusstringDRAFT, SCHEDULED, PUBLISHING, PUBLISHED, PARTIAL, FAILED, or CANCELLED. 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 POST /api/v1/posts/{id}/approve. Editing the title, body, media, or channels clears it. A draft cannot be scheduled while this is empty.
channelsstring[]Channel ids on this post.
channelSettingsstringThe channel options stored as a JSON string.
publicationsobject[] or nullOne object per channel. Included when includePublications=true, and on publish. Fields are listed under Publication.
remoteUpdateWarningsobject[] or nullPresent on PUT when a network rejected the edit after Athenous saved the post. Each object has channel (string) and message (string).
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "connectorId": null,
  "userId": "user-uuid",
  "title": "March update",
  "body": "Hello world",
  "mediaUrl": "https://storage.googleapis.com/your-bucket/media.jpg",
  "status": "DRAFT",
  "scheduledAt": "2026-03-20T14:00:00Z",
  "publishedAt": null,
  "createdAt": "2026-03-18T10:00:00Z",
  "updatedAt": "2026-03-18T10:00:00Z",
  "approvedAt": null,
  "channels": ["linkedin", "facebook"],
  "channelSettings": "{\"linkedin\":{\"contentType\":\"post\",\"connectorId\":\"...\"},\"facebook\":{\"contentType\":\"feed\",\"connectorId\":\"...\",\"page_id\":\"123\"}}",
  "publications": null,
  "remoteUpdateWarnings": null
}

List response

GET /api/v1/posts returns a page, not a single post.

FieldTypeDescription
contentobject[]Posts on this page. Each item uses the response body fields above.
totalElementsnumberTotal posts that match the filter.
totalPagesnumberNumber of pages at the requested size.
QueryTypeDescription
pagenumberPage index, starting at 0. Default 0.
sizenumberPage size from 1 to 1000. Default 20.
statusstringRepeat to filter, for example status=DRAFT&status=SCHEDULED.
includePublicationsbooleanWhen true, each post includes publications. Default false.

Publication

One object for each channel the post was sent to. Also returned by GET /api/v1/posts/{id}/publications and POST /api/v1/posts/{id}/sync.

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.
{
  "id": "pub-uuid",
  "postId": "post-uuid",
  "connectorId": "connector-uuid",
  "channel": "linkedin",
  "externalId": "urn:li:share:123",
  "status": "PUBLISHED",
  "errorMessage": null,
  "publishedAt": "2026-03-20T14:01:00Z",
  "syncedAt": "2026-03-21T09:00:00Z",
  "likes": 42,
  "comments": 5,
  "shares": 3,
  "impressions": 1200,
  "views": 0,
  "clicks": 18
}

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[]Networks that did not delete the live item. Each object has channel (string) and message (string).

Ads API — full reference

Hierarchy: Campaign → Ad set → Ad. Publish top-down.

Ad platforms

platformLabelTypical publish fields
METAMeta (Facebook / Instagram Ads)connectorId, adAccountId, facebookPageId (ads)
LINKEDINLinkedIn AdsconnectorId, adAccountId, objectiveType on ad set
TIKTOKTikTok AdsconnectorId, advertiser id; creative id for some ads
PINTERESTPinterest AdsconnectorId, adAccountId, pinId on ad publish
SNAPCHATSnapchat AdsconnectorId, ad account; creative id
REDDITReddit AdsconnectorId, adAccountId, post_id to promote
OPENAI_ADSOpenAI AdsconnectorId, adAccountId; image + CTA on ad
SPOTIFY_ADSSpotify AdsconnectorId, adAccountId; audio/video asset + image + CTA URL

Campaigns

MethodPathDescriptionBodyStatus
POST/api/v1/campaignsCreate campaignRequest body201
GET/api/v1/campaignsList (page, size, includeCounts)—200
GET/api/v1/campaigns/{id}Get campaign—200
PUT/api/v1/campaigns/{id}Update; ?status=ACTIVE|PAUSED|…Request body200
DELETE/api/v1/campaigns/{id}Delete campaign—204
DELETE/api/v1/campaigns?ids=…Bulk delete—200
POST/api/v1/campaigns/{id}/publishCreate campaign on ad platformRequest body200
POST/api/v1/campaigns/{id}/syncFetch platform metricsRequest body200
POST/api/v1/campaigns/{id}/pushPush local changes to platformRequest body200
GET/api/v1/campaigns/{id}/publicationsPlatform publication records—200
GET/api/v1/campaigns/exportExport JSON (paid)—200
POST/api/v1/campaigns/importImport JSON async (paid)Request body201

Ad sets

MethodPathDescriptionBodyStatus
POST/api/v1/campaigns/{campaignId}/ad-setsCreate ad setRequest body201
GET/api/v1/campaigns/{campaignId}/ad-setsList ad sets—200
GET…/ad-sets/{adSetId}Get ad set—200
PUT…/ad-sets/{adSetId}Update ad setRequest body200
DELETE…/ad-sets/{adSetId}Delete ad set—204
POST…/ad-sets/{adSetId}/publishPublish ad set (after campaign)Request body200
POST…/ad-sets/{adSetId}/syncSync ad set metricsRequest body200
GET…/ad-sets/{adSetId}/publicationsList publications—200

Ads

MethodPathDescriptionBodyStatus
POST…/ad-sets/{adSetId}/adsCreate ad creativeRequest body201
GET…/ads?includePublications=trueList ads—200
GET…/ads/{adId}Get ad—200
PUT…/ads/{adId}Update adRequest body200
DELETE…/ads/{adId}Delete ad—204
POST…/ads/{adId}/publishPublish ad (after ad set)Request body200
POST…/ads/{adId}/syncSync ad metricsRequest body200
GET…/ads/{adId}/publicationsList publications—200

Ad fields

Every field on a campaign, ad set, and ad. Times are ISO-8601 in UTC. On update, a field you omit keeps its current value. name is required when you create.

Campaign request body

POST /api/v1/campaigns and PUT /api/v1/campaigns/{id}. Update also accepts ?status= with a campaign status.

FieldTypeRequiredDescription
namestringYesCampaign name. 1 to 255 characters.
descriptionstringNoLonger description. Up 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 stored with the campaign.
platformstring or nullAd network stored on the campaign.
objectiveTypestring or nullStored objective, used by Pinterest publish.
budgetAllocationobject or nullMap of platform name to amount.
statusstringDRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, or ARCHIVED.
startAtstring or nullScheduled start time.
endAtstring or nullScheduled end time.
createdAtstringWhen the campaign was created.
updatedAtstringWhen the campaign was last saved.
adSetCountnumber or nullNumber of ad sets. Present when includeCounts=true.
adCountnumber or nullNumber of ads. Present when includeCounts=true.

GET /api/v1/campaigns wraps campaigns in a page. page starts at 0. size is 1 to 1000, default 20.

FieldTypeDescription
contentobject[]Campaigns on this page.
totalElementsnumberTotal campaigns for this account.
totalPagesnumberNumber of pages at the requested size.
totalConversionsnumberSum of synced conversions across this account. Independent of the current page.
totalActiveCampaignsnumberCount of campaigns whose status is ACTIVE.
statusCountsobjectMap of each campaign status to how many campaigns have it.

Ad set request body

POST /api/v1/campaigns/{campaignId}/ad-sets and PUT …/ad-sets/{adSetId}.

FieldTypeRequiredDescription
namestringYesAd set name. 1 to 255 characters.
descriptionstringNoDescription. Up to 2000 characters.
statusstringNoDRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, or ARCHIVED.
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.

Ad set response body

FieldTypeDescription
idUUIDAthenous id of the ad set.
campaignIdUUIDCampaign this ad set belongs to.
userIdstringAccount that owns the ad set.
namestringAd set name.
descriptionstring or nullDescription.
statusstringSame values as the request status.
dailyBudgetnumber or nullDaily budget amount.
startAtstring or nullStart time.
endAtstring or nullEnd time.
targetingJsonstring or nullStored targeting JSON.
objectiveTypestring or nullStored objective.
createdAtstringWhen the ad set was created.
updatedAtstringWhen the ad set was last saved.

Ad request body

POST …/ad-sets/{adSetId}/ads and PUT …/ads/{adId}.

FieldTypeRequiredDescription
namestringYesAd name. 1 to 255 characters.
headlinestringNoHeadline text. Up to 500 characters.
bodyTextstringNoPrimary text. Up to 5000 characters.
imageUrlstringNoImage URL. Up to 2048 characters.
ctaLabelstringNoCall-to-action label, such as Learn More or Sign Up. Up to 128 characters.
ctaLinkstringNoLanding page opened when someone clicks the ad. 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.

Ad response body

FieldTypeDescription
idUUIDAthenous id of the ad.
adSetIdUUIDAd set this ad belongs to.
campaignIdUUIDCampaign this ad belongs to.
userIdstringAccount that owns the ad.
namestringAd name.
headlinestring or nullHeadline text.
bodyTextstring or nullPrimary text.
imageUrlstring or nullImage URL.
ctaLabelstring or nullCall-to-action label.
ctaLinkstring or nullLanding page URL.
statusstringDRAFT, ACTIVE, PAUSED, or ARCHIVED.
createdAtstringWhen the ad was created.
updatedAtstringWhen the ad was last saved.
platformCreativeIdstring or nullPlatform creative or asset id saved after publish.
publicationsobject[] or nullOne object per network. Included when includePublications=true. Fields are listed under Ad publication.

Ad publication

One object for each network the ad, ad set, or campaign was sent to.

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 or LINKEDIN.
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 publication row was created.
updatedAtstringWhen this publication row was last saved.

Bulk delete response

POST /api/v1/campaigns/bulk-delete with {"ids": ["…"]}. ids is a required UUID array.

FieldTypeDescription
requestednumberHow many ids were sent.
deletednumberHow many campaigns were removed.

Publish request body

Fields for POST …/publish on a campaign, ad set, or ad.

FieldTypeRequiredDescription
platformstringYesMETA, LINKEDIN, TIKTOK, PINTEREST, SNAPCHAT, REDDIT, OPENAI_ADS, or SPOTIFY_ADS
connectorIdUUIDYesAds-purpose connection from GET /api/v1/connectors
adAccountIdstringNoPlatform ad account (act_… Meta, etc.)
facebookPageIdstringNoMeta link ads / page-backed creatives
objectiveTypestringNoCampaign/ad set objective (platform-specific)
campaignBudgetOptimizationbooleanNoPinterest CBO
billableEventstringNoPinterest: CLICKTHROUGH | IMPRESSION
dailyBudgetOverridenumberNoOverride ad set daily budget on publish
pinIdstringNoPinterest ad: existing pin id
linkedinOffsiteDeliveryEnabledbooleanNoLinkedIn Audience Network
linkedinPoliticalIntentstringNoPOLITICAL | NOT_POLITICAL | NOT_DECLARED
defaultTargetingCountriesstringNoComma-separated ISO country codes (e.g. US,DE)

Sync request body

Fields for POST …/sync on a campaign, ad set, or ad.

FieldTypeRequiredDescription
platformstringYesNetwork to read metrics from. Same values as publish.
connectorIdUUIDYesConnection Athenous uses to call that network.

Status enums

EntityValues
PostDRAFT, SCHEDULED, PUBLISHING, PUBLISHED, PARTIAL, FAILED, CANCELLED
PublicationPENDING, PUBLISHING, PUBLISHED, FAILED
CampaignDRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, ARCHIVED
AdSetDRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, ARCHIVED
AdDRAFT, ACTIVE, PAUSED, ARCHIVED

HTTP errors

CodeMeaning
401 UnauthorizedMissing or invalid X-Api-Key header, or revoked key.
403 ForbiddenFree/Starter plan, invalid API key, missing REST API scope, or a key that does not include this product (athenous_post or athenous_ad).
404 Not foundUnknown id or ad set not under campaign.
409 ConflictConnector quota, duplicate publish, plan limits.
422 ValidationInvalid body, channelSettings JSON, or missing connectorId.
429 Rate limitedRetry with backoff; batch scope allows higher limits.
502 Platform errorExternal network rejected publish; see publications[].errorMessage.

Code examples

Start with curl, then switch to Python or Node. Replace connector IDs and your API key.

Create a multichannel post

curl -sS -X POST "https://public-api.athenous.io/api/v1/posts" \
  -H "X-Api-Key: $ATHENOUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "API launch",
    "body": "We shipped today.",
    "mediaUrl": "https://storage.example/hero.jpg",
    "channels": ["linkedin"],
    "channelSettings": "{\"linkedin\":{\"contentType\":\"post\",\"connectorId\":\"CONNECTOR_UUID\"}}"
  }'

FAQ

How do I authenticate?

Create an API key in Settings → API Keys (Pro, Agency, or Enterprise) and send X-Api-Key: ath_… on every request. No separate token step.

Why is channelSettings a JSON string?

The request body stores channelSettings as a JSON string. Build the object in your language, then JSON.stringify before sending.

How does scheduling work?

Set scheduledAt on the post (and optionally per-channel scheduledAt in channelSettings). Athenous scheduler publishes SCHEDULED posts; or call POST …/publish immediately.

Can I publish to one channel only?

Set channels to a single id, or include only that channel in channelSettings with a valid connectorId.

Ads publish order?

Always: (1) POST campaign …/publish, (2) POST ad set …/publish, (3) POST ad …/publish. Sync with POST …/sync at any level.

Where do media URLs come from?

POST /api/v1/posts/media returns a hosted URL (GCS). Platforms fetch that URL at publish time; use upload rather than localhost links.

MCP (Claude, Cursor, ChatGPT)

Same key, natural language

Prefer agents over curl? Point Claude, Cursor, or ChatGPT at the Athenous MCP server with your existing API key.

Set up MCP →