How SocialHelper makes sure a post never goes out twice
Retries are where duplicate posts come from. Here’s how the publishing workflow decides when retrying is safe.
3 minute read By SocialHelper team
A scheduler that sometimes misses a post is annoying. One that sometimes posts twice is worse: the duplicate is public, people see it, and on some platforms it can look like spam. Most duplicates come from retries. A request times out, the code assumes it failed, sends it again, and the platform had actually published the first one.
SocialHelper is built around one rule: a post never goes out twice. Here’s how the publishing workflow keeps it.
How a failure is classified
An error from a platform usually means “failed” or “worth retrying”. The case that causes duplicates is the third one, where nobody knows. A timeout after the request was sent, a dropped connection, or an error page from a proxy can each mean the post was published or wasn’t.
Each platform integration in SocialHelper reports failures in one of four ways:
- Nothing was published, try again later. Rate limits and outages. The workflow retries.
- The platform rejected the post. It’s too long, or a file is the wrong type. Retrying won’t help, so the post fails with the platform’s reason.
- The account lost access. The post fails, and the account is flagged for reconnecting.
- Anything else during the request that creates the post is treated as an unknown outcome.
Uploads and lookups that happen before the post is created can’t publish anything, so the integrations mark them as preparation. Errors there are always safe to retry.
What happens when the outcome is unknown
Where a platform lets us search an account’s recent posts, the workflow looks for the post before trying again. If it finds it, the post is marked as published with its link, and the timeline says it was found from an earlier attempt. Where a platform gives no way to check, the workflow marks the post as failed and says an earlier attempt may have been published. A failure you can see and fix beats a duplicate you can’t take back.
Some platforms help. For LinkedIn Pages, LinkedIn rejects an exact repeat and names the earlier post, which SocialHelper counts as published. Threads publishes in two steps, creating a container and then publishing it, and the integration never publishes the same container twice.
Retries are the workflow’s decision
Background jobs run once and never retry on their own. The workflow decides whether to try again, waiting 1 minute, 5 minutes and then 15 minutes, or longer when the platform asks for it with a Retry-After header (up to 60 minutes). After the last attempt the post fails with the reason.
A job can also disappear, for example when a server restarts. A scheduled task looks for posts that have been stuck for 15 minutes. A post stuck before it was sent is sent again. A post stuck while it was being sent is an unknown outcome and gets the check described above.
One worker wins
Each account in a post has its own status: scheduled, queued, publishing, confirming, published, failed or cancelled. A status only changes through a single conditional database update: “move from queued to publishing, if it’s still queued”. If two workers pick up the same post, only one update succeeds and the other worker stops. Every change is recorded on the post’s timeline, which is also what you see in the dashboard.
Retries from your side
Your own code can retry too, for example when your request to SocialHelper times out. Send an Idempotency-Key header with each new post. If the same key arrives again, SocialHelper returns the original post instead of creating another. AI assistants get the same protection through the idempotency_key field on create_post.
Checks before anything is sent
The last protection is the simplest. Before any account is queued, the whole post is checked against every target platform’s rules: text length counted the platform’s way, the number and type of images and videos, and required options. If anything doesn’t fit, nothing is posted and you get every problem at once, so a post never half-publishes and leaves you fixing one account by hand.
The idempotency docs and posting docs have the API details.