Rate Limits

Two layers of limits apply to every request: your OmniPost workspace plan, and — once a request reaches a real platform — that platform's own limits.

Plan limits

These apply per workspace, regardless of how many API keys or connected accounts you have.

PlanPosts / monthConnected accountsAPI keysRequests / minute
FREE304130
STARTER300153120
PRO2,0005010300
BUSINESS20,00025050600

"Posts / month" counts successful POST /v1/uploadcalls that create a post — one post counts once regardless of how many platforms it targets. Requests that fail validation (4xx before a post is created) don't count against it. Sandbox posts count toward the same monthly cap as live posts, since sandbox mode still exercises your workspace's infrastructure.

Exceeding the monthly post cap returns 403 plan_limit_exceeded. Exceeding connected-account or API-key caps returns the same code from POST /v1/accounts/:platform/connect or key creation, respectively. Exceeding the requests-per-minute ceiling returns 429 rate_limited.

Platform-imposed limits

Once a target reaches a real platform (live keys only), that platform's own rate limits apply on top of your OmniPost plan limit. OmniPost surfaces these as platform_rate_limited on the affected target rather than rejecting the whole request — see Errors.

PlatformPublish limitNotes
Instagram~50 published posts / 24h per accountRolling window, enforced by Meta on the connected IG account, not per app.
Threads~250 posts / 24h per profileAlso capped at 1,000 replies/24h and 100 deletions/24h on the connected profile.
TikTok~15 posts / 24h per creator (variable)TikTok describes this as a variable, shared-across-clients limit rather than a fixed number — always handle platform_rate_limited defensively instead of assuming a fixed budget. Also capped at 6 requests/min per connected account token.
X100 posts / 15 min per connected accountApp-wide ceiling of 10,000 posts/24h across all your workspace's connected X accounts combined.

Full per-platform detail, including media upload limits, lives on each platform guide.

Handling 429s

A 429 response always includes a Retry-After header in seconds. Respect it — hammering the API during a rate-limit window extends the penalty on some downstream platforms.

async function uploadWithBackoff(body, attempt = 0) {
  const res = await fetch("https://api.omnipost.dev/v1/upload", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.OMNIPOST_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(body),
  });

  if (res.status === 429 && attempt < 5) {
    const retryAfter = Number(res.headers.get("Retry-After") ?? "1");
    await new Promise((r) => setTimeout(r, retryAfter * 1000));
    return uploadWithBackoff(body, attempt + 1);
  }

  if (!res.ok) {
    const { error } = await res.json();
    throw new Error(`${error.code}: ${error.message}`);
  }

  return res.json();
}
  • Cap retries (5 is a reasonable default) and surface a hard failure rather than retrying forever.
  • For bulk publishing, spread requests out proactively rather than bursting up to the limit and relying on retries.
  • Workspace-level 429s and platform-level platform_rate_limited target errors both benefit from the same backoff strategy, but the latter doesn't block the rest of your request — only the affected target.

Need higher throughput than your current plan allows? Upgrading raises both the monthly post cap and the requests-per-minute ceiling immediately — platform-imposed limits are outside OmniPost's control and don't change with your plan.