PM3K gives you two main ways to work with TikTok:
PULL_FROM_URL, not file upload) and then check publish status by publish_id.Base URLs (production):
POST https://pm3k.org/api/tiktok/direct_postPOST https://pm3k.org/api/tiktok/status
All responses are JSON with
Content-Type: application/json; charset=UTF-8.
pm3k.org.
PM3K stores your TikTok open_id and plan in internal storage (KV).
Free plan basics:
Paid plans increase key lifetime, storage TTL and upload limits, but TikTok anti-spam limits stay the same.
Important:
Use this header for all API calls:
Authorization: Bearer YOUR_API_KEY_HERE
source_url)You always pass a URL to the video file, not the file itself. Two common options:
https://files.pm3k.org/uploads/<open_id>/<date>/<id>.mp4
source_url in direct_post.Available on paid plans only (Monthly / Annual).
Requirements for your own domain:
source_url must resolve within a reasonable number of HTTP redirects.
If PM3K cannot fetch the file (for example, redirect loop or network timeout over ~30 seconds),
the request may fail with proxy_error.
File size limit: Maximum file size is typically around 200 MB.
Larger files may fail with tiktok_error or proxy_error.
Domain verification flow:
media.example.com).https://your-domain.com/pm3k-verify.txt).
source_url in the API.
Even for a verified domain, PM3K will still pull your video through its own storage first (temporary copy) to build a fingerprint (size + ETag) and run duplicate / anti-spam checks. TikTok does not fetch directly from your domain via this API; PM3K sits in the middle for better safety and de-duplication.
POST /api/tiktok/direct_postCreate a TikTok post using PULL_FROM_URL.
URL
POST https://pm3k.org/api/tiktok/direct_post
Headers
Authorization: Bearer YOUR_API_KEY_HERE
Content-Type: application/json
Accept: application/json
Body (basic)
{
"source_url": "https://files.pm3k.org/uploads/.../video.mp4",
"title": "Your TikTok caption here"
}
Use this endpoint from n8n, Make, or your own scripts to send a prepared vertical video
directly to TikTok. The same payload is used for scheduled posts via /api/jobs.
{
"source_url": "https://files.pm3k.org/abc123.mp4",
"title": "Your TikTok caption here",
"privacy": "PUBLIC_TO_EVERYONE",
"disable_comment": false,
"disable_duet": false,
"disable_stitch": false,
"is_ad": false,
"ad_your_brand": false,
"ad_branded_content": false,
"brand": ""
}
source_url (string) — HTTPS URL of your vertical MP4/MOV.
The URL must be either from PM3K storage or from a verified custom domain.
title (string) — TikTok caption, up to ~2200 characters.
privacy (string) — TikTok privacy level. Recommended values:
PUBLIC_TO_EVERYONEMUTUAL_FOLLOW_FRIENDSFOLLOWER_OF_CREATORSELF_ONLY"public", "friends", "followers", "private" —
PM3K will normalize them to the correct TikTok enum.
If value is missing or unknown, PM3K falls back to SELF_ONLY.
Important: The video must be vertical (9:16).
PM3K does not re-encode, rotate or compress your video — the exact file you provide
is uploaded to TikTok as-is. Files that are not vertical may be rejected with a
bad_request error.
Duration limits: TikTok may reject videos that exceed the maximum duration allowed for your account. PM3K does not override this limit — TikTok enforces it on upload.
PM3K does not compress, modify or re-encode your video. The file hosted at
source_url is the file TikTok receives.
disable_comment (boolean, optional) — if true,
comments are turned off for this video.
disable_duet (boolean, optional) — if true,
duets are disabled.
disable_stitch (boolean, optional) — if true,
stitches are disabled.
These fields are optional but recommended if you use PM3K for commercial posts. They are designed to reflect TikTok’s ad / branded content policies.
is_ad (boolean, optional) — mark this post as commercial / advertising content.
ad_your_brand (boolean, optional) — you promote your own brand, product, or service.
ad_branded_content (boolean, optional) — you promote a third-party brand
(branded content collaboration).
brand (string, optional) — brand or advertiser name.
When is_ad is true and you use branded content,
you should provide a clear brand name here.
You don’t need to send any technical metadata (file size, ETag, etc.). PM3K reads everything it needs from its own storage and uses internal checks to block duplicate uploads and spammy captions.
{
"ok": true,
"publish_id": "7153xxxxxxxxxxxxxxxxxx"
}
You will use publish_id with /api/tiktok/status to check the publish state.
POST /api/tiktok/status
Check current TikTok publish status for a post created by
direct_post.
URL
POST https://pm3k.org/api/tiktok/status
Headers
Authorization: Bearer YOUR_API_KEY_HERE
Content-Type: application/json
Accept: application/json
Body
{
"publish_id": "7153xxxxxxxxxxxxxxxxxx"
}
{
"ok": true,
"status": "PUBLISHING",
"state": "pending",
"fail_reason": null,
"raw": {
"...": "TikTok original response"
}
}
Fields:
status – raw TikTok status (e.g. PUBLISHING, PUBLISH_COMPLETE, PUBLISH_FAILED).state – simplified state:
pending – still processing.done – published successfully.failed – TikTok failed to publish.fail_reason – TikTok fail reason, if any.raw – full TikTok JSON for debugging.
For /status, PM3K applies a soft limit of
20 calls per minute per TikTok account.
If exceeded, you receive:
{
"ok": false,
"error": "rate_limit_status",
"message": "Too many /status calls for this account, please slow down.",
"retry_after": "2025-01-01T12:00:00.000Z"
}
HTTP status: 429.
Recommended polling pattern:
You can manage TikTok posts directly from the PM3K dashboard. No extra API endpoints are required for this.
source_url in your own low-code workflows
(for example, call /api/tiktok/direct_post from n8n or Make).
Captions with links are blocked. If title contains
http://, https:// or www., PM3K rejects the request:
{
"ok": false,
"error": "links_forbidden",
"message": "Links are not allowed in TikTok captions for this service."
}
HTTP status: 400.
PM3K maintains a forbidden-words list (STOP_WORDS) covering, for example:
If your caption contains one of these words or phrases, PM3K returns:
{
"ok": false,
"error": "forbidden_word",
"message": "Caption contains forbidden content: \"...\"."
}
HTTP status: 400. Such captions are not sent to TikTok at all.
PM3K avoids posting the same video repeatedly for the same account.
A fingerprint is built from source_url, file size and ETag.
If the same fingerprint was used on this account in the last 12 months:
{
"ok": false,
"error": "video_duplicate",
"message": "This video URL was already used for this TikTok account recently."
}
HTTP status: 400.
Two layers:
{
"ok": false,
"error": "caption_duplicate_account",
"message": "This caption was already used on this TikTok account recently."
}
{
"ok": false,
"error": "caption_global_spam",
"message": "This caption is being reused across too many accounts in a short time."
}
Both responses use HTTP status 400.
Per TikTok account (per creator_id):
If you exceed these limits:
{
"ok": false,
"error": "daily_limit",
"message": "Daily TikTok posting limit (10/day) reached."
}
or
{
"ok": false,
"error": "interval_limit",
"message": "You must wait about N more minutes before next TikTok post."
}
HTTP status: 429.
If TikTok itself returns HTTP 429 (rate limit) for your account, PM3K
sets a cooldown for this creator_id (around 3 hours).
During cooldown you will see:
{
"ok": false,
"error": "tiktok_rate_limit",
"message": "TikTok returned HTTP 429 (rate limit) for this account; posting is temporarily paused.",
"retry_after_seconds": 10800
}
or subsequent calls will return:
{
"ok": false,
"error": "tiktok_cooldown",
"message": "TikTok recently returned HTTP 429 for this account; posting is temporarily paused."
}
Both use HTTP status 429. Do not auto-retry during cooldown.
Common error values in API responses:
unauthorized (401) – missing or malformed Authorization header.invalid_api_key (401) – key not found, expired or revoked.no_token (401) – no TikTok token stored for this user; reconnect in dashboard.bad_request (400) – invalid or missing required fields (e.g. source_url, publish_id).links_forbidden (400) – caption contains a link.forbidden_word (400) – caption hits forbidden content; not sent to TikTok.video_duplicate (400) – same video reused on this account within 12 months.caption_duplicate_account (400) – same caption reused on this account within 12 months.caption_global_spam (400) – same caption reused on too many accounts in 24 hours.daily_limit (429) – 10 posts/day limit reached.interval_limit (429) – 1-hour interval not respected.tiktok_rate_limit / tiktok_cooldown (429) – TikTok asked to slow down; wait cooldown.rate_limit_status (429) – too many /status calls; see retry_after.tiktok_error (400) – TikTok returned an error; inspect raw.proxy_error (502) – internal backend unreachable; usually transient.POSThttps://pm3k.org/api/tiktok/direct_postAuthorization: Bearer <your key>, Content-Type: application/jsonBody (JSON):
{
"source_url": "https://files.pm3k.org/uploads/.../video.mp4",
"title": "My caption without links"
}
Store ok and publish_id from the response. If
ok is false, log error and message and stop the flow.
In n8n, always add an IF node right after this step:
if ok == true → continue to the status loop;
if ok == false → stop the flow and log error/message.
Do not try to access publish_id when ok is false.
POST https://pm3k.org/api/tiktok/status with:
{
"publish_id": "{{$json["publish_id"]}}"
}
state == "done" → success.state == "failed" → log fail_reason and stop.pending) → wait and repeat, up to a max attempts limit.