Repostly API · v1

Build on Repostly

Everything you schedule in the Repostly app, available from your own code, automations and AI agents. One JSON API, one key, the same rules as the app.

Base URLhttps://api.repostly.app/v1
  • Schedule posts

    Publish one post to TikTok, YouTube, Instagram, Facebook and Threads, now or at a set time.

  • Upload media

    Send a video or images once. Reuse the asset ID in as many posts as you like.

  • Automate reposting

    Create workflows that repost new or older content from one account to another.

  • Read analytics

    Views, likes, comments and followers, per post or across your accounts.

Made for automations and AI agents

Predictable JSON, stable post IDs, idempotent writes and scoped keys make the API safe to call from Zapier-style tools and LLM agents. An MCP server is planned on top of this API. It will expose the same operations, scopes and limits.

Connecting social accounts, billing and API keys stay in the Repostly app. Prefer a machine-readable contract? Download the OpenAPI document.

Get started

Quickstart

Publish your first video in five requests.

  1. Create an API key

    In the app, open Account → API keys, pick the scopes you need and copy the key. It is shown once.

    Shell
    export REPOSTLY_API_KEY='rp_live_…'
  2. Check the key

    GET /me confirms your plan, the key's scopes and your rate limits.

    Shell
    curl https://api.repostly.app/v1/me \
      -H "Authorization: Bearer $REPOSTLY_API_KEY"
    Response (abridged)
    {
      "plan": "pro",
      "key": { "name": "Zapier", "scopes": ["accounts:read", "assets:write", "posts:write"] },
      "rateLimits": { "windowSeconds": 60, "total": 300, "writes": 60 }
    }
  3. Pick a destination

    Each connected social account has an ID. Connect accounts in the app first.

    Shell
    curl https://api.repostly.app/v1/accounts \
      -H "Authorization: Bearer $REPOSTLY_API_KEY"
    # → { "data": [{ "id": "6650f1c2a4b3c2d1e0f98765", "platform": "instagram", ... }] }
  4. Upload a video

    Send the file as multipart field file (up to 100 MiB). The kind (video or image) is detected for you. For larger videos (up to 500 MiB), use a direct upload: create an upload, PUT the file to its URL, then complete it.

    Shell
    curl -X POST https://api.repostly.app/v1/assets \
      -H "Authorization: Bearer $REPOSTLY_API_KEY" \
      -F 'file=@launch.mp4'
    # → { "id": "6650f1c2a4b3c2d1e0f98766", "kind": "video", ... }
  5. Create the post

    Leave out scheduledAt to publish on the next scheduler run. The placement defaults per platform (Instagram → Reels).

    Shell
    curl -X POST https://api.repostly.app/v1/posts \
      -H "Authorization: Bearer $REPOSTLY_API_KEY" \
      -H 'Idempotency-Key: launch-post-0001' \
      -H 'Content-Type: application/json' \
      -d '{
        "caption": "Launch day!",
        "scheduledAt": "2026-11-01T09:00:00+01:00",
        "videoAssetId": "6650f1c2a4b3c2d1e0f98766",
        "destinations": [{ "accountId": "6650f1c2a4b3c2d1e0f98765" }]
      }'
  6. Track it

    Poll the post. Each destination reports its own status and, once live, its URL.

    Shell
    curl https://api.repostly.app/v1/posts/6650f1c2a4b3c2d1e0f98767 \
      -H "Authorization: Bearer $REPOSTLY_API_KEY"
    # → { "status": "scheduled", "destinations": [{ "status": "scheduled", "url": null, ... }] }

Get started

Authentication & scopes

One bearer key per integration, with only the permissions it needs.

Every request
Authorization: Bearer rp_live_…
  • Paid plans only. Starter, Pro and Expert include API access. Without a plan only GET /me works; everything else returns 403 PLAN_REQUIRED with a link to upgrade.
  • Opaque and shown once. Keys are random secrets stored as hashes. Copy the key when you create it; Repostly cannot show it again.
  • They expire. Choose a lifetime when you create the key. GET /me returns key.expiresAt.
  • Revocable at any time. Revoking blocks new requests immediately. It does not cancel posts that are already scheduled.

Scopes

A read scope never implies write. GET /me works with any scope.

ScopeAllows
accounts:readList connected accounts and read TikTok creator info
assets:readList and read uploaded media
assets:writeUpload media
posts:readList and read posts, including late posts
posts:writeCreate, replace and cancel posts; resolve late posts
workflows:readList workflows and read their activity
workflows:writeCreate, replace, pause, resume and delete workflows
analytics:readAccount analytics and per-post analytics

A key without the required scope gets 403 INSUFFICIENT_SCOPE. Each operation in the reference lists its scope.

Guides

Posts & media

Upload once, then publish one post to many accounts.

A post is one piece of content sent to one or more destinations (connected accounts). Creating a post schedules it. Its ID stays the same across every destination and every edit.

Upload assets

There are two ways to upload. Both return an asset whose ID you use as videoAssetId, thumbnailAssetId or in imageAssetIds, and uploading the same file again returns the same asset.

Direct upload (recommended for videos)

The file goes straight to storage, so large videos are fast and reliable. Videos up to 500 MiB, images up to 20 MiB (MP4, MOV, JPEG, PNG).

  1. POST/uploads with contentType, the exact sizeBytes and an optional fileName. It needs an Idempotency-Key and returns an upload object with a URL that works for one hour.
  2. PUT the file bytes to upload.url with exactly upload.headers. Don't send your API key there. A different content type or size is rejected by storage.
  3. POST/uploads/{id}/complete returns 202 while the file is checked. 409 UPLOAD_NOT_RECEIVED means the file hasn't arrived yet: retry the PUT if it failed, then complete again.
  4. Poll GET/uploads/{id} every few seconds until status is ready (with assetId and asset) or failed (with error.code and a message, for example UNSUPPORTED_MEDIA or UPLOAD_SIZE_MISMATCH).
Direct upload
FILE=launch.mp4
SIZE=$(wc -c < "$FILE" | tr -d ' ')
# 1. Create the upload session
curl -X POST https://api.repostly.app/v1/uploads \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: launch-upload-0001' \
  -H 'Content-Type: application/json' \
  -d "{\"contentType\": \"video/mp4\", \"sizeBytes\": $SIZE, \"fileName\": \"$FILE\"}"
# → { "id": "6650f1c2a4b3c2d1e0f98768", "upload": { "method": "PUT", "url": "https://…", ... } }

# 2. PUT the bytes to upload.url (no Authorization header)
UPLOAD_URL='<upload.url from step 1>'
curl -X PUT "$UPLOAD_URL" -H 'Content-Type: video/mp4' --upload-file "$FILE"

# 3. Complete, then poll until status is "ready"
curl -X POST https://api.repostly.app/v1/uploads/6650f1c2a4b3c2d1e0f98768/complete \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
curl https://api.repostly.app/v1/uploads/6650f1c2a4b3c2d1e0f98768 \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
# → { "status": "ready", "assetId": "6650f1c2a4b3c2d1e0f98766", ... }
  • Up to 3 uploads per account can wait for their file or be processing at once (409 TOO_MANY_UPLOADS). DELETE/uploads/{id} cancels one you won't finish. When you hit the limit, uploads whose file never arrived are cancelled automatically once they are old enough (UPLOAD_ABANDONED).
  • Up to 10 GiB can be declared per rolling 24 hours (429 UPLOAD_QUOTA_EXCEEDED).
  • 503 DIRECT_UPLOAD_UNAVAILABLE means direct uploads are switched off: use multipart upload instead.

Multipart upload (simple, up to 100 MiB)

POST/assets takes one multipart file part named file. The kind is detected from the file; add ?kind=image or ?kind=video to override it.

  • Up to 100 MiB per video and 20 MiB per image.
  • One upload per account at a time. A busy upload slot returns 429 with Retry-After.

Content kind is inferred

You sendKind
videoAssetId video
imageAssetIds with 1 IDimage
imageAssetIds with 2–20 IDscarousel
No assettext

Sending a video and images together returns 400.

Placements

Set placement per destination, or leave it out to get the default (bold).

PlatformVideoImageCarouselText
YouTubevideoshorts———
Instagramreelsimagecarousel—
Facebookvideoreelsimagecarousel—
Threadsvideoimagecarouseltext
TikTokvideoimagecarousel—

Create a post

POST /posts
{
  "caption": "Launch day!",
  "scheduledAt": "2026-11-01T09:00:00+01:00",
  "timezone": "Europe/Berlin",
  "videoAssetId": "6650f1c2a4b3c2d1e0f98766",
  "destinations": [
    { "accountId": "…instagram", "placement": "reels", "shareToFeed": true },
    { "accountId": "…youtube", "placement": "shorts", "title": "Launch day", "tags": ["launch"] },
    {
      "accountId": "…tiktok",
      "tiktokPrivacyLevel": "PUBLIC_TO_EVERYONE",
      "allowComments": true,
      "tiktokConsent": { "contentSharingConfirmed": true, "musicUsageConfirmed": true }
    }
  ]
}
  • scheduledAt is optional. Leave it out to publish on the next scheduler run. More than 60 seconds in the past returns 400 SCHEDULE_IN_PAST.
  • timezone (default UTC) is display context only. It never shifts scheduledAt.
  • PUT/posts/{id} replaces a pending post with a full body. Read GET/posts/{id}, change it, send it back (for TikTok destinations add fresh tiktokConsent; it is never returned). Once processing starts it returns 409.
  • DELETE/posts/{id} cancels a pending post. It never deletes content that is already live on a platform.

Destination options

Every destination needs accountId. Platform options sit next to it:

PlatformOptions
Allcaption (overrides the post caption)
YouTubetitledescriptiontagscategoryIdvisibilitynotifySubscribersselfDeclaredMadeForKidscontainsSyntheticMedia
InstagramshareToFeedthumbOffsetMsaltTextaiGenerated
FacebooktitledescriptionaltTextaiGenerated
ThreadsreplyControltopicTagaltText
TikToktiktokPrivacyLevelallowCommentsallowDuetallowStitchcommercialContentbrandedContentpromoteBrandaiContenttiktokPhotoTitletiktokAutoAddMusictiktokConsent

visibility is public, unlisted or private for YouTube video, and defaults to public, like the app. On YouTube title is the video title: at most 100 characters, without < or >. Options a platform doesn't use are rejected.

Audio is converted for you

If a destination can't accept a video's audio, Repostly converts it before publishing. There is no extra step. Failures show up in the destination's error.

TikTok: creator info and consent

  1. Call GET/accounts/{id}/tiktok-creator-info right before you show the publish form.
  2. Show the creator the privacy levels, interaction settings and maximum duration it returns, plus a preview of the content.
  3. Send their explicit choices: tiktokPrivacyLevel, allowComments/allowDuet/allowStitch, disclosure flags and tiktokConsent. Collect consent again for every create and every PUT: stored consent is never returned or reused.

Status

A post's status sums up its destinations: scheduled, publishing, published, partially_published, failed, paused or cancelled. Each destination has its own status, url, error and latest metrics.

Guides

Workflows

Repost content from one account to another, automatically.

A workflow watches a source account and reposts to a destination account. Create one with POST/workflows (an Idempotency-Key is required).

POST /workflows
{
  "sourceAccountId": "6650f1c2a4b3c2d1e0f98765",
  "destinationAccountId": "6650f1c2a4b3c2d1e0f98766",
  "sourceContent": "reels",
  "destinationContent": "shorts",
  "mode": "backlog",
  "backlog": {
    "order": "from_date",
    "startAt": "2026-01-01T00:00:00Z",
    "timezone": "Europe/Berlin",
    "postTimes": ["09:00", "18:30"]
  }
}
FieldValues
modenew_posts reposts what's published from now on. backlog works through older posts and needs backlog.
sourceContent, destinationContentreels, video, videos, shorts, text, image, carousel
backlog.orderoldest_first (default) or from_date, which starts at backlog.startAt
backlog.postTimes1–24 unique 24-hour times, such as "09:00", in backlog.timezone
tiktokDeclarationRequired when the destination is TikTok

Run and inspect

Unsupported routes and loops are rejected; a loop returns 508. You need the rights to repost the source content.

Guides

Analytics

The same numbers you see in the app.

EndpointReturns
GET/analytics/summaryTotals for views, likes, comments, followers and posts, with change vs. the previous period
GET/analytics/timeseriesOne metric over time: metric=views|likes|comments|followers, in hourly (24h), daily or weekly (all) buckets that start on boundaries in your timezone
GET/analytics/top-postsBest-performing posts, with the same IDs as /posts
GET/analytics/best-timesAudience activity: view growth by the hour of day (0–23) it was recorded, in your timezone. Use it to pick posting times; it is not grouped by when posts were published
GET/posts/{id}/analyticsEngagement over time for one post

Filter with range (24h, 7d, 30d, 365d, all; default 30d), accountId and platform. Top posts and per-post analytics accept 24h, 7d and 30d. For timeseries, endAt (default now) is aligned down to the start of its hour, day or Monday week, and that bucket is the last point.

Shell
curl 'https://api.repostly.app/v1/analytics/timeseries?metric=views&range=7d&timezone=Europe/Berlin' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response (abridged)
{
  "metric": "views",
  "range": "7d",
  "timezone": "Europe/Berlin",
  "points": [{ "at": "2026-10-26T00:00:00.000Z", "value": 1840 }],
  "coverage": { ... }
}

Conventions

Pagination & time formats

Cursor pagination

Every list returns { "data": [...], "nextCursor": "…" | null }. Pass limit (1–100, default 20) and the previous nextCursor as cursor. Treat cursors as opaque; a malformed one returns 400 INVALID_CURSOR.

Read every post
let cursor = null;
do {
  const url = new URL('https://api.repostly.app/v1/posts');
  url.searchParams.set('limit', '100');
  if (cursor) url.searchParams.set('cursor', cursor);
  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.REPOSTLY_API_KEY}` },
  });
  const page = await response.json();
  for (const post of page.data) console.log(post.id, post.status);
  cursor = page.nextCursor;
} while (cursor);

Times

InputsISO 8601 with an offset or Z (2026-11-01T09:00:00+01:00), or Unix milliseconds (1793520000000)
OutputsAlways ISO 8601 in UTC: 2026-11-01T08:00:00.000Z
timezoneAn IANA name such as Europe/Berlin. Used for display, emails and daily buckets. It never changes an absolute time.

IDs and enums

IDs are 24-character hex strings. Posts imported from a connected account use imported:<hex>. Enum values are lowercase, except values defined by a platform such as TikTok's PUBLIC_TO_EVERYONE.

Conventions

Errors

One envelope for every error. Branch on code, show message.

400 Bad Request
{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Check the highlighted fields.",
    "requestId": "9bd8d90b-3156-4c55-ad11-65b29d5c5eed",
    "details": {
      "fields": [{ "field": "destinations[0].accountId", "message": "is required" }]
    }
  }
}
StatusCodeMeaning
400INVALID_REQUESTUNKNOWN_FIELDA field is missing, malformed or not part of the schema. See details.fields.
400INVALID_CURSORSCHEDULE_IN_PASTIDEMPOTENCY_KEY_REQUIREDBad cursor, a time too far in the past, or a missing Idempotency-Key.
401INVALID_API_KEYKey missing, invalid, expired or revoked.
403PLAN_REQUIREDINSUFFICIENT_SCOPENo paid plan, or the key lacks the scope.
404—The resource doesn't exist or belongs to another account.
409IDEMPOTENCY_CONFLICTREQUEST_UNCONFIRMEDKey reused with different input, or an earlier attempt is unconfirmed. Also returned when a post is already processing.
409TOO_MANY_UPLOADSUPLOAD_NOT_RECEIVEDDirect uploads: 3 already running, or complete was called before the file arrived (retry the PUT, then complete).
413ASSET_UPLOAD_TOO_LARGEFile larger than the upload limit.
415—Wrong content type (JSON expected, or multipart for uploads).
422—Content or consent not compatible with a destination.
429RATE_LIMITEDUPLOAD_BUSYUPLOAD_QUOTA_EXCEEDEDRate limit, upload slot or daily direct-upload volume exhausted. Wait for Retry-After.
503DIRECT_UPLOAD_UNAVAILABLETemporarily unavailable. Retry with the same Idempotency-Key. DIRECT_UPLOAD_UNAVAILABLE: use multipart POST /assets.
508WORKFLOW_LOOPThe workflow would repost into itself.

New codes can appear at any time; fall back to the HTTP status for codes you don't know. Quote requestId (also sent as the X-Request-Id header) when you contact support.

Conventions

Idempotency & retries

A timeout must never become a duplicate post.

Send an Idempotency-Key header (8–128 letters, digits, . _ : -) with each logical write that accepts one.

RequiredPOST/posts, POST/workflows, POST/posts/late/resolve, POST/uploads
OptionalPUT, DELETE (including DELETE/uploads/{id} to cancel an upload), POST/uploads/{id}/complete, workflow pause and resume. Honored when sent.
Not usedPOST/assets. Uploading the same file again returns the same asset.
Kept for30 days
  • Same key, same input: the original response is replayed with Idempotency-Replayed: true.
  • Same key, different input: 409 IDEMPOTENCY_CONFLICT.
  • Earlier attempt still running or unconfirmed: 409 REQUEST_UNCONFIRMED. Check the resource before acting again.

When to retry

ResponseDo this
Timeout, network error, 5xxRetry with the same key and body, with exponential backoff. A 5xx that happened before anything changed frees the key for that retry; if the retry replays a 5xx (Idempotency-Replayed: true), the outcome is unconfirmed: read the post, workflow or upload before acting again.
429Wait Retry-After seconds, then retry with the same key.
409 REQUEST_UNCONFIRMEDRead the post, workflow or upload to see whether it was created. Don't switch to a new key.
Other 4xxFix the request. Retrying won't help.

Conventions

Rate limits

Requests per minute, per account, shared by all of its keys.

BucketStarterProExpert
All requests total120300600
Writes writes3060120
Uploads uploads61220
Analytics analytics3060120
TikTok creator info creatorInfo666
  • Windows are fixed UTC minutes. Every request counts toward total; writes, uploads, analytics and creator info also count toward their own bucket. Failed requests and replays count too.
  • Responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (epoch seconds), plus headers for the bucket used.
  • A 429 includes Retry-After in seconds. Each operation in the reference names its bucket.

Social platforms' own publishing limits apply on top of these.

Conventions

Changelog & versioning

The version is in the path: /v1. Within v1 we only make additive changes: new endpoints, new optional fields, new enum values and new error codes. Ignore fields you don't know. Breaking changes ship as a new version, announced in advance.

  1. v1 · preview

    Posts, assets (multipart and direct uploads up to 500 MiB), workflows, analytics and TikTok creator info. Cursor pagination, ISO 8601 times, required idempotency keys and one error envelope. OpenAPI document version 1.0.0.

  2. Planned

    An MCP server built on these operations, so AI agents can use Repostly with the same keys and scopes.

API reference

Every endpoint

31 operations, generated from the OpenAPI document (version 1.0.0). Examples show the shape; replace IDs and times with your own.

Account

The API key owner.

Get the API key owner

GET/me

  • Scope any
  • Rate bucket total

Your Repostly plan, this key's scopes and expiry, your per-minute rate limits and useful app links. Works with any scope and without a paid plan (plan is "none" then; every other operation returns 403 PLAN_REQUIRED — upgrade at https://app.repostly.app/upgrade).

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/me' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "userId": "6650f1c2a4b3c2d1e0f98765",
  "plan": "starter",
  "key": {
    "id": "6650f1c2a4b3c2d1e0f98766",
    "name": "My brand",
    "scopes": [
      "string"
    ],
    "expiresAt": "2026-11-01T09:00:00.000Z"
  },
  "rateLimits": {
    "windowSeconds": 60,
    "total": 1,
    "writes": 1,
    "uploads": 1,
    "analytics": 1,
    "creatorInfo": 1
  },
  "links": {
    "app": "string",
    "connectAccounts": "string",
    "upgrade": "string",
    "docs": "string"
  }
}

Accounts

Connected social accounts.

List connected accounts

GET/accounts

  • Scope accounts:read
  • Rate bucket total

Social accounts connected in the Repostly app (connect or reconnect them at links.connectAccounts from GET /me). Provider credentials are never returned.

Parameters

List connected accounts parameters
NameInDescription
platformType: stringOptionalIn: query

Only accounts on this platform.

  • One ofinstagramfacebooktiktokyoutubethreads
limitType: integerOptionalIn: query

Items per page (1–100, default 20).

  • Range 1–100
cursorType: stringOptionalIn: query

Opaque nextCursor from the previous page.

  • Length 1–512 chars

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/accounts' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "data": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "platform": "instagram",
      "name": "My brand",
      "status": "active",
      "followers": 5210,
      "pages": [
        {
          "id": "6650f1c2a4b3c2d1e0f98766",
          "name": "My brand"
        }
      ],
      "lastSyncedAt": "2026-11-01T09:00:00.000Z"
    }
  ],
  "nextCursor": "eyJwYWdlIjoyfQ"
}

Get a connected account

GET/accounts/{id}

  • Scope accounts:read
  • Rate bucket total

One connected account. pages lists selectable pages when the connection has them; otherwise it is empty.

Parameters

Get a connected account parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/accounts/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "platform": "instagram",
  "name": "My brand",
  "status": "active",
  "followers": 5210,
  "pages": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "name": "My brand"
    }
  ],
  "lastSyncedAt": "2026-11-01T09:00:00.000Z"
}

Get TikTok creator publishing options

GET/accounts/{id}/tiktok-creator-info

  • Scope accounts:read
  • Rate bucket creatorInfo

Live TikTok creator capabilities: nickname, allowed privacy levels, whether comments/duets/stitches are available and the maximum video duration. TikTok requires showing these choices and collecting explicit consent before posting; use them to fill the TikTok destination fields of POST /posts. Only for TikTok accounts.

Parameters

Get TikTok creator publishing options parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 422Content or destination not supported, or required consent missing
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/accounts/6650f1c2a4b3c2d1e0f98766/tiktok-creator-info' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "creatorNickname": "My brand",
  "creatorUsername": "My brand",
  "creatorAvatarUrl": "https://example.com/media/launch.mp4",
  "privacyLevelOptions": [
    "string"
  ],
  "commentDisabled": true,
  "duetDisabled": true,
  "stitchDisabled": true,
  "maxVideoPostDurationSec": 1,
  "checkedAt": "2026-11-01T09:00:00.000Z"
}

Assets

Uploaded videos and images.

List uploaded assets

GET/assets

  • Scope assets:read
  • Rate bucket total

Your uploaded originals, newest first.

Parameters

List uploaded assets parameters
NameInDescription
kindType: stringOptionalIn: query

Only this kind.

  • One ofvideoimage
limitType: integerOptionalIn: query

Items per page (1–100, default 20).

  • Range 1–100
cursorType: stringOptionalIn: query

Opaque nextCursor from the previous page.

  • Length 1–512 chars

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/assets' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "data": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "kind": "video",
      "sizeBytes": 48211327,
      "contentType": "video/mp4",
      "url": "https://example.com/media/launch.mp4",
      "createdAt": "2026-11-01T09:00:00.000Z",
      "width": 1080,
      "height": 1920,
      "durationSeconds": 27.5
    }
  ],
  "nextCursor": "eyJwYWdlIjoyfQ"
}

Upload a video or image

POST/assets

  • Scope assets:write
  • Rate bucket uploads

multipart/form-data with exactly one file part named file (mediaFile is accepted too). The kind is inferred from the file (JPEG/PNG → image, MP4/MOV → video); ?kind= overrides it. Limits: video 100 MiB, image 20 MiB, one upload at a time per account. Uploading the same file again returns the same asset. Remote URLs are not fetched: upload the bytes.

Parameters

Upload a video or image parameters
NameInDescription
kindType: stringOptionalIn: query

Override the inferred kind.

  • One ofvideoimage

Request body multipart/form-data

Upload a video or image request body fields
NameDescription
fileType: fileRequired

The video or image (alias: mediaFile).

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 413Upload too large
  • 415Unsupported media type
  • 422Content or destination not supported, or required consent missing
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X POST 'https://api.repostly.app/v1/assets' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -F 'file=@launch.mp4'
Response · 201 Created
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "sizeBytes": 48211327,
  "contentType": "video/mp4",
  "url": "https://example.com/media/launch.mp4",
  "createdAt": "2026-11-01T09:00:00.000Z",
  "width": 1080,
  "height": 1920,
  "durationSeconds": 27.5
}

Get an asset

GET/assets/{id}

  • Scope assets:read
  • Rate bucket total

One uploaded original.

Parameters

Get an asset parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/assets/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "sizeBytes": 48211327,
  "contentType": "video/mp4",
  "url": "https://example.com/media/launch.mp4",
  "createdAt": "2026-11-01T09:00:00.000Z",
  "width": 1080,
  "height": 1920,
  "durationSeconds": 27.5
}

Start a direct upload

POST/uploads

  • Scope assets:write
  • Rate bucket uploads

Recommended for videos (up to 500 MiB; images up to 20 MiB). Returns a one-hour presigned upload URL: PUT the file bytes there with exactly upload.headers (no Authorization header), then call POST /uploads/{id}/complete and poll GET /uploads/{id} until status is ready; asset.id is then usable as videoAssetId, thumbnailAssetId or in imageAssetIds. At most 3 uploads may be awaiting upload or processing per account (409 TOO_MANY_UPLOADS; uploads whose file never arrived are cancelled automatically once old enough, or cancel them with DELETE /uploads/{id}), and 10 GiB may be declared per rolling 24 hours (429 UPLOAD_QUOTA_EXCEEDED). 503 DIRECT_UPLOAD_UNAVAILABLE: use multipart POST /assets instead. Requires an Idempotency-Key: retry with the same key and body after a timeout; the original result is replayed (Idempotency-Replayed: true, with its Retry-After). Keys are remembered for 30 days. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Start a direct upload parameters
NameInDescription
Idempotency-KeyType: stringRequiredIn: header

Unique per logical action. Retry with the same key and body after a timeout; remembered for 30 days.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Request body application/json

Start a direct upload request body fields
NameDescription
kindType: stringOptional

Optional; inferred from contentType and must match it.

  • One ofvideoimage
contentTypeType: stringRequired

The file's MIME type. Signed into the upload URL.

  • One ofvideo/mp4video/quicktimeimage/jpegimage/png
sizeBytesType: integerRequired

Exact file size in bytes (video ≤ 524288000 = 500 MiB, image ≤ 20971520 = 20 MiB). Signed into the upload URL.

  • Range ≥ 1
fileNameType: stringOptional

Display name only (stored up to 200 characters).

  • Length ≤ 255 chars

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 413Upload too large
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X POST 'https://api.repostly.app/v1/uploads' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: createUpload-0001' \
  -H 'Content-Type: application/json' \
  -d '{
    "kind": "video",
    "contentType": "video/mp4",
    "sizeBytes": 471859200,
    "fileName": "launch.mp4"
  }'
Response · 201 Created
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "status": "awaiting_upload",
  "sizeBytes": 48211327,
  "contentType": "video/mp4",
  "fileName": "My brand",
  "createdAt": "2026-11-01T09:00:00.000Z",
  "expiresAt": "2026-11-01T09:00:00.000Z",
  "assetId": "6650f1c2a4b3c2d1e0f98766",
  "asset": {
    "id": "6650f1c2a4b3c2d1e0f98766",
    "kind": "video",
    "sizeBytes": 48211327,
    "contentType": "video/mp4",
    "url": "https://example.com/media/launch.mp4",
    "createdAt": "2026-11-01T09:00:00.000Z",
    "width": 1080,
    "height": 1920,
    "durationSeconds": 27.5
  },
  "error": {
    "code": "string",
    "message": "Human-readable explanation."
  },
  "upload": {
    "method": "PUT",
    "url": "https://example.com/media/launch.mp4",
    "headers": {
      "Content-Type": "string",
      "Content-Length": "string"
    },
    "expiresAt": "2026-11-01T09:00:00.000Z"
  },
  "maxBytes": 1
}

Get a direct upload

GET/uploads/{id}

  • Scope assets:read
  • Rate bucket total

The upload's status. Poll every few seconds while processing (verification of a 500 MiB video usually takes well under a minute); ready includes the asset, failed includes error.code and a message. Polling also resumes verification interrupted by a server restart.

Parameters

Get a direct upload parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/uploads/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "status": "awaiting_upload",
  "sizeBytes": 48211327,
  "contentType": "video/mp4",
  "fileName": "My brand",
  "createdAt": "2026-11-01T09:00:00.000Z",
  "expiresAt": "2026-11-01T09:00:00.000Z",
  "assetId": "6650f1c2a4b3c2d1e0f98766",
  "asset": {
    "id": "6650f1c2a4b3c2d1e0f98766",
    "kind": "video",
    "sizeBytes": 48211327,
    "contentType": "video/mp4",
    "url": "https://example.com/media/launch.mp4",
    "createdAt": "2026-11-01T09:00:00.000Z",
    "width": 1080,
    "height": 1920,
    "durationSeconds": 27.5
  },
  "error": {
    "code": "string",
    "message": "Human-readable explanation."
  }
}

Cancel a direct upload

DELETE/uploads/{id}

  • Scope assets:write
  • Rate bucket total

Cancels an upload that was not completed (status cancelled) so it stops counting toward the 3 concurrent uploads. Finished uploads are returned unchanged; 409 UPLOAD_PROCESSING once complete was called. Never deletes a ready asset. An Idempotency-Key is optional and honored when sent. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Cancel a direct upload parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
Idempotency-KeyType: stringOptionalIn: header

Optional. When sent, retries with the same key and input replay the first result.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X DELETE 'https://api.repostly.app/v1/uploads/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: cancelUpload-0001'
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "status": "awaiting_upload",
  "sizeBytes": 48211327,
  "contentType": "video/mp4",
  "fileName": "My brand",
  "createdAt": "2026-11-01T09:00:00.000Z",
  "expiresAt": "2026-11-01T09:00:00.000Z",
  "assetId": "6650f1c2a4b3c2d1e0f98766",
  "asset": {
    "id": "6650f1c2a4b3c2d1e0f98766",
    "kind": "video",
    "sizeBytes": 48211327,
    "contentType": "video/mp4",
    "url": "https://example.com/media/launch.mp4",
    "createdAt": "2026-11-01T09:00:00.000Z",
    "width": 1080,
    "height": 1920,
    "durationSeconds": 27.5
  },
  "error": {
    "code": "string",
    "message": "Human-readable explanation."
  }
}

Complete a direct upload

POST/uploads/{id}/complete

  • Scope assets:write
  • Rate bucket total

Call after the PUT succeeded. Starts verification (202, status processing): the size must equal sizeBytes and the content must be an MP4/MOV video or a JPEG/PNG image. Identical bytes already in your library return that asset. 409 UPLOAD_NOT_RECEIVED: the file is not in storage yet; retry the PUT if it failed, then complete again (the URL works until expiresAt). Send no body or {}. An Idempotency-Key is optional and honored when sent. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Complete a direct upload parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
Idempotency-KeyType: stringOptionalIn: header

Optional. When sent, retries with the same key and input replay the first result.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X POST 'https://api.repostly.app/v1/uploads/6650f1c2a4b3c2d1e0f98766/complete' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: completeUpload-0001'
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "status": "awaiting_upload",
  "sizeBytes": 48211327,
  "contentType": "video/mp4",
  "fileName": "My brand",
  "createdAt": "2026-11-01T09:00:00.000Z",
  "expiresAt": "2026-11-01T09:00:00.000Z",
  "assetId": "6650f1c2a4b3c2d1e0f98766",
  "asset": {
    "id": "6650f1c2a4b3c2d1e0f98766",
    "kind": "video",
    "sizeBytes": 48211327,
    "contentType": "video/mp4",
    "url": "https://example.com/media/launch.mp4",
    "createdAt": "2026-11-01T09:00:00.000Z",
    "width": 1080,
    "height": 1920,
    "durationSeconds": 27.5
  },
  "error": {
    "code": "string",
    "message": "Human-readable explanation."
  }
}

Posts

Scheduled, published and imported posts.

List posts

GET/posts

  • Scope posts:read
  • Rate bucket total

Posts created in Repostly (scheduled in the app or via the API, or by workflows) and posts imported from your connected accounts, newest scheduledAt first, with per-destination status.

Parameters

List posts parameters
NameInDescription
accountIdType: stringOptionalIn: query

Only posts with a destination on this account.

  • Pattern^[a-fA-F0-9]{24}$
kindType: stringOptionalIn: query

Only this content kind.

  • One ofvideoimagecarouseltext
fromType: string (date-time) | integerOptionalIn: query

Only posts scheduled at or after this time. ISO 8601 date-time with offset (e.g. 2026-11-01T09:00:00Z) or Unix milliseconds.

toType: string (date-time) | integerOptionalIn: query

Only posts scheduled before this time. ISO 8601 date-time with offset (e.g. 2026-11-01T09:00:00Z) or Unix milliseconds.

limitType: integerOptionalIn: query

Items per page (1–100, default 20).

  • Range 1–100
cursorType: stringOptionalIn: query

Opaque nextCursor from the previous page.

  • Length 1–512 chars

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/posts' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "data": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "kind": "video",
      "caption": "Launch day!",
      "status": "scheduled",
      "scheduledAt": "2026-11-01T09:00:00.000Z",
      "timezone": "Europe/Berlin",
      "origin": "schedule",
      "workflowId": "6650f1c2a4b3c2d1e0f98765",
      "videoAssetId": "6650f1c2a4b3c2d1e0f98765",
      "thumbnailAssetId": "6650f1c2a4b3c2d1e0f98766",
      "imageAssetIds": [
        "6650f1c2a4b3c2d1e0f98768"
      ],
      "thumbnailUrl": "https://example.com/media/launch.mp4",
      "editable": true,
      "destinations": [
        {
          "id": "6650f1c2a4b3c2d1e0f98766",
          "accountId": "6650f1c2a4b3c2d1e0f98767",
          "accountName": "My brand",
          "platform": "instagram",
          "placement": "string",
          "status": "scheduled",
          "url": "https://example.com/media/launch.mp4",
          "publishedAt": "2026-11-01T09:00:00.000Z",
          "providerPostIds": [
            "6650f1c2a4b3c2d1e0f98766"
          ],
          "error": {
            "code": "string",
            "message": "Human-readable explanation."
          },
          "metrics": {
            "views": 1840,
            "likes": 132,
            "comments": 18,
            "observedAt": "2026-11-01T09:00:00.000Z"
          },
          "options": {
            "caption": "Launch day!",
            "title": "Launch day",
            "description": "string",
            "visibility": "public",
            "tags": [
              "string"
            ],
            "categoryId": "6650f1c2a4b3c2d1e0f98768",
            "selfDeclaredMadeForKids": true,
            "containsSyntheticMedia": true,
            "notifySubscribers": true,
            "shareToFeed": true,
            "thumbOffsetMs": 0,
            "altText": "string",
            "aiGenerated": true,
            "replyControl": "everyone",
            "topicTag": "string",
            "tiktokPrivacyLevel": "PUBLIC_TO_EVERYONE",
            "allowComments": true,
            "allowDuet": true,
            "allowStitch": true,
            "commercialContent": true,
            "brandedContent": true,
            "promoteBrand": true,
            "aiContent": true,
            "tiktokPhotoTitle": "string",
            "tiktokAutoAddMusic": true
          }
        }
      ]
    }
  ],
  "nextCursor": "eyJwYWdlIjoyfQ"
}

Create (schedule) a post

POST/posts

  • Scope posts:write
  • Rate bucket writes

Schedules one piece of content to one or more connected accounts. The kind is inferred: videoAssetId → video, one image → image, 2–20 images → carousel, neither → text. Omit scheduledAt to publish on the next scheduler run. Audio a destination cannot accept is converted automatically before publishing. Publishing is asynchronous: watch each destination's status. Requires an Idempotency-Key: retry with the same key and body after a timeout; the original result is replayed (Idempotency-Replayed: true, with its Retry-After). Keys are remembered for 30 days. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Create (schedule) a post parameters
NameInDescription
Idempotency-KeyType: stringRequiredIn: header

Unique per logical action. Retry with the same key and body after a timeout; remembered for 30 days.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Request body application/json

Create (schedule) a post request body fields
NameDescription
captionType: stringOptional

Shared caption. Required for text posts.

  • Length ≤ 4000 chars
scheduledAtType: string (date-time) | integerOptional

When to publish. On create, omit it (or send a time up to 60 s in the past) to publish on the next scheduler run. On replace (PUT), omit it to keep the post's current scheduled time. ISO 8601 date-time with offset (e.g. 2026-11-01T09:00:00Z) or Unix milliseconds.

timezoneType: stringOptional

IANA time zone for display and email context (default UTC). It never reinterprets scheduledAt.

  • Length 1–64 chars
videoAssetIdType: stringOptional

Uploaded video asset (makes this a video post).

  • Pattern^[a-fA-F0-9]{24}$
thumbnailAssetIdType: stringOptional

Optional uploaded image asset used as the video thumbnail.

  • Pattern^[a-fA-F0-9]{24}$
imageAssetIdsType: string[]Optional

Uploaded image assets: one → image post, 2–20 → carousel.

  • Items ≤ 20 items
  • Unique items
destinationsType: object[]Required
  • Items 1–50 items
destinations[].accountIdType: stringRequired

Connected account ID from GET /accounts.

  • Pattern^[a-fA-F0-9]{24}$
destinations[].placementType: stringOptional

Where the content appears. Defaults: video posts → youtube video, instagram reels, facebook video, threads video, tiktok video; image → image; carousel → carousel; text → text.

  • One ofvideoshortsreelsimagecarouseltext
destinations[].captionType: stringOptional

Caption for this destination only (overrides the post caption).

  • Length ≤ 4000 chars
destinations[].titleType: stringOptional

YouTube: the video title, at most 100 characters without < or > (YouTube uses the caption as title). Facebook video: the video title (at most 200).

  • Length ≤ 200 chars
destinations[].descriptionType: stringOptional

YouTube and Facebook video description.

  • Length ≤ 5000 chars
destinations[].visibilityType: stringOptional

YouTube video visibility. Default public.

  • One ofpublicunlistedprivate
destinations[].tagsType: string[]Optional

YouTube tags.

  • Items ≤ 100 items
destinations[].categoryIdType: stringOptional

YouTube category ID.

  • Length ≤ 3 chars
destinations[].selfDeclaredMadeForKidsType: booleanOptional

YouTube: made for kids.

destinations[].containsSyntheticMediaType: booleanOptional

YouTube: altered or synthetic content disclosure.

destinations[].notifySubscribersType: booleanOptional

YouTube: notify subscribers.

destinations[].shareToFeedType: booleanOptional

Instagram Reels: also share to the feed.

destinations[].thumbOffsetMsType: integerOptional

Instagram Reels: cover frame offset in milliseconds.

  • Range 0–86400000
destinations[].altTextType: stringOptional

Image alt text (Instagram, Facebook, Threads).

  • Length ≤ 1000 chars
destinations[].aiGeneratedType: booleanOptional

Instagram / Facebook Reels: AI-generated label.

destinations[].replyControlType: stringOptional

Threads reply control.

  • One ofeveryoneaccounts_you_followmentioned_onlyparent_post_author_onlyfollowers_only
destinations[].topicTagType: stringOptional

Threads topic tag.

  • Length ≤ 50 chars
destinations[].tiktokPrivacyLevelType: stringOptional

TikTok privacy level; must be one of privacyLevelOptions from tiktok-creator-info.

  • One ofPUBLIC_TO_EVERYONEMUTUAL_FOLLOW_FRIENDSFOLLOWER_OF_CREATORSELF_ONLY
destinations[].allowCommentsType: booleanOptional

TikTok: allow comments (explicit choice required).

destinations[].allowDuetType: booleanOptional

TikTok video: allow duets (explicit choice required).

destinations[].allowStitchType: booleanOptional

TikTok video: allow stitches (explicit choice required).

destinations[].commercialContentType: booleanOptional

TikTok: commercial content disclosure; true exactly when brandedContent or promoteBrand is true.

destinations[].brandedContentType: booleanOptional

TikTok: branded content (paid partnership).

destinations[].promoteBrandType: booleanOptional

TikTok: promotes your own brand.

destinations[].aiContentType: booleanOptional

TikTok: AI-generated content label.

destinations[].tiktokPhotoTitleType: stringOptional

TikTok photo posts: title (required for photo posts).

  • Length ≤ 90 chars
destinations[].tiktokAutoAddMusicType: booleanOptional

TikTok photo posts: let TikTok add music (required for photo posts).

destinations[].tiktokConsentType: objectOptional

Explicit TikTok creator consent collected in your UI for this post. Required for TikTok destinations on every create and every replace (PUT); GET never returns stored consent.

destinations[].tiktokConsent.contentSharingConfirmedType: booleanRequired

The creator confirmed TikTok's content sharing terms.

destinations[].tiktokConsent.musicUsageConfirmedType: booleanRequired

The creator confirmed TikTok's music usage confirmation.

destinations[].tiktokConsent.brandedContentPolicyConfirmedType: booleanOptional

Required when brandedContent is true.

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 422Content or destination not supported, or required consent missing
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X POST 'https://api.repostly.app/v1/posts' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: createPost-0001' \
  -H 'Content-Type: application/json' \
  -d '{
    "caption": "Launch day!",
    "scheduledAt": "2026-11-01T09:00:00+01:00",
    "timezone": "Europe/Berlin",
    "videoAssetId": "64b7f0c2a1e4d3b2c1f0e9d8",
    "destinations": [
      {
        "accountId": "507f1f77bcf86cd799439011",
        "placement": "reels",
        "caption": "IG-specific caption",
        "shareToFeed": true
      },
      {
        "accountId": "507f1f77bcf86cd799439012",
        "placement": "shorts",
        "title": "Launch day",
        "visibility": "public",
        "tags": [
          "launch"
        ]
      },
      {
        "accountId": "507f1f77bcf86cd799439013",
        "tiktokPrivacyLevel": "PUBLIC_TO_EVERYONE",
        "allowComments": true,
        "allowDuet": false,
        "allowStitch": false,
        "commercialContent": false,
        "brandedContent": false,
        "promoteBrand": false,
        "aiContent": false,
        "tiktokConsent": {
          "contentSharingConfirmed": true,
          "musicUsageConfirmed": true
        }
      }
    ]
  }'
Response · 201 Created
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "caption": "Launch day!",
  "status": "scheduled",
  "scheduledAt": "2026-11-01T09:00:00.000Z",
  "timezone": "Europe/Berlin",
  "origin": "schedule",
  "workflowId": "6650f1c2a4b3c2d1e0f98765",
  "videoAssetId": "6650f1c2a4b3c2d1e0f98765",
  "thumbnailAssetId": "6650f1c2a4b3c2d1e0f98766",
  "imageAssetIds": [
    "6650f1c2a4b3c2d1e0f98768"
  ],
  "thumbnailUrl": "https://example.com/media/launch.mp4",
  "editable": true,
  "destinations": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "accountId": "6650f1c2a4b3c2d1e0f98767",
      "accountName": "My brand",
      "platform": "instagram",
      "placement": "string",
      "status": "scheduled",
      "url": "https://example.com/media/launch.mp4",
      "publishedAt": "2026-11-01T09:00:00.000Z",
      "providerPostIds": [
        "6650f1c2a4b3c2d1e0f98766"
      ],
      "error": {
        "code": "string",
        "message": "Human-readable explanation."
      },
      "metrics": {
        "views": 1840,
        "likes": 132,
        "comments": 18,
        "observedAt": "2026-11-01T09:00:00.000Z"
      },
      "options": {
        "caption": "Launch day!",
        "title": "Launch day",
        "description": "string",
        "visibility": "public",
        "tags": [
          "string"
        ],
        "categoryId": "6650f1c2a4b3c2d1e0f98768",
        "selfDeclaredMadeForKids": true,
        "containsSyntheticMedia": true,
        "notifySubscribers": true,
        "shareToFeed": true,
        "thumbOffsetMs": 0,
        "altText": "string",
        "aiGenerated": true,
        "replyControl": "everyone",
        "topicTag": "string",
        "tiktokPrivacyLevel": "PUBLIC_TO_EVERYONE",
        "allowComments": true,
        "allowDuet": true,
        "allowStitch": true,
        "commercialContent": true,
        "brandedContent": true,
        "promoteBrand": true,
        "aiContent": true,
        "tiktokPhotoTitle": "string",
        "tiktokAutoAddMusic": true
      }
    }
  ]
}

List late posts

GET/posts/late

  • Scope posts:read
  • Rate bucket total

Posts paused past their scheduled time (for example after a plan lapse or a disconnected account). Resolve them with POST /posts/late/resolve.

Parameters

List late posts parameters
NameInDescription
limitType: integerOptionalIn: query

Items per page (1–100, default 20).

  • Range 1–100
cursorType: stringOptionalIn: query

Opaque nextCursor from the previous page.

  • Length 1–512 chars

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/posts/late' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "data": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "kind": "video",
      "caption": "Launch day!",
      "status": "scheduled",
      "scheduledAt": "2026-11-01T09:00:00.000Z",
      "timezone": "Europe/Berlin",
      "origin": "schedule",
      "workflowId": "6650f1c2a4b3c2d1e0f98765",
      "videoAssetId": "6650f1c2a4b3c2d1e0f98765",
      "thumbnailAssetId": "6650f1c2a4b3c2d1e0f98766",
      "imageAssetIds": [
        "6650f1c2a4b3c2d1e0f98768"
      ],
      "thumbnailUrl": "https://example.com/media/launch.mp4",
      "editable": true,
      "destinations": [
        {
          "id": "6650f1c2a4b3c2d1e0f98766",
          "accountId": "6650f1c2a4b3c2d1e0f98767",
          "accountName": "My brand",
          "platform": "instagram",
          "placement": "string",
          "status": "scheduled",
          "url": "https://example.com/media/launch.mp4",
          "publishedAt": "2026-11-01T09:00:00.000Z",
          "providerPostIds": [
            "6650f1c2a4b3c2d1e0f98766"
          ],
          "error": {
            "code": "string",
            "message": "Human-readable explanation."
          },
          "metrics": {
            "views": 1840,
            "likes": 132,
            "comments": 18,
            "observedAt": "2026-11-01T09:00:00.000Z"
          },
          "options": {
            "caption": "Launch day!",
            "title": "Launch day",
            "description": "string",
            "visibility": "public",
            "tags": [
              "string"
            ],
            "categoryId": "6650f1c2a4b3c2d1e0f98768",
            "selfDeclaredMadeForKids": true,
            "containsSyntheticMedia": true,
            "notifySubscribers": true,
            "shareToFeed": true,
            "thumbOffsetMs": 0,
            "altText": "string",
            "aiGenerated": true,
            "replyControl": "everyone",
            "topicTag": "string",
            "tiktokPrivacyLevel": "PUBLIC_TO_EVERYONE",
            "allowComments": true,
            "allowDuet": true,
            "allowStitch": true,
            "commercialContent": true,
            "brandedContent": true,
            "promoteBrand": true,
            "aiContent": true,
            "tiktokPhotoTitle": "string",
            "tiktokAutoAddMusic": true
          }
        }
      ]
    }
  ],
  "nextCursor": "eyJwYWdlIjoyfQ"
}

Publish, reschedule or discard late posts

POST/posts/late/resolve

  • Scope posts:write
  • Rate bucket writes

Resolves up to 100 late posts at once. publish queues them now; reschedule spaces them from startAt by spacingMinutes; discard cancels them. Each post is reported as resolved or skipped (with a reason). Requires an Idempotency-Key: retry with the same key and body after a timeout; the original result is replayed (Idempotency-Replayed: true, with its Retry-After). Keys are remembered for 30 days. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Publish, reschedule or discard late posts parameters
NameInDescription
Idempotency-KeyType: stringRequiredIn: header

Unique per logical action. Retry with the same key and body after a timeout; remembered for 30 days.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Request body application/json

Publish, reschedule or discard late posts request body fields
NameDescription
actionType: stringRequired

publish now, reschedule from startAt, or discard (cancel).

  • One ofpublishreschedulediscard
postIdsType: string[]Required
  • Items 1–100 items
  • Unique items
startAtType: string (date-time) | integerOptional

reschedule: the first new publishing time (must not be in the past). ISO 8601 date-time with offset (e.g. 2026-11-01T09:00:00Z) or Unix milliseconds.

spacingMinutesType: integerOptional

reschedule: minutes between consecutive posts.

  • Default15
  • Range 0–240

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X POST 'https://api.repostly.app/v1/posts/late/resolve' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: resolveLatePosts-0001' \
  -H 'Content-Type: application/json' \
  -d '{
    "action": "reschedule",
    "postIds": [
      "507f1f77bcf86cd799439011"
    ],
    "startAt": "2026-11-02T09:00:00Z",
    "spacingMinutes": 30
  }'
Response · 200 OK
{
  "resolved": [
    {
      "postId": "6650f1c2a4b3c2d1e0f98768",
      "scheduledAt": "2026-11-01T09:00:00.000Z"
    }
  ],
  "skipped": [
    {
      "postId": "6650f1c2a4b3c2d1e0f98768",
      "reason": "string"
    }
  ]
}

Get a post

GET/posts/{id}

  • Scope posts:read
  • Rate bucket total

One post and its destinations. For editable posts each destination includes options in the form PUT /posts/{id} accepts, so you can GET, modify and PUT.

Parameters

Get a post parameters
NameInDescription
idType: stringRequiredIn: path

Post ID. Posts imported from a connected account use the form imported:<id>.

  • Pattern^(imported:)?[a-fA-F0-9]{24}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/posts/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "caption": "Launch day!",
  "status": "scheduled",
  "scheduledAt": "2026-11-01T09:00:00.000Z",
  "timezone": "Europe/Berlin",
  "origin": "schedule",
  "workflowId": "6650f1c2a4b3c2d1e0f98765",
  "videoAssetId": "6650f1c2a4b3c2d1e0f98765",
  "thumbnailAssetId": "6650f1c2a4b3c2d1e0f98766",
  "imageAssetIds": [
    "6650f1c2a4b3c2d1e0f98768"
  ],
  "thumbnailUrl": "https://example.com/media/launch.mp4",
  "editable": true,
  "destinations": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "accountId": "6650f1c2a4b3c2d1e0f98767",
      "accountName": "My brand",
      "platform": "instagram",
      "placement": "string",
      "status": "scheduled",
      "url": "https://example.com/media/launch.mp4",
      "publishedAt": "2026-11-01T09:00:00.000Z",
      "providerPostIds": [
        "6650f1c2a4b3c2d1e0f98766"
      ],
      "error": {
        "code": "string",
        "message": "Human-readable explanation."
      },
      "metrics": {
        "views": 1840,
        "likes": 132,
        "comments": 18,
        "observedAt": "2026-11-01T09:00:00.000Z"
      },
      "options": {
        "caption": "Launch day!",
        "title": "Launch day",
        "description": "string",
        "visibility": "public",
        "tags": [
          "string"
        ],
        "categoryId": "6650f1c2a4b3c2d1e0f98768",
        "selfDeclaredMadeForKids": true,
        "containsSyntheticMedia": true,
        "notifySubscribers": true,
        "shareToFeed": true,
        "thumbOffsetMs": 0,
        "altText": "string",
        "aiGenerated": true,
        "replyControl": "everyone",
        "topicTag": "string",
        "tiktokPrivacyLevel": "PUBLIC_TO_EVERYONE",
        "allowComments": true,
        "allowDuet": true,
        "allowStitch": true,
        "commercialContent": true,
        "brandedContent": true,
        "promoteBrand": true,
        "aiContent": true,
        "tiktokPhotoTitle": "string",
        "tiktokAutoAddMusic": true
      }
    }
  ]
}

Replace a scheduled post

PUT/posts/{id}

  • Scope posts:write
  • Rate bucket writes

Full replacement (same body as create) of a post that has not started publishing; omitting scheduledAt keeps the post's current scheduled time. Returns 409 once publishing started and for workflow posts (409 POST_BUSY while the scheduler or another change holds the post: retry shortly). The content kind cannot change. Keeping the same videoAssetId keeps its prepared audio. An Idempotency-Key is optional and honored when sent. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Replace a scheduled post parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
Idempotency-KeyType: stringOptionalIn: header

Optional. When sent, retries with the same key and input replay the first result.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Request body application/json

Replace a scheduled post request body fields
NameDescription
captionType: stringOptional

Shared caption. Required for text posts.

  • Length ≤ 4000 chars
scheduledAtType: string (date-time) | integerOptional

When to publish. On create, omit it (or send a time up to 60 s in the past) to publish on the next scheduler run. On replace (PUT), omit it to keep the post's current scheduled time. ISO 8601 date-time with offset (e.g. 2026-11-01T09:00:00Z) or Unix milliseconds.

timezoneType: stringOptional

IANA time zone for display and email context (default UTC). It never reinterprets scheduledAt.

  • Length 1–64 chars
videoAssetIdType: stringOptional

Uploaded video asset (makes this a video post).

  • Pattern^[a-fA-F0-9]{24}$
thumbnailAssetIdType: stringOptional

Optional uploaded image asset used as the video thumbnail.

  • Pattern^[a-fA-F0-9]{24}$
imageAssetIdsType: string[]Optional

Uploaded image assets: one → image post, 2–20 → carousel.

  • Items ≤ 20 items
  • Unique items
destinationsType: object[]Required
  • Items 1–50 items
destinations[].accountIdType: stringRequired

Connected account ID from GET /accounts.

  • Pattern^[a-fA-F0-9]{24}$
destinations[].placementType: stringOptional

Where the content appears. Defaults: video posts → youtube video, instagram reels, facebook video, threads video, tiktok video; image → image; carousel → carousel; text → text.

  • One ofvideoshortsreelsimagecarouseltext
destinations[].captionType: stringOptional

Caption for this destination only (overrides the post caption).

  • Length ≤ 4000 chars
destinations[].titleType: stringOptional

YouTube: the video title, at most 100 characters without < or > (YouTube uses the caption as title). Facebook video: the video title (at most 200).

  • Length ≤ 200 chars
destinations[].descriptionType: stringOptional

YouTube and Facebook video description.

  • Length ≤ 5000 chars
destinations[].visibilityType: stringOptional

YouTube video visibility. Default public.

  • One ofpublicunlistedprivate
destinations[].tagsType: string[]Optional

YouTube tags.

  • Items ≤ 100 items
destinations[].categoryIdType: stringOptional

YouTube category ID.

  • Length ≤ 3 chars
destinations[].selfDeclaredMadeForKidsType: booleanOptional

YouTube: made for kids.

destinations[].containsSyntheticMediaType: booleanOptional

YouTube: altered or synthetic content disclosure.

destinations[].notifySubscribersType: booleanOptional

YouTube: notify subscribers.

destinations[].shareToFeedType: booleanOptional

Instagram Reels: also share to the feed.

destinations[].thumbOffsetMsType: integerOptional

Instagram Reels: cover frame offset in milliseconds.

  • Range 0–86400000
destinations[].altTextType: stringOptional

Image alt text (Instagram, Facebook, Threads).

  • Length ≤ 1000 chars
destinations[].aiGeneratedType: booleanOptional

Instagram / Facebook Reels: AI-generated label.

destinations[].replyControlType: stringOptional

Threads reply control.

  • One ofeveryoneaccounts_you_followmentioned_onlyparent_post_author_onlyfollowers_only
destinations[].topicTagType: stringOptional

Threads topic tag.

  • Length ≤ 50 chars
destinations[].tiktokPrivacyLevelType: stringOptional

TikTok privacy level; must be one of privacyLevelOptions from tiktok-creator-info.

  • One ofPUBLIC_TO_EVERYONEMUTUAL_FOLLOW_FRIENDSFOLLOWER_OF_CREATORSELF_ONLY
destinations[].allowCommentsType: booleanOptional

TikTok: allow comments (explicit choice required).

destinations[].allowDuetType: booleanOptional

TikTok video: allow duets (explicit choice required).

destinations[].allowStitchType: booleanOptional

TikTok video: allow stitches (explicit choice required).

destinations[].commercialContentType: booleanOptional

TikTok: commercial content disclosure; true exactly when brandedContent or promoteBrand is true.

destinations[].brandedContentType: booleanOptional

TikTok: branded content (paid partnership).

destinations[].promoteBrandType: booleanOptional

TikTok: promotes your own brand.

destinations[].aiContentType: booleanOptional

TikTok: AI-generated content label.

destinations[].tiktokPhotoTitleType: stringOptional

TikTok photo posts: title (required for photo posts).

  • Length ≤ 90 chars
destinations[].tiktokAutoAddMusicType: booleanOptional

TikTok photo posts: let TikTok add music (required for photo posts).

destinations[].tiktokConsentType: objectOptional

Explicit TikTok creator consent collected in your UI for this post. Required for TikTok destinations on every create and every replace (PUT); GET never returns stored consent.

destinations[].tiktokConsent.contentSharingConfirmedType: booleanRequired

The creator confirmed TikTok's content sharing terms.

destinations[].tiktokConsent.musicUsageConfirmedType: booleanRequired

The creator confirmed TikTok's music usage confirmation.

destinations[].tiktokConsent.brandedContentPolicyConfirmedType: booleanOptional

Required when brandedContent is true.

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 422Content or destination not supported, or required consent missing
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X PUT 'https://api.repostly.app/v1/posts/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: replacePost-0001' \
  -H 'Content-Type: application/json' \
  -d '{
    "caption": "Launch day!",
    "scheduledAt": "2026-11-01T09:00:00+01:00",
    "timezone": "Europe/Berlin",
    "videoAssetId": "64b7f0c2a1e4d3b2c1f0e9d8",
    "destinations": [
      {
        "accountId": "507f1f77bcf86cd799439011",
        "placement": "reels",
        "caption": "IG-specific caption",
        "shareToFeed": true
      },
      {
        "accountId": "507f1f77bcf86cd799439012",
        "placement": "shorts",
        "title": "Launch day",
        "visibility": "public",
        "tags": [
          "launch"
        ]
      }
    ]
  }'
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "kind": "video",
  "caption": "Launch day!",
  "status": "scheduled",
  "scheduledAt": "2026-11-01T09:00:00.000Z",
  "timezone": "Europe/Berlin",
  "origin": "schedule",
  "workflowId": "6650f1c2a4b3c2d1e0f98765",
  "videoAssetId": "6650f1c2a4b3c2d1e0f98765",
  "thumbnailAssetId": "6650f1c2a4b3c2d1e0f98766",
  "imageAssetIds": [
    "6650f1c2a4b3c2d1e0f98768"
  ],
  "thumbnailUrl": "https://example.com/media/launch.mp4",
  "editable": true,
  "destinations": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "accountId": "6650f1c2a4b3c2d1e0f98767",
      "accountName": "My brand",
      "platform": "instagram",
      "placement": "string",
      "status": "scheduled",
      "url": "https://example.com/media/launch.mp4",
      "publishedAt": "2026-11-01T09:00:00.000Z",
      "providerPostIds": [
        "6650f1c2a4b3c2d1e0f98766"
      ],
      "error": {
        "code": "string",
        "message": "Human-readable explanation."
      },
      "metrics": {
        "views": 1840,
        "likes": 132,
        "comments": 18,
        "observedAt": "2026-11-01T09:00:00.000Z"
      },
      "options": {
        "caption": "Launch day!",
        "title": "Launch day",
        "description": "string",
        "visibility": "public",
        "tags": [
          "string"
        ],
        "categoryId": "6650f1c2a4b3c2d1e0f98768",
        "selfDeclaredMadeForKids": true,
        "containsSyntheticMedia": true,
        "notifySubscribers": true,
        "shareToFeed": true,
        "thumbOffsetMs": 0,
        "altText": "string",
        "aiGenerated": true,
        "replyControl": "everyone",
        "topicTag": "string",
        "tiktokPrivacyLevel": "PUBLIC_TO_EVERYONE",
        "allowComments": true,
        "allowDuet": true,
        "allowStitch": true,
        "commercialContent": true,
        "brandedContent": true,
        "promoteBrand": true,
        "aiContent": true,
        "tiktokPhotoTitle": "string",
        "tiktokAutoAddMusic": true
      }
    }
  ]
}

Cancel a scheduled post

DELETE/posts/{id}

  • Scope posts:write
  • Rate bucket writes

Cancels a post that has not started publishing (204). Never deletes content that is already live on a platform. An Idempotency-Key is optional and honored when sent. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Cancel a scheduled post parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
Idempotency-KeyType: stringOptionalIn: header

Optional. When sent, retries with the same key and input replay the first result.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X DELETE 'https://api.repostly.app/v1/posts/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: cancelPost-0001'
Response · 204 No Content

Empty body.

Get post engagement over time

GET/posts/{id}/analytics

  • Scope analytics:read
  • Rate bucket analytics

Net change in views, likes and comments per hour (24h) or day (7d, 30d), for the post and for each published destination. Values are null where no observation exists.

Parameters

Get post engagement over time parameters
NameInDescription
idType: stringRequiredIn: path

Post ID. Posts imported from a connected account use the form imported:<id>.

  • Pattern^(imported:)?[a-fA-F0-9]{24}$
rangeType: stringOptionalIn: query
  • One of24h7d30d
  • Default7d

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/posts/6650f1c2a4b3c2d1e0f98766/analytics' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "postId": "6650f1c2a4b3c2d1e0f98768",
  "range": "string",
  "timezone": "Europe/Berlin",
  "points": [
    {
      "at": "2026-11-01T09:00:00.000Z",
      "views": 1840,
      "likes": 132,
      "comments": 18
    }
  ],
  "destinations": [
    {
      "accountId": "6650f1c2a4b3c2d1e0f98767",
      "platform": "instagram",
      "points": [
        {
          "at": "2026-11-01T09:00:00.000Z",
          "views": 1840,
          "likes": 132,
          "comments": 18
        }
      ]
    }
  ]
}

Workflows

Automatic reposting between accounts.

List workflows

GET/workflows

  • Scope workflows:read
  • Rate bucket total

Workflows automatically repost content from a source account to a destination account.

Parameters

List workflows parameters
NameInDescription
limitType: integerOptionalIn: query

Items per page (1–100, default 20).

  • Range 1–100
cursorType: stringOptionalIn: query

Opaque nextCursor from the previous page.

  • Length 1–512 chars

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/workflows' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "data": [
    {
      "id": "6650f1c2a4b3c2d1e0f98766",
      "name": "My brand",
      "status": "active",
      "mode": "new_posts",
      "source": {
        "accountId": "6650f1c2a4b3c2d1e0f98767",
        "accountName": "My brand",
        "platform": "instagram",
        "content": "string",
        "pageId": "6650f1c2a4b3c2d1e0f98767"
      },
      "destination": {
        "accountId": "6650f1c2a4b3c2d1e0f98767",
        "accountName": "My brand",
        "platform": "instagram",
        "content": "string",
        "pageId": "6650f1c2a4b3c2d1e0f98767"
      },
      "backlog": {
        "startAt": "2026-11-01T09:00:00.000Z",
        "timezone": "Europe/Berlin",
        "order": "oldest_first",
        "postTimes": [
          "string"
        ]
      },
      "createdAt": "2026-11-01T09:00:00.000Z"
    }
  ],
  "nextCursor": "eyJwYWdlIjoyfQ"
}

Create a workflow

POST/workflows

  • Scope workflows:write
  • Rate bucket writes

Creates an active workflow. mode new_posts reposts content as it appears; backlog works through existing content at the daily postTimes. Source/destination combinations follow the app's supported matrix (422 otherwise); TikTok destinations need tiktokDeclaration: true; loops return 508. Requires an Idempotency-Key: retry with the same key and body after a timeout; the original result is replayed (Idempotency-Replayed: true, with its Retry-After). Keys are remembered for 30 days. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Create a workflow parameters
NameInDescription
Idempotency-KeyType: stringRequiredIn: header

Unique per logical action. Retry with the same key and body after a timeout; remembered for 30 days.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Request body application/json

Create a workflow request body fields
NameDescription
sourceAccountIdType: stringRequired

Account to repost from.

  • Pattern^[a-fA-F0-9]{24}$
destinationAccountIdType: stringRequired

Account to publish to.

  • Pattern^[a-fA-F0-9]{24}$
sourceContentType: stringRequired

Source content type to repost.

  • One ofreelsvideovideosshortstextimagecarousel
destinationContentType: stringRequired

Destination content type.

  • One ofreelsvideovideosshortstextimagecarousel
modeType: stringRequired

new_posts: repost new content as it appears. backlog: work through existing content on a schedule.

  • One ofnew_postsbacklog
backlogType: objectOptional

Required when mode is backlog.

backlog.startAtType: string (date-time) | integerOptional

from_date order: the first source post date to repost from. Ignored for oldest_first. ISO 8601 date-time with offset (e.g. 2026-11-01T09:00:00Z) or Unix milliseconds.

backlog.timezoneType: stringOptional

Time zone of postTimes (default UTC).

  • Length 1–64 chars
backlog.orderType: stringOptional

Default oldest_first.

  • One ofoldest_firstfrom_date
backlog.postTimesType: string[]Required

Daily publishing times, 24-hour HH:mm.

  • Items 1–24 items
  • Unique items
tiktokDeclarationType: booleanOptional

Required for TikTok destinations: the user confirmed the workflow publishing declaration.

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 422Content or destination not supported, or required consent missing
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
  • 508The workflow would create a repost loop
Request
curl -X POST 'https://api.repostly.app/v1/workflows' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: createWorkflow-0001' \
  -H 'Content-Type: application/json' \
  -d '{
    "sourceAccountId": "507f1f77bcf86cd799439011",
    "destinationAccountId": "507f1f77bcf86cd799439012",
    "sourceContent": "reels",
    "destinationContent": "shorts",
    "mode": "backlog",
    "backlog": {
      "startAt": "2026-11-01T00:00:00Z",
      "timezone": "Europe/Berlin",
      "order": "from_date",
      "postTimes": [
        "09:00",
        "18:30"
      ]
    }
  }'
Response · 201 Created
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "name": "My brand",
  "status": "active",
  "mode": "new_posts",
  "source": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "destination": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "backlog": {
    "startAt": "2026-11-01T09:00:00.000Z",
    "timezone": "Europe/Berlin",
    "order": "oldest_first",
    "postTimes": [
      "string"
    ]
  },
  "createdAt": "2026-11-01T09:00:00.000Z"
}

Get a workflow

GET/workflows/{id}

  • Scope workflows:read
  • Rate bucket total

One workflow's settings and status.

Parameters

Get a workflow parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/workflows/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "name": "My brand",
  "status": "active",
  "mode": "new_posts",
  "source": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "destination": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "backlog": {
    "startAt": "2026-11-01T09:00:00.000Z",
    "timezone": "Europe/Berlin",
    "order": "oldest_first",
    "postTimes": [
      "string"
    ]
  },
  "createdAt": "2026-11-01T09:00:00.000Z"
}

Replace a workflow

PUT/workflows/{id}

  • Scope workflows:write
  • Rate bucket writes

Full replacement of a workflow's settings (same body as create). As in the app, saving reactivates a paused workflow. An Idempotency-Key is optional and honored when sent. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Replace a workflow parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
Idempotency-KeyType: stringOptionalIn: header

Optional. When sent, retries with the same key and input replay the first result.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Request body application/json

Replace a workflow request body fields
NameDescription
sourceAccountIdType: stringRequired

Account to repost from.

  • Pattern^[a-fA-F0-9]{24}$
destinationAccountIdType: stringRequired

Account to publish to.

  • Pattern^[a-fA-F0-9]{24}$
sourceContentType: stringRequired

Source content type to repost.

  • One ofreelsvideovideosshortstextimagecarousel
destinationContentType: stringRequired

Destination content type.

  • One ofreelsvideovideosshortstextimagecarousel
modeType: stringRequired

new_posts: repost new content as it appears. backlog: work through existing content on a schedule.

  • One ofnew_postsbacklog
backlogType: objectOptional

Required when mode is backlog.

backlog.startAtType: string (date-time) | integerOptional

from_date order: the first source post date to repost from. Ignored for oldest_first. ISO 8601 date-time with offset (e.g. 2026-11-01T09:00:00Z) or Unix milliseconds.

backlog.timezoneType: stringOptional

Time zone of postTimes (default UTC).

  • Length 1–64 chars
backlog.orderType: stringOptional

Default oldest_first.

  • One ofoldest_firstfrom_date
backlog.postTimesType: string[]Required

Daily publishing times, 24-hour HH:mm.

  • Items 1–24 items
  • Unique items
tiktokDeclarationType: booleanOptional

Required for TikTok destinations: the user confirmed the workflow publishing declaration.

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 422Content or destination not supported, or required consent missing
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
  • 508The workflow would create a repost loop
Request
curl -X PUT 'https://api.repostly.app/v1/workflows/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: replaceWorkflow-0001' \
  -H 'Content-Type: application/json' \
  -d '{
    "sourceAccountId": "507f1f77bcf86cd799439011",
    "destinationAccountId": "507f1f77bcf86cd799439012",
    "sourceContent": "reels",
    "destinationContent": "shorts",
    "mode": "new_posts"
  }'
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "name": "My brand",
  "status": "active",
  "mode": "new_posts",
  "source": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "destination": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "backlog": {
    "startAt": "2026-11-01T09:00:00.000Z",
    "timezone": "Europe/Berlin",
    "order": "oldest_first",
    "postTimes": [
      "string"
    ]
  },
  "createdAt": "2026-11-01T09:00:00.000Z"
}

Delete a workflow

DELETE/workflows/{id}

  • Scope workflows:write
  • Rate bucket writes

Deletes the workflow (204). Posts it already created are kept. An Idempotency-Key is optional and honored when sent. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Delete a workflow parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
Idempotency-KeyType: stringOptionalIn: header

Optional. When sent, retries with the same key and input replay the first result.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X DELETE 'https://api.repostly.app/v1/workflows/6650f1c2a4b3c2d1e0f98766' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: deleteWorkflow-0001'
Response · 204 No Content

Empty body.

Pause a workflow

POST/workflows/{id}/pause

  • Scope workflows:write
  • Rate bucket writes

Stops future processing. Content already handed to publishing is not recalled. An Idempotency-Key is optional and honored when sent. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Pause a workflow parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
Idempotency-KeyType: stringOptionalIn: header

Optional. When sent, retries with the same key and input replay the first result.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl -X POST 'https://api.repostly.app/v1/workflows/6650f1c2a4b3c2d1e0f98766/pause' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: pauseWorkflow-0001'
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "name": "My brand",
  "status": "active",
  "mode": "new_posts",
  "source": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "destination": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "backlog": {
    "startAt": "2026-11-01T09:00:00.000Z",
    "timezone": "Europe/Berlin",
    "order": "oldest_first",
    "postTimes": [
      "string"
    ]
  },
  "createdAt": "2026-11-01T09:00:00.000Z"
}

Resume a workflow

POST/workflows/{id}/resume

  • Scope workflows:write
  • Rate bucket writes

Reactivates the workflow after re-checking your plan, both accounts and loops. An Idempotency-Key is optional and honored when sent. Non-final outcomes are not saved: errors returned before any change was made, every 429 and transient capacity or state conflicts (such as 409 POST_BUSY); retry those with the same key after Retry-After.

Parameters

Resume a workflow parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
Idempotency-KeyType: stringOptionalIn: header

Optional. When sent, retries with the same key and input replay the first result.

  • Length 8–128 chars
  • Pattern^[A-Za-z0-9_.:-]{8,128}$

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 409Conflict: state does not allow the change, or idempotency conflict / unconfirmed request
  • 422Content or destination not supported, or required consent missing
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
  • 508The workflow would create a repost loop
Request
curl -X POST 'https://api.repostly.app/v1/workflows/6650f1c2a4b3c2d1e0f98766/resume' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY" \
  -H 'Idempotency-Key: resumeWorkflow-0001'
Response · 200 OK
{
  "id": "6650f1c2a4b3c2d1e0f98766",
  "name": "My brand",
  "status": "active",
  "mode": "new_posts",
  "source": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "destination": {
    "accountId": "6650f1c2a4b3c2d1e0f98767",
    "accountName": "My brand",
    "platform": "instagram",
    "content": "string",
    "pageId": "6650f1c2a4b3c2d1e0f98767"
  },
  "backlog": {
    "startAt": "2026-11-01T09:00:00.000Z",
    "timezone": "Europe/Berlin",
    "order": "oldest_first",
    "postTimes": [
      "string"
    ]
  },
  "createdAt": "2026-11-01T09:00:00.000Z"
}

Get workflow activity

GET/workflows/{id}/activity

  • Scope workflows:read
  • Rate bucket total

Source items being processed (including blocked items and the reason) and the posts the workflow created, newest first. Each list pages independently: pass processing.nextCursor as processingCursor and posts.nextCursor as postsCursor; limit applies to both. Media preparation is automatic.

Parameters

Get workflow activity parameters
NameInDescription
idType: stringRequiredIn: path

24-character hexadecimal ID.

  • Pattern^[a-fA-F0-9]{24}$
limitType: integerOptionalIn: query

Items per page (1–100, default 20).

  • Range 1–100
processingCursorType: stringOptionalIn: query

Opaque nextCursor from the previous page.

  • Length 1–512 chars
postsCursorType: stringOptionalIn: query

Opaque nextCursor from the previous page.

  • Length 1–512 chars

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/workflows/6650f1c2a4b3c2d1e0f98766/activity' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "status": "active",
  "processing": {
    "data": [
      {
        "id": "6650f1c2a4b3c2d1e0f98766",
        "state": "pending",
        "blockedReason": "string",
        "postId": "6650f1c2a4b3c2d1e0f98768",
        "sourceCaption": "string",
        "sourcePostedAt": "2026-11-01T09:00:00.000Z",
        "updatedAt": "2026-11-01T09:00:00.000Z"
      }
    ],
    "nextCursor": "eyJwYWdlIjoyfQ"
  },
  "posts": {
    "data": [
      {
        "id": "6650f1c2a4b3c2d1e0f98766",
        "kind": "video",
        "caption": "Launch day!",
        "status": "scheduled",
        "scheduledAt": "2026-11-01T09:00:00.000Z",
        "timezone": "Europe/Berlin",
        "origin": "schedule",
        "workflowId": "6650f1c2a4b3c2d1e0f98765",
        "videoAssetId": "6650f1c2a4b3c2d1e0f98765",
        "thumbnailAssetId": "6650f1c2a4b3c2d1e0f98766",
        "imageAssetIds": [
          "6650f1c2a4b3c2d1e0f98768"
        ],
        "thumbnailUrl": "https://example.com/media/launch.mp4",
        "editable": true,
        "destinations": [
          {
            "id": "6650f1c2a4b3c2d1e0f98766",
            "accountId": "6650f1c2a4b3c2d1e0f98767",
            "accountName": "My brand",
            "platform": "instagram",
            "placement": "string",
            "status": "scheduled",
            "url": "https://example.com/media/launch.mp4",
            "publishedAt": "2026-11-01T09:00:00.000Z",
            "providerPostIds": [
              "6650f1c2a4b3c2d1e0f98766"
            ],
            "error": {
              "code": "string",
              "message": "Human-readable explanation."
            },
            "metrics": {
              "views": 1840,
              "likes": 132,
              "comments": 18,
              "observedAt": "2026-11-01T09:00:00.000Z"
            },
            "options": {
              "caption": "Launch day!",
              "title": "Launch day",
              "description": "string",
              "visibility": "public",
              "tags": [
                "string"
              ],
              "categoryId": "6650f1c2a4b3c2d1e0f98768",
              "selfDeclaredMadeForKids": true,
              "containsSyntheticMedia": true,
              "notifySubscribers": true,
              "shareToFeed": true,
              "thumbOffsetMs": 0,
              "altText": "string",
              "aiGenerated": true,
              "replyControl": "everyone",
              "topicTag": "string",
              "tiktokPrivacyLevel": "PUBLIC_TO_EVERYONE",
              "allowComments": true,
              "allowDuet": true,
              "allowStitch": true,
              "commercialContent": true,
              "brandedContent": true,
              "promoteBrand": true,
              "aiContent": true,
              "tiktokPhotoTitle": "string",
              "tiktokAutoAddMusic": true
            }
          }
        ]
      }
    ],
    "nextCursor": "eyJwYWdlIjoyfQ"
  }
}

Analytics

Stored engagement observations.

Get analytics totals

GET/analytics/summary

  • Scope analytics:read
  • Rate bucket analytics

Views, likes, comments, follower change and posts in the range, with the change against the previous range (null without a baseline) and lifetime totals. Stored observations, not a live platform refresh; may lag up to about an hour.

Parameters

Get analytics totals parameters
NameInDescription
rangeType: stringOptionalIn: query

Time window ending now.

  • One of24h7d30d365dall
  • Default30d
accountIdType: stringOptionalIn: query

Only this connected account.

  • Pattern^[a-fA-F0-9]{24}$
platformType: stringOptionalIn: query

Only this platform.

  • One ofinstagramfacebooktiktokyoutubethreads

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/analytics/summary' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "range": "string",
  "valueKind": "string",
  "totals": {
    "views": 1840,
    "likes": 132,
    "comments": 18,
    "followers": 5210,
    "posts": 12
  },
  "changePercent": {
    "views": 1840,
    "likes": 132,
    "comments": 18,
    "followers": 5210,
    "posts": 12
  },
  "lifetime": {
    "views": 1840,
    "likes": 132,
    "comments": 18,
    "posts": 12
  }
}

Get a metric over time

GET/analytics/timeseries

  • Scope analytics:read
  • Rate bucket analytics

Hourly (24h), daily (7d, 30d, 365d) or weekly (all, Monday-based) points of the observed change in one metric. Buckets start on hour/day/week boundaries in timezone: endAt (default now, at most one day ahead) is aligned down to the start of its bucket, and the last point is that (possibly partial) bucket. Stored observations; may lag up to about an hour.

Parameters

Get a metric over time parameters
NameInDescription
metricType: stringRequiredIn: query
  • One ofviewslikescommentsfollowers
rangeType: stringOptionalIn: query

Time window ending now.

  • One of24h7d30d365dall
  • Default30d
accountIdType: stringOptionalIn: query

Only this connected account.

  • Pattern^[a-fA-F0-9]{24}$
platformType: stringOptionalIn: query

Only this platform.

  • One ofinstagramfacebooktiktokyoutubethreads
timezoneType: stringOptionalIn: query

Bucket boundaries' time zone (default UTC).

  • Length 1–64 chars
endAtType: string (date-time) | integerOptionalIn: query

Any time within the last bucket (default now); aligned down to its bucket start. ISO 8601 date-time with offset (e.g. 2026-11-01T09:00:00Z) or Unix milliseconds.

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/analytics/timeseries?metric=views' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "metric": "string",
  "range": "string",
  "timezone": "Europe/Berlin",
  "points": [
    {
      "at": "2026-11-01T09:00:00.000Z",
      "value": 1840,
      "increase": 1,
      "decrease": 1,
      "baseline": 1,
      "availableSamples": 1,
      "unavailableSamples": 1
    }
  ],
  "coverage": {
    "latestObservedAt": "2026-11-01T09:00:00.000Z"
  }
}

Get top posts

GET/analytics/top-posts

  • Scope analytics:read
  • Rate bucket analytics

Posts ranked by observed view growth in the range. postId is the same ID as in /posts.

Parameters

Get top posts parameters
NameInDescription
rangeType: stringOptionalIn: query
  • One of24h7d30d
  • Default7d
accountIdType: stringOptionalIn: query

Only this connected account.

  • Pattern^[a-fA-F0-9]{24}$
platformType: stringOptionalIn: query

Only this platform.

  • One ofinstagramfacebooktiktokyoutubethreads

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/analytics/top-posts' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "range": "string",
  "valueKind": "string",
  "data": [
    {
      "postId": "6650f1c2a4b3c2d1e0f98768",
      "caption": "Launch day!",
      "thumbnailUrl": "https://example.com/media/launch.mp4",
      "netViews": 1,
      "destinations": [
        {
          "accountId": "6650f1c2a4b3c2d1e0f98767",
          "accountName": "My brand",
          "platform": "instagram",
          "netViews": 1,
          "lastCheckedAt": "2026-11-01T09:00:00.000Z"
        }
      ]
    }
  ]
}

Get audience activity by hour of day

GET/analytics/best-times

  • Scope analytics:read
  • Rate bucket analytics

Observed view growth across your accounts grouped by the hour of day (0–23) in which it was recorded, in the given time zone (using the zone's current UTC offset). It shows when your audience watches, which you can use to choose posting times; it is not grouped by when posts were published. Baseline readings are excluded.

Parameters

Get audience activity by hour of day parameters
NameInDescription
rangeType: stringOptionalIn: query

Time window ending now.

  • One of24h7d30d365dall
  • Default30d
accountIdType: stringOptionalIn: query

Only this connected account.

  • Pattern^[a-fA-F0-9]{24}$
platformType: stringOptionalIn: query

Only this platform.

  • One ofinstagramfacebooktiktokyoutubethreads
timezoneType: stringOptionalIn: query

Time zone of the hours (default UTC).

  • Length 1–64 chars

Errors

  • 400Invalid request (details.fields names each problem)
  • 401Missing, invalid, expired or revoked API key
  • 403Missing scope (INSUFFICIENT_SCOPE) or no paid plan (PLAN_REQUIRED)
  • 404Not found among your resources
  • 429Rate limit or upload capacity reached; wait for Retry-After
  • 503Temporarily unavailable or outcome unconfirmed; check the resource before retrying a mutation
Request
curl 'https://api.repostly.app/v1/analytics/best-times' \
  -H "Authorization: Bearer $REPOSTLY_API_KEY"
Response · 200 OK
{
  "range": "string",
  "timezone": "Europe/Berlin",
  "hours": [
    {
      "hour": 9,
      "value": 1840
    }
  ]
}