> Markdown version of https://socialcannon.app/guides/cross-post-social-media-api (Cross-post to Facebook, Instagram, LinkedIn, TikTok, YouTube and X from one API). The index of all SocialCannon documentation is at https://socialcannon.app/llms.txt.

# Cross-post to Facebook, Instagram, LinkedIn, TikTok, YouTube and X from one API

Publish the same message to every connected account with one request body, adapt it per network, and respect each platform's media and caption rules.

Updated 23 October 2026 · 7 min read

Cross-posting sounds simple: say the same thing everywhere. In practice each network has its own API, its own sign-in, its own caption limits and its own idea of what a post needs. SocialCannon puts Facebook, Instagram, LinkedIn, TikTok, YouTube and X behind one request format, so the hard part becomes deciding what to say on each network. This guide shows both ways to cross-post and the rules that decide whether a post can go out.

## One request format for every network

Every connected account, whatever the network, takes the same body on `POST /api/v1/posts`: `accountId`, `content`, optional `mediaUrls`, optional `scheduledAt` and optional `platformOptions` for network features such as Reels or Stories. You authenticate once with your API credentials; SocialCannon holds each network’s tokens, encrypted, and refreshes them when they expire.

## Option 1: One post per account

When the same text works everywhere, send one request per account. This is the most predictable approach, because every post has its own ID, status and retry path.

Node.js 18+, saved as cross-post.mjs: post the same update to several accounts

```
// Credentials come from the environment, never from source code.
const auth = await fetch('https://socialcannon.app/api/v1/auth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'client_credentials',
    client_id: process.env.SOCIALCANNON_CLIENT_ID,
    client_secret: process.env.SOCIALCANNON_CLIENT_SECRET,
  }),
}).then((res) => res.json());
const token = auth.data?.access_token;
if (!token) throw new Error(auth.error ?? 'Authentication failed');

const accounts = ['FACEBOOK_ACCOUNT_ID', 'LINKEDIN_ACCOUNT_ID', 'INSTAGRAM_ACCOUNT_ID'];
// A fixed UTC time within the next 30 days, for example 2026-11-04T14:00:00Z. Keep it
// unchanged when you re-run the script so each Idempotency-Key replays its original post.
const scheduledAt = process.env.SCHEDULED_AT;
if (!scheduledAt) throw new Error('Set SCHEDULED_AT to a UTC time within the next 30 days');
const body = {
  content: 'We just shipped dark mode. Try it today.',
  mediaUrls: ['https://example.com/dark-mode.png'],
  scheduledAt,
};
let failed = false;

for (const accountId of accounts) {
  try {
    const res = await fetch('https://socialcannon.app/api/v1/posts', {
      method: 'POST',
      headers: {
        Authorization: 'Bearer ' + token,
        'Content-Type': 'application/json',
        // One key per post: campaign plus account. Replaying this exact request never creates a
      // second post; if you change the post or SCHEDULED_AT, change the campaign part too.
        'Idempotency-Key': 'dark-mode-launch-' + accountId,
      },
      body: JSON.stringify({ ...body, accountId }),
    });
    const json = await res.json();
    if (res.ok && json.success) {
      console.log(accountId, 'scheduled', json.data.id);
    } else {
      // Keep both: an error can come with the ID of a post that already exists.
      failed = true;
      console.error(accountId, res.status, json.code ?? '', json.error, json.data?.id ?? '');
    }
  } catch (error) {
    // No response means the outcome is unknown: re-run later to replay with the same key.
    failed = true;
    console.error(accountId, 'no response', error.message);
  }
}
if (failed) process.exitCode = 1;
```

Replace the account IDs and the example image URL with your own and set `SCHEDULED_AT`; the image must be publicly reachable. Each request succeeds or fails on its own. If Instagram rejects a post because the image is missing, Facebook and LinkedIn are unaffected. The Idempotency-Key protects the case where a response is lost: repeat the identical request with the same key and you get the original post back, or a `409` with its ID if it is still in progress or needs recovery, never a second post. When you fix and change a request, send it with a new key, because a changed body under a used key is refused with `409 IDEMPOTENCY_CONFLICT`. If a `409` includes a post ID, that post already exists; check it with `GET /api/v1/posts/POST_ID` before sending anything new.

## Option 2: Adapt and publish in one call

The same words rarely suit every network. An X post has 280 characters, a LinkedIn post can run to 3,000 and reads best with line breaks, and Instagram captions cannot contain clickable links. The repurpose endpoint rewrites one source text for each target and, in `post` mode, publishes the results to the accounts you name. Networks are named in lowercase, and X is written as `twitter`:

Adapt and publish to two networks

```
curl -X POST https://socialcannon.app/api/v1/posts/repurpose \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceContent": "We just shipped dark mode. It follows your system setting and is on for everyone today.",
    "targetPlatforms": ["linkedin", "instagram"],
    "mode": "post",
    "accountIds": {
      "linkedin": "LINKEDIN_ACCOUNT_ID",
      "instagram": "INSTAGRAM_ACCOUNT_ID"
    },
    "mediaUrls": { "instagram": ["https://example.com/dark-mode.png"] }
  }'
```

The Free plan allows two networks per repurpose request; Pro allows five. The response has one result per platform, each with its own post ID or error. Run it in the default `preview` mode first if you want to read the variants before anything is published. The [repurposing guide](https://socialcannon.app/guides/ai-content-repurposing.md) covers tone, appended text and limits.

Repurpose takes no Idempotency-Key. A reconciliation code stops retries until support resolves it. Never resend the whole request after any target published or may have published, even when none reports success. Check each result: providerPublished or reconciliationRequired, a published post status, platformPostId, platformPostUrl or a recovery object means stop and reconcile, not retry. Before retrying a missing target, follow the full repurpose recovery rule to identify any saved post or draft, including a draft whose acknowledgement was lost. Retry a failed post or delete it before replacing that target, never both; a draft cannot be retried. Any required deletion must succeed first. If the state is unclear, scopes are missing or deletion fails, contact support instead of retrying. Before you publish this content again, check whether the owner already published it as the account recovery check post: if that content is now live, do not publish it again. Follow the [full repurpose recovery rule](https://socialcannon.app/docs/errors#repurpose-recovery) before retrying.

## What each network requires

Most cross-posting failures come from a post that one network cannot accept. SocialCannon checks these rules before publishing and returns a clear error instead of sending something that would fail upstream.

| Network | Text only? | Main rules |
| --- | --- | --- |
| Facebook | Yes | Posts to a Page you manage. Text, images or video; Reels and Stories through platformOptions.mediaType. |
| Instagram | No | Needs an image or video on a professional account. Up to 10 items in a carousel. Caption up to 2,200 characters. |
| LinkedIn | Yes | Text and images. Up to 3,000 characters. |
| TikTok | No | Needs video or photos, and an explicit privacy level in platformOptions. Check allowed values with GET /api/v1/accounts/ACCOUNT_ID/tiktok/creator-info. |
| YouTube | No | Publishes video, including Shorts with platformOptions.mediaType "short". |
| X (Twitter) | Yes | Paid plans only. Up to 280 characters including links; up to 4 images. |

Media URLs must be publicly reachable. If your files are local, upload them first through the media endpoints in the [API reference](https://socialcannon.app/docs.md); each upload is checked against the limits of the account you name, so an oversized video is refused before you schedule anything.

## Plans and networks

- **Free** covers two connected accounts on Facebook, Instagram, LinkedIn, TikTok and YouTube, with unlimited posts and scheduling.
- **Pro** adds X and more connected accounts, plus analytics on Instagram, X and YouTube, A/B testing and timing suggestions.
- **Agency** and **Enterprise** are for many brands or clients.

Current prices and limits are on the [pricing page](https://socialcannon.app/pricing).

## Next steps

To post later instead of now, add `scheduledAt` to the `POST /api/v1/posts` requests in option 1. The repurpose endpoint has no schedule and publishes at once in `post` mode, so to schedule adapted text, preview it first and send each variant to `POST /api/v1/posts` with a `scheduledAt`. The [scheduling guide](https://socialcannon.app/guides/schedule-social-media-posts-api.md) covers time formats and how to change queued posts. To find out which wording works best on one network, try an [A/B test](https://socialcannon.app/guides/social-media-ab-testing-api.md).

## Frequently asked questions

**Is there one request that posts to every network?**

Two options. Send one POST /api/v1/posts per connected account with the same body, or call POST /api/v1/posts/repurpose in post mode to adapt and publish to several platforms in one call.

**Can I post text only everywhere?**

Facebook, LinkedIn and X accept text-only posts. Instagram and TikTok need media, and YouTube publishes video. Each post is checked just before it is published, so one that is missing required media is refused without affecting the others.

**How do I avoid duplicate posts when retrying?**

Send an Idempotency-Key header with each POST /api/v1/posts request, a different key for each post (for example campaign plus account). Replaying the same request with the same key never publishes again: it returns the original post, or a 409 with its ID if it is still in progress or needs recovery.

**Which networks are on the Free plan?**

Facebook, Instagram, LinkedIn, TikTok and YouTube, for up to two connected accounts. Twitter/X needs a paid plan.
