Operations 5
Documentation
Documentation
https://docs.publora.com
APIReference
https://docs.publora.com/endpoints/create-post
GettingStarted
https://docs.publora.com/getting-started
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/publora:publora-media-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Publora Media API
description: 'Affordable REST API for scheduling and publishing social media posts
across X/Twitter, LinkedIn, Instagram, Threads, TikTok, YouTube,
Facebook, Bluesky, Mastodon, and Telegram.
All plans include full API access. Starting at $5.40/month (yearly) or $9/month.
14-day free trial, no credit card needed.
## Workspace API
The Workspace API allows you to manage multiple users under a single account.
**To enable Workspace access, please contact Publora support at serge@publora.com.**'
version: 1.0.0
contact:
email: serge@publora.com
url: https://publora.com
servers:
- url: https://api.publora.com/api/v1
description: Production
security:
- ApiKeyAuth: []
tags:
- name: Media
description: Upload media files
paths:
/media/{mediaId}:
parameters:
- $ref: '#/components/parameters/XPubloraClient'
delete:
summary: Delete an uploaded media file
description: 'Delete a media record and its stored object. If the media belonged to a
scheduled post, the post is demoted to draft and must be scheduled again.
Media cannot be removed while its post is publishing or after the group
has reached published or failed status.'
operationId: deleteMedia
tags:
- Media
parameters:
- name: mediaId
in: path
required: true
schema:
type: string
responses:
'200':
description: Media deleted
content:
application/json:
schema:
type: object
required:
- success
properties:
success:
type: boolean
example: true
postGroupDemoted:
type: boolean
message:
type: string
groupAlreadyMissing:
type: boolean
mediaWasOrphan:
type: boolean
thumbnailCleared:
type: boolean
coverCleared:
type: boolean
'400':
description: Invalid media ID, or media belongs to a published or failed post
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Media file not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Post is publishing or changed state concurrently; retry later
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Failed to delete media
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/get-upload-url:
parameters:
- $ref: '#/components/parameters/XPubloraClient'
post:
summary: Get pre-signed URL for media upload
description: 'Get a pre-signed S3 URL to upload images, videos, or PDF documents.
Upload the file directly to S3 via HTTP PUT, then create a post.
Upload URLs expire in 1 hour. The image count per post is
platform-specific (up to 4 for Twitter/X, Bluesky, Mastodon; up to 10
for Instagram, LinkedIn, Facebook, Telegram; up to 20 for Threads), or 1 video per
post. Attach multiple images to the same postGroupId to form a carousel;
these post-media count rules do not apply to PDF document assets.'
operationId: getUploadUrl
tags:
- Media
parameters:
- name: x-publora-user-id
in: header
required: false
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- fileName
- contentType
- postGroupId
properties:
fileName:
type: string
example: photo.jpg
contentType:
type: string
description: MIME type. It must match an image/*, video/*, or application/pdf upload.
example: image/jpeg
type:
type: string
enum:
- image
- video
- document
description: Optional media-type hint; inferred from contentType when omitted
example: image
postGroupId:
type: string
description: Post group to attach media to
example: 507f1f77bcf86cd799439011
responses:
'200':
description: Upload URL generated
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
uploadUrl:
type: string
format: uri
description: Pre-signed S3 URL. PUT your file here.
fileUrl:
type: string
format: uri
description: Public URL after upload
mediaId:
type: string
description: Media record ID
'400':
description: Missing required fields
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingFields:
value:
error: fileName, contentType, and postGroupId are required
invalidPostGroupId:
value:
error: Invalid postGroupId
unsupportedContentType:
value:
error: Only video, image, and PDF files are allowed
'401':
description: Invalid API key
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
serverError:
value:
error: Server error
/complete-media/{mediaFileId}:
parameters:
- $ref: '#/components/parameters/XPubloraClient'
post:
summary: Probe and finalize an uploaded media file
description: 'Optionally run the server-side media probe immediately after uploading
bytes to the pre-signed URL. This call is not required: scheduling also
probes media that is still uploading.'
operationId: completeMedia
tags:
- Media
parameters:
- name: mediaFileId
in: path
required: true
schema:
type: string
responses:
'200':
description: Media probe completed
content:
application/json:
schema:
type: object
required:
- success
- mediaFile
properties:
success:
type: boolean
example: true
mediaFile:
type: object
required:
- _id
properties:
_id:
type: string
type:
type:
- string
- 'null'
mimeType:
type:
- string
- 'null'
fileName:
type:
- string
- 'null'
url:
type:
- string
- 'null'
format: uri
status:
type:
- string
- 'null'
metadata:
type:
- object
- 'null'
additionalProperties: true
'400':
description: Invalid media ID or uploaded media failed validation
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Media file not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'409':
description: Media was deleted or failed concurrently while being probed
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Failed to complete media upload
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'503':
description: Media probe service or uploaded object is temporarily unavailable
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/upload-instagram-cover:
parameters:
- $ref: '#/components/parameters/XPubloraClient'
post:
summary: Upload a custom Instagram Reel cover image
description: 'Upload a cover image for an Instagram Reel. The image is stored as a
JPEG (PNG/WebP are transcoded server-side; transparency is flattened
onto white) and a public URL is returned. Set that URL as
platformSettings.instagram.coverUrl via update-post to use it as the
Reel cover. The target post group must be an editable (draft or
scheduled) post you own and not currently publishing.
Covers apply to Reels only and are ignored for Stories and images.'
operationId: uploadInstagramCover
tags:
- Media
parameters:
- name: x-publora-user-id
in: header
required: false
description: Managed user ID (workspace only)
schema:
type: string
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- cover
- postGroupId
properties:
cover:
type: string
format: binary
description: Cover image (JPEG, PNG, or WebP; 8 MB max). PNG/WebP are converted to JPEG.
postGroupId:
type: string
description: Editable post group (draft or scheduled) to attach the cover to
example: 507f1f77bcf86cd799439011
responses:
'200':
description: Cover uploaded
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
cover:
type: object
properties:
mediaId:
type: string
description: Cover media record ID
example: 665f8a1b2c3d4e5f6a7b8c9d
url:
type: string
format: uri
description: Public JPEG URL — set as platformSettings.instagram.coverUrl via update-post
example: https://media.publora.com/images/665f...-reel-cover.jpg
message:
type: string
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingPostGroupId:
value:
error: postGroupId is required
invalidPostGroupId:
value:
error: Invalid postGroupId
missingFile:
value:
error: cover file is required
badType:
value:
error: Instagram covers must be JPEG, PNG, or WebP images
tooLarge:
value:
error: Instagram covers must be 8 MB or smaller
tooLargeAfterTranscode:
value:
error: Instagram cover exceeds 8 MB after JPEG conversion; use a smaller image
nonEditable:
value:
error: Cannot upload Instagram cover for a non-editable post
'401':
description: Invalid API key
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Post group not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Post group not found
'409':
description: Post group is currently publishing
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Post group is currently being published; cannot upload Instagram cover. Retry once publishing completes.
'500':
description: Server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Failed to upload Instagram cover
/upload-youtube-thumbnail:
parameters:
- $ref: '#/components/parameters/XPubloraClient'
post:
summary: Upload a custom YouTube video thumbnail
description: 'Upload a custom thumbnail for a YouTube video post. The image is stored
as Publora-tracked media and a mediaId + url are returned; attach them
via platformSettings.youtube.thumbnail on update-post. A thumbnail
cannot be set on create-post — the upload requires an existing
postGroupId (draft or scheduled) that is not currently publishing.
JPEG or PNG, 2 MB max, minimum 640x360 px (1280x720 recommended).'
operationId: uploadYouTubeThumbnail
tags:
- Media
parameters:
- name: x-publora-user-id
in: header
required: false
description: Managed user ID (workspace only)
schema:
type: string
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- thumbnail
- postGroupId
properties:
thumbnail:
type: string
format: binary
description: Thumbnail image (JPEG or PNG; 2 MB max; min 640x360 px, 1280x720 recommended).
postGroupId:
type: string
description: Editable post group (draft or scheduled) to attach the thumbnail to
example: 507f1f77bcf86cd799439011
responses:
'200':
description: Thumbnail uploaded
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
thumbnail:
type: object
properties:
mediaId:
type: string
description: Thumbnail media record ID — set as platformSettings.youtube.thumbnail.mediaId via update-post
example: 665f8a1b2c3d4e5f6a7b8c9d
url:
type: string
format: uri
description: Public thumbnail URL — set as platformSettings.youtube.thumbnail.url via update-post
example: https://media.publora.com/images/665f...-thumb.jpg
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
missingPostGroupId:
value:
error: postGroupId is required
invalidPostGroupId:
value:
error: Invalid postGroupId
missingFile:
value:
error: thumbnail file is required
badType:
value:
error: YouTube thumbnails must be JPEG or PNG images
tooLarge:
value:
error: YouTube thumbnails must be 2 MB or smaller
tooSmall:
value:
error: YouTube thumbnails must be at least 640x360 pixels
nonEditable:
value:
error: Cannot upload YouTube thumbnail for a non-editable post
'401':
description: Invalid API key
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Post group not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Post group not found
'409':
description: Post group is currently publishing
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Post group is currently being published; cannot upload YouTube thumbnail. Retry once publishing completes.
'500':
description: Server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error: Failed to upload YouTube thumbnail
components:
schemas:
Error:
type: object
properties:
error:
type: string
description: Human-readable message. Do not match on this string — match on code where present.
example: Invalid API key
code:
type: string
description: 'Stable machine-readable error code. Present on the newer error paths
(scheduling, platformSettings validation, idempotency); older errors
return `error` only. Always prefer this over the `error` text.
'
example: SCHEDULED_TIME_IN_PAST
field:
type: string
description: '`PLATFORM_SETTING_UNKNOWN` only. The exact dotted path of the rejected
key, relative to the `platformSettings` object (no `platformSettings.`
prefix).
'
example: youtube.thumbnail.typo
serverTime:
type: string
format: date-time
description: '`SCHEDULED_TIME_IN_PAST` only. Current server time (UTC) when the
request was rejected — compare against your clock to diagnose skew.
'
example: '2026-03-01T14:02:11.412Z'
parameters:
XPubloraClient:
name: x-publora-client
in: header
required: false
description: Optional client identifier accepted by every API-key-authenticated operation. Any non-empty value is preserved; `api` is used when absent. Setting `mcp` triggers the MCP access entitlement check.
schema:
type: string
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: x-publora-key
description: 'API key from Settings > API Keys. Format: sk_timestamp.hexstring'
x-provenance:
method: harvested
publisher: Publora
source: https://docs.publora.com/openapi.json
note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json);
stamped by stamp-openapi-provenance.py (roadmap#793)