1. Get an API key#
API keys are part of the plans that include API access (see pricing). In the SkedCast console, open Settings › Developer and create a key (Owner or Manager only). Give it the posts.compose scope at minimum: a key can never do more than your own role allows, and posts.compose is what POST /v1/posts checks. The full secret is shown once.
2. Build the request#
A post needs four things:
clientId: the client (a brand or customer in your workspace) the post belongs to.content: the caption.targets: the connected accounts to post to, as{ "mode": "accounts", "accountIds": [...] }. To post to every account of one client, use{ "mode": "group", "clientId": "..." }.schedule:{ "type": "now" },{ "type": "at", "scheduledAt": "<ISO 8601 UTC time ending in Z>" }, or{ "type": "range", "from": "...", "to": "...", "spreadMinutes": 120 }.spreadMinutes(0 to 720, optional) spreads the posts across that many minutes after the chosen time.
All ids are UUIDs. To attach media, upload or import it first, then list the returned ids in mediaIds.
3. Schedule it#
curl -X POST https://api.skedcast.com/v1/posts \
-H "Authorization: Bearer sked_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"clientId": "00000000-0000-4000-8000-000000000001",
"content": "Our autumn collection is live.",
"mediaIds": ["00000000-0000-4000-8000-000000000003"],
"targets": { "mode": "accounts", "accountIds": ["00000000-0000-4000-8000-000000000002"] },
"schedule": { "type": "at", "scheduledAt": "2030-01-15T15:00:00Z" }
}'{
"data": {
"postId": "00000000-0000-4000-8000-000000000004",
"status": "scheduled",
"targetCount": 1,
"targets": [
{
"targetId": "00000000-0000-4000-8000-000000000005",
"socialAccountId": "00000000-0000-4000-8000-000000000002",
"platform": "instagram",
"status": "scheduled",
"scheduledAt": "2030-01-15T15:00:00.000Z"
}
],
"replayed": false
},
"meta": { "correlationId": "c-…" }
}Scheduling to more than one platform at once#
One content goes to every account named in targets.accountIds, whichever platforms they belong to. When a platform needs a field the caption cannot carry (YouTube's subtitles, Pinterest's board, TikTok's privacy level), add an entry for it to variants and put the field in its overrides. The per-platform pages give the exact fields.
Scheduling many posts in one call#
POST /v1/posts/bulk takes { "items": [ ... ] } with 1 to 50 items, each the same body as POST /v1/posts, and needs an Idempotency-Key. The response is 200 with a created or failed result for each item, in request order, so one bad item does not fail the others. Send validateOnly: true to check the items without creating anything.
Checking what happened#
GET /v1/post-targets/{id} returns the destination's current status. Fields that explain it:
error: a readable message when the post did not simply publish.holdReason: while the status ison_hold, the machine-readable reason (the table below).retryAtsays when the next try is.outcomeReason: why amissedpost missed. Its time passed while it waited; reschedule it.requiredAction: what you must do (reconnect_channel,change_plan,contact_support,reschedule,retryorcheck_platform), ornullwhen nothing is needed.externalIdandpermalink: the live post, once it is published. Each platform's page says whenpermalinkis available.
Hold reasons#
holdReason | What it means |
|---|---|
platform_post_limit, platform_rate_limit, reels_daily_limit | The platform's own limit for this account is reached or it is slowing requests. The post goes out on its own when the limit resets. |
platform_app_limit, platform_outage, shared_capacity, app_daily_limit, quota_unconfirmed | The platform is temporarily not accepting this post. It goes out on its own when publishing resumes. |
daily_limit, spacing, safe_pace, another_post_publishing | SkedCast's per-account pacing is spacing the posts out. The post goes out on its own. |
media_preparing | The media is still being processed. The post goes out when it is ready. |
plan_upload_limit, x_budget | Your plan's allowance for this platform is used up. The post goes out when the allowance resets, or after you upgrade. |
plan_limit | The account is outside your plan's connected-account limit. Keep this account within the limit, or upgrade your plan. |
channel_needs_reconnect | The account must be reconnected. The post waits, does not fail, and goes out after you reconnect. |
slowed_platform_flag, slowed_failures, slowed_views | SkedCast has slowed this channel for now to protect it. The post goes out on its own at retryAt. |
channel_paused | SkedCast support paused publishing on this channel. Contact support; the post goes out once the pause is lifted. |
Poll GET /v1/post-targets/{id} for an immediate one-off answer. To avoid polling, subscribe to the target.published and target.failed webhooks (see the webhooks overview).
Common first-call errors#
| Status and code | What to do |
|---|---|
422 validation_failed | The body failed validation. detail names the field. |
400 idempotency_key_required | Send the Idempotency-Key header. |
429 rate_limited or 429 tenant_quota_exceeded | Wait the number of seconds in Retry-After, then retry. |
502, 503, 504 | Retry with backoff and the same Idempotency-Key. |
500 | The answer is kept for that key: retry with a new Idempotency-Key. |
FAQ
- What's the minimum required to schedule a post via API?
- clientId, content, targets (at least one connected account) and schedule. Media is optional unless the platform requires it.
- Can I schedule to multiple platforms in one API call?
- Yes. One POST /v1/posts call sends the same caption to every account named in targets.accountIds, whatever platforms they belong to. Platform-specific fields go in that platform's variants entry, under overrides.
- How do I schedule many posts at once via API?
- POST /v1/posts/bulk takes an items array of 1 to 50 posts, each the same body as POST /v1/posts. The response is 200 with a created or failed result for each item, in request order. Send validateOnly: true to check the items without creating anything.
- What time format does scheduledAt use?
- ISO 8601 in UTC, ending in Z, for example 2030-01-15T15:00:00Z.