Developers

REST API

Push videos and photo carousels from your own scripts. The API works on the same accounts and posts as the app.

Quick start

  1. In the app, open Settings → API keys and create a key. It's shown once, so copy it then.
  2. Upload your media with POST /v1/media.
  3. Create the post with POST /v1/posts.
  4. Open the post in the app, check its settings and confirm it. See Needs review.
# 1. upload a slide (repeat per file)
curl https://scheduler.rewirelabs.app/v1/media \
  -H "Authorization: Bearer rs_…" \
  -F file=@slide-01.jpg

# 2. create the post
curl https://scheduler.rewirelabs.app/v1/posts \
  -H "Authorization: Bearer rs_…" \
  -H "Content-Type: application/json" \
  -d '{"account_id": "…", "kind": "photo", "media_ids": ["…", "…"],
       "title": "weird food rules that stuck", "scheduled_at": "2026-11-22T12:50:00Z",
       "ref": "carousel/02"}'

Base URL and authentication

All endpoints live under:

https://scheduler.rewirelabs.app/v1

Send your API key as a bearer token on every request:

Authorization: Bearer rs_…
  • Create and revoke keys in the app under Settings → API keys. A new key is shown once; we store only a hash of it.
  • You can have up to 10 active keys. Keys can only be created and revoked in the app, not through the API.
  • A wrong or revoked key gets 401 with {"error": "invalid_api_key"}. A request without a key gets 401 with {"error": "unauthorized"}.

Needs review

TikTok requires the creator to confirm each post's settings before it is published. So posts for TikTok accounts that you create with an API key land in "Needs review", and they are only published after you open them in the app, check the settings and confirm.

The API can't confirm a TikTok post for you. That's why action in POST /v1/posts has no effect on TikTok posts created with an API key.

Rate limits

  • 120 requests a minute per user. All your API keys and your app session share this limit.
  • Over the limit you get 429 with {"error": "rate_limited", "message": "Slow down: 120 requests a minute."}. Wait for the minute to pass and try again.

Errors

Errors use HTTP status codes and a JSON body:

{ "error": "post_not_found", "message": "Post not found." }

error is a stable code you can branch on; message is a human-readable explanation (authentication errors return only error). Common codes:

Status error When
400 invalid_input A field is missing or invalid. message names the field.
400 no_file, bad_media The upload has no file field, or the file isn't supported.
400 media_not_found, duplicate_media A media id doesn't exist in your account, or appears twice.
400 caption_too_long, bad_title Video caption over 2,200 characters, or a title on a video post.
401 invalid_api_key, unauthorized The key is wrong or revoked, or no key was sent.
404 post_not_found, account_not_found No such post or account in your account.
409 not_editable, not_cancelable, cancel_first The post's status doesn't allow this change.
429 rate_limited Over 120 requests a minute.
500 server_error Something went wrong on our side.

List accounts

GET /v1/accounts returns the TikTok accounts you've connected. Use an account's id as account_id when you create a post.

curl https://scheduler.rewirelabs.app/v1/accounts \
  -H "Authorization: Bearer rs_…"
{
  "accounts": [
    {
      "id": "…",
      "adapter": "tiktok",
      "external_id": "…",
      "display_name": "…",
      "avatar_url": "/v1/accounts/…/avatar",
      "status": "…",
      "created_at": 1794182400000
    }
  ]
}

created_at is in milliseconds since the Unix epoch. avatar_url points to the copy of the avatar saved on our server; fetch it with the same Authorization header.

Upload media

POST /v1/media uploads one file as multipart/form-data, in a field named file. Upload each slide of a carousel separately and keep the ids in order.

curl https://scheduler.rewirelabs.app/v1/media \
  -H "Authorization: Bearer rs_…" \
  -F file=@clip.mp4

The file type is detected from the file's contents, not its name.

Type Accepted Rules
Images JPG, PNG, WebP, up to 60 MB Converted to JPEG and scaled down (never up) to fit 1080×1920 portrait or 1920×1080 landscape. At least 360 px on each side after resizing.
Videos MP4, MOV, WebM, up to 250 MB H.264, H.265, VP8 or VP9. At least 3 seconds, 23 to 60 fps, at least 360 px on each side.

Response (201):

{
  "id": "…",
  "kind": "image",
  "mime": "image/jpeg",
  "width": 1080,
  "height": 1350,
  "duration_ms": null,
  "bytes": 245731,
  "url": "https://scheduler.rewirelabs.app/m/…/1.jpg"
}

url is a long, unguessable link that TikTok uses to fetch the file when it publishes.

Create a post

POST /v1/posts with a JSON body:

Field Type Notes
account_id string, required From GET /v1/accounts.
kind "video" or "photo", required
media_ids array of strings, required One video for video; 1 to 35 images, in order, for photo.
title string, up to 90 Photo posts only.
caption string, up to 4,000 Video captions are limited to 2,200. Defaults to empty.
scheduled_at ISO 8601 date-time with offset For example 2026-11-22T12:50:00Z.
tz IANA time zone For example America/New_York. Defaults to your account's time zone.
series string, up to 40 Your own grouping label.
ref string, up to 120 Your own reference, for example carousel/02. Filter by it with ?ref=.
options object TikTok settings, below.
action "draft" or "schedule" Defaults to "draft". Has no effect on TikTok posts created with an API key: they always start in Needs review.

options (all optional; unknown fields are rejected). They are saved with the post, and you check them in the app before you confirm it:

Field Type Notes
privacy_level string One of TikTok's privacy levels, for example "SELF_ONLY".
allow_comment, allow_duet, allow_stitch boolean Interactions, as far as the account allows them.
auto_add_music boolean Photo posts: TikTok adds a recommended sound.
cover_index integer, 0 to 34 Photo posts: which image is the cover.
cover_ms integer Video posts: the cover frame, in milliseconds from the start.
brand_organic boolean Commercial content disclosure: "Your brand".
brand_content boolean Commercial content disclosure: "Branded content" (a paid partnership).
ai_generated boolean The AI-generated content label.
curl https://scheduler.rewirelabs.app/v1/posts \
  -H "Authorization: Bearer rs_…" \
  -H "Content-Type: application/json" \
  -d '{
    "account_id": "…",
    "kind": "photo",
    "media_ids": ["…", "…", "…"],
    "title": "weird food rules that stuck",
    "caption": "Five rules I actually kept.",
    "scheduled_at": "2026-11-22T12:50:00Z",
    "ref": "carousel/02",
    "options": { "auto_add_music": true, "brand_organic": true }
  }'

The response (201) is the post:

{
  "id": "…",
  "account_id": "…",
  "kind": "photo",
  "route": "tiktok_direct",
  "status": "needs_review",
  "source": "api",
  "title": "weird food rules that stuck",
  "caption": "Five rules I actually kept.",
  "series": null,
  "ref": "carousel/02",
  "options": { "auto_add_music": true, "brand_organic": true },
  "scheduled_at": "2026-11-22T12:50:00.000Z",
  "tz": "America/New_York",
  "published_at": null,
  "confirmed_at": null,
  "external_id": null,
  "external_url": null,
  "error": null,
  "media": [{ "id": "…", "kind": "image", "url": "…", "thumbnail": "…", "width": 1080, "height": 1350, "duration_ms": null }],
  "media_source": "local",
  "created_at": "…",
  "updated_at": "…"
}

status is one of draft, needs_review, scheduled, publishing, processing, published, failed or canceled. confirmed_at is set when you confirm the post in the app. If a post fails, error says why.

List posts

GET /v1/posts returns your posts, oldest scheduled time first, as {"posts": [ … ]}. Query parameters, all optional:

Parameter Notes
from, to ISO dates or date-times. Posts scheduled from from and before to. Posts without a time are always included.
status One or more statuses, comma-separated, for example needs_review,scheduled.
account_id Only posts for this account.
ref Only posts with exactly this reference.
series Only posts in this series.
limit 1 to 1,000. Defaults to 500.
curl "https://scheduler.rewirelabs.app/v1/posts?ref=carousel/02" \
  -H "Authorization: Bearer rs_…"

Get a post

GET /v1/posts/:id returns one post.

curl https://scheduler.rewirelabs.app/v1/posts/… \
  -H "Authorization: Bearer rs_…"

Update a post

PATCH /v1/posts/:id changes any of media_ids, title, caption, scheduled_at, tz, series, ref and options. Send only what changes; options is merged into the existing options. account_id and kind can't be changed.

Only posts that are draft, needs_review, scheduled, failed or canceled can be edited. Others return 409 not_editable.

Editing a scheduled TikTok post with an API key sends it back to Needs review, so you confirm the changed post in the app again.

curl -X PATCH https://scheduler.rewirelabs.app/v1/posts/… \
  -H "Authorization: Bearer rs_…" \
  -H "Content-Type: application/json" \
  -d '{"scheduled_at": "2026-11-23T17:00:00Z", "options": {"auto_add_music": false}}'

Cancel a post

POST /v1/posts/:id/cancel unschedules a post and sets its status to canceled. Works for posts that are scheduled, needs_review, draft or failed; others return 409 not_cancelable. Returns the post.

curl -X POST https://scheduler.rewirelabs.app/v1/posts/…/cancel \
  -H "Authorization: Bearer rs_…"

Delete a post

DELETE /v1/posts/:id deletes the post from Rewire Labs Scheduler and returns {"ok": true}. Cancel a scheduled post first (otherwise 409 cancel_first); a post that is being published can't be deleted until it finishes. Deleting here doesn't remove a post that is already on TikTok.

curl -X DELETE https://scheduler.rewirelabs.app/v1/posts/… \
  -H "Authorization: Bearer rs_…"

More

Questions? Email connect@rewirelabs.app.