Schedule and publish social posts, read your connected channels, upload media, and answer comments — from your own code.
Authenticating
Every request carries an API key in the Authorization header:
Authorization: Bearer eksis_sk_...
Create a key in Settings → API inside the workspace it should reach. A key belongs to one workspace and cannot be pointed at another, so the workspace is never part of a request.
Keys are shown once, at creation. eksis stores only a hash, so a key that is lost is replaced, not recovered. Treat one like a password: server-side only, never in a browser, an app bundle, or a repository.
Roles
A key carries a role — viewer, contributor, editor, or admin — and can never be given more access than the person who created it has. Pick the smallest one that does the job: a key that only reads analytics has no business being able to publish.
Rate limits
Quotas are counted per workspace per day, shared by every key it has.
Plan
Keys
Requests / day
Free
—
no API access
Starter
3
1,000
Premium
10
10,000
Agency
30
50,000
Every response carries x-ratelimit-limit and x-ratelimit-remaining. Once the quota is gone the API answers 429 with a retry-after header in seconds.
Errors
Failures are always JSON, always under an error key, and always carry a stable code. Branch on code, never on message — messages are written for people and get reworded.
Pagination
Collections return data and nextCursor. Pass nextCursor back as cursor to get the next page; null means you have reached the end. Cursors are timestamps of the last row, so a page never skips or repeats a row when new ones arrive mid-walk.
Versioning
The version is in the path. Breaking changes arrive as /v2; v1 keeps working. New optional fields can appear in a response at any time, so parse tolerantly.
Your first call
This proves the key works and shows which workspace it reaches.
The cheapest call that proves a key is alive and shows what it points at.
Responses
200
The workspace, the key, and its quota.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
Channels
The social accounts connected to the workspace.
get/v1/channels
List connected channels
Every connected social account, with the health of its connection. A channel whose status is not active will hold anything scheduled to it until it is reconnected in the app.
Parameters
includeDisconnectedquery
stringtrue · falseInclude channels that were disconnected. Off by default.
Responses
200
The channels of this workspace.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
Posts
Draft, schedule, publish, and inspect posts.
get/v1/posts
List posts
Newest first, by creation time.
Parameters
statusquery
stringall · draft · pending_approval · scheduled · publishing · published · failed · partialKeep only posts in this state.
channelsquery
stringComma-separated channel ids. Keeps posts aimed at any of them.
limitquery
integerRows per page. Values above 100 are clamped to 100.
cursorquery
stringThe nextCursor from the previous page, unchanged.
Responses
200
One page of posts.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
post/v1/posts
Create a post
One endpoint covers drafts, schedules, the queue, and publishing now — mode is what differs. Anything but draft is refused while a target still has a blocking validation error, and the offending checks come back in error.details.validations.
Request body
title
string | nullInternal label, never published.
baseCaption
stringUsed by every target that has no caption of its own.
baseMediaIds
string[]Media ids from POST /v1/media/uploads. Used by targets with an empty mediaIds.
targetsrequired
object[]One entry per destination channel. A post always has at least one.
socialAccountIdrequired
stringChannel id from GET /v1/channels.
contentTyperequired
stringfeed · photo · post · text · carousel · reels · video · short · story
caption
string | nullOverrides baseCaption for this channel only.
mediaIds
string[]
firstComment
string | null
settings
objectPlatform-specific settings, e.g. YouTube privacy or TikTok disclosure.
scheduledAt
string | null · date-timeRequired when mode is schedule. Interpreted as an absolute instant, so send an offset.
mode
stringdraft · schedule · now · queuedraft saves without publishing. schedule needs scheduledAt. queue takes the next free slot from the channel queue. now publishes immediately.
Responses
201
The post was created.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
get/v1/posts/{postId}
Read one post
Includes every target with its own status, permalink, and failure — this is how you learn whether publishing worked.
Parameters
postIdpathrequired
string
Responses
200
The post and its targets.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
404
No such row in this workspace.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
patch/v1/posts/{postId}
Replace a post
The body is the whole post, not a partial patch: targets you leave out are removed.
Parameters
postIdpathrequired
string
Request body
title
string | nullInternal label, never published.
baseCaption
stringUsed by every target that has no caption of its own.
baseMediaIds
string[]Media ids from POST /v1/media/uploads. Used by targets with an empty mediaIds.
targetsrequired
object[]One entry per destination channel. A post always has at least one.
socialAccountIdrequired
stringChannel id from GET /v1/channels.
contentTyperequired
stringfeed · photo · post · text · carousel · reels · video · short · story
caption
string | nullOverrides baseCaption for this channel only.
mediaIds
string[]
firstComment
string | null
settings
objectPlatform-specific settings, e.g. YouTube privacy or TikTok disclosure.
scheduledAt
string | null · date-timeRequired when mode is schedule. Interpreted as an absolute instant, so send an offset.
mode
stringdraft · schedule · now · queuedraft saves without publishing. schedule needs scheduledAt. queue takes the next free slot from the channel queue. now publishes immediately.
Responses
200
The post was replaced.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
404
No such row in this workspace.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
delete/v1/posts/{postId}
Delete a post
Only before it goes out. A published post lives on the platform, and eksis will not pretend otherwise.
Parameters
postIdpathrequired
string
Responses
200
The post was deleted.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
404
No such row in this workspace.
409
The post is already published.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
post/v1/posts/{postId}/publish
Publish now
Queues every target that has not gone out yet and returns immediately — it does not wait for the platforms. Poll GET /v1/posts/{postId} to see how each target ended up.
Parameters
postIdpathrequired
string
Responses
200
Publishing was queued.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
404
No such row in this workspace.
409
The post is already published.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
Media
Upload images and video for posts to use.
get/v1/media
List media
Parameters
typequery
stringimage · video
limitquery
integerRows per page. Values above 100 are clamped to 100.
cursorquery
stringThe nextCursor from the previous page, unchanged.
Responses
200
One page of media.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
post/v1/media/uploads
Start an upload
Uploading takes two steps. This one reserves the media id and hands back a pre-signed URL; you then PUT the bytes straight to storage and call complete. The file never passes through the API, so a large video does not sit in an API instance’s memory.
integerBytes. Checked against the plan’s storage quota.
folderId
string | null
Responses
201
Upload where uploadUrl says, with the headers given, then call complete.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
post/v1/media/{mediaId}/complete
Finish an upload
Marks the media ready so a post may use it. Send the dimensions if you know them — several platforms reject media whose aspect ratio they cannot check ahead of time.
Parameters
mediaIdpathrequired
string
Request body
width
integer
height
integer
durationMs
integerVideo only.
Responses
200
The media is ready.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
404
No such row in this workspace.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
Comments
Read and answer comments from the inbox.
get/v1/comments
List comments
Comments left by other people on your posts, newest first. Your own comments are not included.
Parameters
statusquery
stringall · open · assigned · done
channelsquery
stringComma-separated channel ids.
qquery
stringCase-insensitive substring of the comment text.
limitquery
integerRows per page. Values above 100 are clamped to 100.
cursorquery
stringThe nextCursor from the previous page, unchanged.
Responses
200
One page of comments.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
post/v1/comments/{commentId}/reply
Reply to a comment
Sends the reply to the platform right away and marks the comment done. Unlike publishing, this one does wait: a failure here comes back as a platform error rather than as a status to poll for.
Parameters
commentIdpathrequired
string
Request body
textrequired
string
Responses
200
The reply was sent.
401
The API key is missing, revoked, expired, or unknown.
402
The workspace moved to a plan that does not include the API.
403
The key’s role does not allow this.
404
No such row in this workspace.
422
The body did not validate.
429
The workspace used up its daily quota. See retry-after.
502
The platform rejected the reply.
Start on the free plan
Connect two accounts, schedule your first week, and see whether it fits. No card, no trial countdown.