How to schedule social media posts with one REST API
Authenticate, connect an account, and schedule posts up to 30 days ahead with one endpoint. Covers time formats, the "optimal" slot, idempotency, rescheduling and retries.
Updated · 7 min read
Most schedulers are dashboards: you paste a caption, pick a time and press a button. That works for people, but it breaks down when posts come from your own code, a content pipeline or an AI agent. Then you want scheduling to be one API call. This guide walks through the whole path with SocialCannon: get a token, find the account, schedule the post, and handle what happens afterwards.
Before you start
- A SocialCannon account. The Free plan includes unlimited posts and scheduling for two connected accounts.
- API credentials. In the dashboard under Settings → API Keys, copy your client ID from the API Credentials card. Reuse your existing secret if you have one: pressing Generate API Secret replaces it and breaks anything using the old one. A new secret is shown once, so store it straight away somewhere private, such as your server’s secret store.
- At least one connected social account. Until you have one, you can try creating a post with the built-in sandbox by using
sandboxas the account ID; it simulates an immediate publication.
Step 1: Get an access token
SocialCannon uses the OAuth 2.0 client credentials flow. Exchange your client ID and secret for a bearer token, which is valid for one hour. Request a new one when it expires.
curl -X POST https://socialcannon.app/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}'The token is in the response’s data object, as data.access_token, alongside expires_in: 3600 and the scope list the client holds. Scheduling needs posts:write, listing accounts needs accounts:read, creating connect links needs accounts:write, reading posts needs posts:read and cancelling them needs posts:delete; you can change scopes in the API Scopes card on the same tab, then request a new token, because a token keeps the scopes it was issued with. Every example below sends the token as Authorization: Bearer TOKEN.
Step 2: Find the account to post to
Each post targets one connected account. List your accounts and note the id of the one you want.
curl https://socialcannon.app/api/v1/accounts \
-H "Authorization: Bearer TOKEN"Accounts are connected in a browser, because the person who owns the account has to sign in to the social network and approve access. If you are building for your own users, call POST /api/v1/accounts/connect-token with a body such as { "platform": "linkedin", "redirectTo": "/dashboard/accounts" } (this needs the accounts:write scope; redirectTo is optional). It returns a connectUrl that is valid for ten minutes; send the user there and they come back with the account connected to your client.
Step 3: Schedule the post
Creating a post and scheduling it are the same request. Add scheduledAt and the post waits until that time; leave it out and the post publishes immediately.
curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: launch-post-2026-11-03" \
-d '{
"accountId": "ACCOUNT_ID",
"content": "Our autumn update is live: faster exports and a new calendar view.",
"mediaUrls": ["https://example.com/autumn-update.jpg"],
"scheduledAt": "2026-11-03T09:00:00Z"
}'Replace TOKEN, ACCOUNT_ID and the example image URL with your own, and set scheduledAt to a time within the next 30 days; the image must be publicly reachable. This example posts an image, which suits Facebook, Instagram, LinkedIn and X. YouTube needs video, and TikTok needs a privacy level on every post (see the platform rules below). A successful response returns the post with status: "scheduled" and its ID. A few rules keep scheduling predictable:
- Use UTC.
scheduledAtis an ISO 8601 timestamp ending inZ. Convert local times before you send them. A time in the past is rejected rather than published late. - Stay within 30 days. Posts can be scheduled up to 30 days ahead; a later
scheduledAtis rejected. - Omitting
scheduledAtpublishes now. There is no separate draft step on this endpoint, so check the field is set before you send a post meant for later. - Send an Idempotency-Key. If a response is lost, repeat the identical request with the same key: you get the original post back, or a
409with its ID if it is still in progress or needs recovery, but never a second post. If you change the request, use a new key, because a changed body under an old key is refused with409 IDEMPOTENCY_CONFLICT. A409that includes a post ID means that post already exists and may need recovery; check it withGET /api/v1/posts/POST_IDrather than creating another.
"scheduledAt": "optimal". SocialCannon then picks the best open slot in the next seven days from the account’s engagement history and your existing calendar.Step 4: Check, change or cancel
Scheduled posts stay editable until they publish.
| Task | Request |
|---|---|
| List what is queued | GET /api/v1/posts?status=scheduled |
| Read one post | GET /api/v1/posts/POST_ID |
| Move it or edit the text | PATCH /api/v1/posts/POST_ID with a new content or UTC scheduledAt timestamp |
| Turn it back into a draft | PATCH with "scheduledAt": null |
| Cancel it | DELETE /api/v1/posts/POST_ID |
Lists are paginated with a cursor: pass the createdAt of the last item you received as cursor to fetch the next page.
What happens at publish time
The scheduler checks for due posts every minute. When a post comes due, it claims the post, so it is never published twice even if two runs overlap, and sends it to the network. Busy queues or slow network processing can delay a post a little past its time. Then one of four things happens:
- It publishes, and the post records
platformPostId,platformPostUrlandpublishedAt. - A temporary problem, such as a network error, is retried automatically, five minutes apart, for up to three attempts in total. Before trying again, SocialCannon checks that the earlier attempt did not reach the network; if it cannot be sure, it stops and marks the post failed with a reason rather than risk publishing it twice; temporary problems fetching media get a longer budget of up to nine attempts. Capacity pressure and daily publishing limits never fail a post on their own; the scheduler keeps retrying more slowly, every 30 minutes for capacity and the next day for a daily limit. If those slower attempts keep being interrupted, the post is also flagged for the SocialCannon team, but it stays scheduled and keeps retrying; cancel it if you want to stop it.
- If it still cannot publish, the post is marked
failedwith a plain-English reason. Fix the cause, then callPOST /api/v1/posts/POST_ID/retry. Retry checks the earlier attempt first: if the post did reach the network after all, it is marked published without posting again, and if that is still unknown the retry is refused with a409until it is resolved. If the reason is that the post’s media is gone (MEDIA_INACCESSIBLEorSCHEDULED_MEDIA_UNAVAILABLE), upload the file again and create a new post instead, because an existing post’s media cannot be replaced. - If the account has lost access, or an earlier post on it has an outcome that is not yet known, the post is not sent. It is held with status
awaiting_accountand is never published automatically; list held posts withGET /api/v1/posts?status=awaiting_account. Retry does not apply to a held post. Once the account works again (reconnect it if its access expired) and any earlier uncertain post is settled, choose a new time withPATCHand ascheduledAtat least five minutes ahead and at least five minutes from any other scheduled post on that account, or send"scheduledAt": nullto keep it as a draft.
Plan the week, not just one post
Once posts are flowing, GET /api/v1/calendar?startDate=…&endDate=… returns the posts in that range with a summary: post counts per platform and per day, and a list of days in the range with no posts at all, whether scheduled, published or failed. It is a quick way for a script or an agent to spot gaps. On Pro, timing suggestions rank the hours when an account’s posts have performed best, and auto-scheduling spreads up to 20 posts per request across those slots over the next seven days.
Platform rules to know
| Network | What to keep in mind |
|---|---|
| Needs at least one image or video. Captions up to 2,200 characters. | |
| TikTok | Needs media, and every post must state its privacy level in platformOptions. |
| YouTube | Publishes video, including Shorts. |
| Text, images or video, published to a Page you manage. | |
| Text and images. | |
| X (Twitter) | Available on paid plans. Posts are limited to 280 characters, including links. |
Media URLs must be publicly reachable. If your files are not hosted anywhere, upload them first through the media endpoints described in the API reference.
Next steps
If an AI agent will do the scheduling, the MCP server guide shows how to give it the same abilities as tools. To send one message to several networks at once, read cross-posting from one API. Plans and limits are on the pricing page.
Frequently asked questions
- How far ahead can I schedule a post?
- Up to 30 days. A scheduledAt further out is rejected when you create or update the post, because uploaded media is only kept for a limited time.
- Which time format does scheduledAt use?
- An ISO 8601 timestamp in UTC, for example 2026-11-03T09:00:00Z. Convert local times to UTC before sending them. When you create a post on Pro and above, you can pass "optimal" instead, and SocialCannon picks a slot from your engagement history and calendar. Updates take a UTC timestamp, or null to turn the post back into a draft.
- Does scheduling cost extra on the Free plan?
- No. Free includes unlimited posts and scheduling for two connected accounts. Twitter/X publishing needs a paid plan.
- What happens if a scheduled post fails?
- Temporary failures are retried automatically before the post is marked failed. A failed post keeps a plain-English reason, and once you have fixed the cause you can retry it with POST /api/v1/posts/{id}/retry. If its media is gone, upload the file again and create a new post. If the account loses access, due posts are held as awaiting_account instead; once access is restored, give each one a new time with PATCH or save it as a draft.
Start with a free account
Free includes two connected accounts, unlimited posts and scheduling, and a sandbox for testing. Some features in this guide need Pro; the pricing page lists what each plan includes.