TikTok Content Posting API: публикация видео и фото
TikTok Content Posting API работает в двух режимах. Direct Post (scope video.publish) публикует контент сразу в профиль автора, Upload (video.upload) отправляет видео во «Входящие» TikTok, где автор завершает пост в приложении. Перед каждым Direct Post запрашивается creator_info. Пока TikTok не провёл аудит приложения, любой пост будет приватным (SELF_ONLY).
Обновлено:
Direct Post или Upload: что выбрать?
Direct Post нужен, когда пользователь завершает публикацию в вашем приложении: оно собирает подпись, приватность и настройки взаимодействия, а TikTok публикует пост сразу после обработки. Upload нужен, когда автор должен редактировать в TikTok: приложение передаёт медиа, автор получает уведомление во «Входящих» и публикует пост в приложении TikTok.
Механика у режимов общая: init-запрос возвращает publish_id (а при загрузке файла ещё и upload_url), затем медиа передаётся, а результат отслеживается через endpoint статуса или вебхуки Content Posting.
У Direct Post правила строже — обязательный запрос creator info, заданный интерфейс публикации и аудит для публичной видимости, — потому что контент выходит в эфир без открытия TikTok автором.
Сравнение Direct Post и Upload
| Параметр | Direct Post | Upload (inbox) |
|---|---|---|
| Scope | video.publish | video.upload |
| Endpoint для видео | /v2/post/publish/video/init/ | /v2/post/publish/inbox/video/init/ |
| Endpoint для фото | /v2/post/publish/content/init/, post_mode = DIRECT_POST | /v2/post/publish/content/init/, post_mode = MEDIA_UPLOAD |
| Кто задаёт подпись и приватность | Ваше приложение по явному выбору пользователя | Автор внутри TikTok |
| Запрос creator info | Обязателен перед каждым постом | В документированном потоке отсутствует |
| Ограничение объёма | Лимит на автора за 24 часа, обычно около 15 постов | Не более 5 ожидающих отправок за любые 24 часа |
| Итоговый статус | PUBLISH_COMPLETE | SEND_TO_USER_INBOX, дальше публикует автор |
| Socialift SDK | publish_video, publish_photos | upload_video, publish_photos(draft=True) |
Как опубликовать видео через Direct Post
Порядок важен: правила TikTok требуют запросить creator info и показать определённый интерфейс ещё до init-запроса.
Получите разрешение
video.publishАвтор должен авторизовать ваше приложение со scope
video.publishчерез Login Kit. Access token живёт 24 часа — вовремя его обновляйте.Запросите creator info
POST
/v2/post/publish/creator_info/query/возвращает никнейм и аватар автора,privacy_level_options,comment_disabled,duet_disabled,stitch_disabledиmax_video_post_duration_sec. Ошибкиspam_risk_too_many_postsилиreached_active_user_capозначают, что сейчас публикацию предлагать не стоит.Покажите экран публикации по правилам
Никнейм автора, выпадающий список приватности без значения по умолчанию, выключенные по умолчанию переключатели взаимодействий (неактивные, если автор их отключил), переключатель раскрытия коммерческого контента, превью и согласие с Music Usage Confirmation. Предупредите, что обработка может занять несколько минут.
Инициализируйте публикацию
Отправьте
post_info(title, privacy_level, disable_comment, disable_duet, disable_stitch, флаги брендированного контента) иsource_infoс источникомFILE_UPLOADилиPULL_FROM_URL. В ответе будетpublish_id, а для загрузки файла —upload_url.Передайте медиа
Для
FILE_UPLOADотправьте части методом PUT наupload_urlв течение часа. ДляPULL_FROM_URLTikTok сам скачивает файл с подтверждённого адреса.Отследите результат
Запрашивайте
/v2/post/publish/status/fetch/сpublish_idили подпишитесь на вебхуки Content Posting и покажите пользователю итог.
Значения privacy_level
Отправляйте только значение, которое creator info вернул для этого автора, — иначе запрос отклонится с privacy_level_option_mismatch.
| Значение | Кто видит пост |
|---|---|
PUBLIC_TO_EVERYONE | Все |
MUTUAL_FOLLOW_FRIENDS | Друзья — взаимные подписчики |
FOLLOWER_OF_CREATOR | Подписчики автора |
SELF_ONLY | Только автор; единственное значение для клиентов без аудита |
Список зависит от настроек аккаунта автора, поэтому не зашивайте его в код.
Direct Post на Python
SDK отправляет init-запрос, загружает части файла и при wait=True опрашивает статус, пока пост не опубликуется или не упадёт с ошибкой.
from socialift import TikTok
async def post_video(token: str, privacy: str, caption: str) -> dict:
async with TikTok.from_env() as tt:
info = await tt.creator_info(token)
if privacy not in info["privacy_level_options"]:
raise ValueError(f"{privacy} is not allowed for this creator")
return await tt.publish_video(
token,
video="renders/launch.mp4", # FILE_UPLOAD, SDK режет файл на части
title=caption,
privacy_level=privacy, # до аудита только SELF_ONLY
disable_comment=info.get("comment_disabled", False),
disable_duet=info.get("duet_disabled", False),
disable_stitch=info.get("stitch_disabled", False),
wait=True,
)По умолчанию publish_video отправляет SELF_ONLY — единственный уровень, доступный клиенту без аудита. wait=True опрашивает статус каждые 3 секунды до 2 минут и при неудаче выбрасывает ApiError со значением fail_reason от TikTok.
Если вместо video= передать video_url=, включается режим PULL_FROM_URL. Для неподтверждённого домена SDK выбросит PermissionError на ошибку url_ownership_unverified.
Что может и чего не может приложение без аудита
TikTok ограничивает каждый клиент Content Posting API, не прошедший аудит. Весь опубликованный им контент виден только в приватном режиме (SELF_ONLY), аккаунт автора в момент публикации должен быть приватным, а за 24 часа через клиента могут публиковать не более 5 пользователей. Попытка опубликовать публично вернёт unaudited_client_can_only_post_to_private_accounts.
Этого достаточно, чтобы разработать и показать интеграцию, но не чтобы запустить её. После тестирования подаётся заявка на аудит — он проверяет соответствие условиям TikTok и content sharing guidelines. Sandbox здесь не поможет: в нём нет доступа к Content Posting API для публичных видео.
Посты, созданные до аудита, остаются приватными. Чтобы сделать такой пост публичным, владелец сначала переводит аккаунт в публичный режим, а затем меняет приватность поста на «Everyone» в TikTok.
PULL_FROM_URL или FILE_UPLOAD
Оба источника работают для видео в Direct Post и Upload. Фотопосты поддерживают только PULL_FROM_URL.
| Параметр | PULL_FROM_URL | FILE_UPLOAD |
|---|---|---|
| Как работает | TikTok скачивает медиа по вашему URL | Вы отправляете байты методом PUT на полученный upload_url |
| Предусловие | Домен или URL-префикс подтверждён в URL properties | Нет |
| Требования к адресу | HTTPS, без редиректов, домен, а не IP; загрузка прерывается через час после старта — адрес должен быть доступен всё это время | upload_url действует один час |
| Разбиение на части | Не нужно | Части по 5–64 МБ, последняя до 128 МБ; файл меньше 5 МБ отправляется целиком; до 1000 частей, строго по порядку |
| Число частей | Не нужно | total_chunk_count = video_size ÷ chunk_size, с округлением вниз |
| Заголовок | Не нужен | Каждая часть с Content-Range: bytes первый-последний/всего |
| Типичная ошибка | url_ownership_unverified | HTTP 403, если upload_url истёк |
| Socialift SDK | video_url=, photo_urls= | video= (байты или путь к файлу), опционально chunk_size= |
Примечания и ограничения
URL-префикс проверяется буквально: подтверждённый https://example.com/videos/user/ покрывает https://example.com/videos/user/123/a.mp4, но не https://example.com/videos/2023/user/123/a.mp4.
Требования к медиа
| Медиа | Требование |
|---|---|
| Контейнер видео | MP4 (рекомендуется), WebM, MOV |
| Кодек видео | H.264 (рекомендуется), H.265, VP8, VP9 |
| Частота кадров | 23–60 FPS |
| Разрешение | 360–4096 px по каждой стороне |
| Размер файла | До 4 ГБ |
| Длительность | Не больше max_video_post_duration_sec из creator info |
| Подпись к видео | До 2200 символов UTF-16 |
| Фото | WebP или JPEG, до 1080p, до 20 МБ каждое, до 35 изображений в посте |
| Заголовок / описание фотопоста | До 90 / 4000 символов UTF-16 |
Фотопосты, черновики и проверка статуса
Фото всегда берутся с подтверждённых URL. Загрузка во «Входящие» заканчивается статусом SEND_TO_USER_INBOX, поэтому проверяйте её через publish_status, а не ждите публикации.
from socialift import TikTok
async def carousel_and_draft(token: str) -> None:
async with TikTok.from_env() as tt:
# перед Direct Post — creator_info(), как в примере с видео
carousel = await tt.publish_photos(
token,
photo_urls=[
"https://media.example.com/tiktok/look-1.jpg",
"https://media.example.com/tiktok/look-2.jpg",
],
title="Осенний лукбук",
description="Два образа из новой коллекции",
privacy_level="SELF_ONLY",
cover_index=0,
wait=True,
)
print(carousel["status"]["status"]) # PUBLISH_COMPLETE
draft = await tt.upload_video(token, video="renders/raw-cut.mp4")
state = await tt.publish_status(draft["publish_id"], token)
print(state["status"]) # PROCESSING_UPLOAD или SEND_TO_USER_INBOXpublish_photos(..., draft=True) отправляет фото во «Входящие» автора в режиме MEDIA_UPLOAD; для этого нужен scope video.upload.
Статусы публикации
| Статус | Значение |
|---|---|
PROCESSING_UPLOAD | Идёт загрузка частей файла (только FILE_UPLOAD) |
PROCESSING_DOWNLOAD | TikTok скачивает файл по URL (только PULL_FROM_URL) |
SEND_TO_USER_INBOX | Режим Upload: уведомление пришло во «Входящие» автора |
PUBLISH_COMPLETE | Контент опубликован |
FAILED | Обработка остановлена; причина в fail_reason, например file_format_check_failed, duration_check_failed, frame_rate_check_failed, video_pull_failed, spam_risk_too_many_posts |
publicaly_available_post_id (в API написано именно так) возвращается только после того, как пост стал публичным и прошёл проверку; у приватных постов публичного ID нет.
Чек-лист публикации в TikTok для продакшена
- Запрашивайте creator info перед каждым Direct Post и стройте список приватности из
privacy_level_options. - Не задавайте значения по умолчанию для приватности и взаимодействий; отключённое автором делайте неактивным.
- Держитесь в пределах 6 init-запросов и 30 запросов статуса в минуту на один user token.
- Обрабатывайте
spam_risk_too_many_postsиspam_risk_too_many_pending_shareкак паузу, а не как цикл повторов. - Не накладывайте на контент для TikTok логотипы, водяные знаки, ссылки или рекламный текст.
- Храните медиа на подтверждённом HTTPS-префиксе без редиректов.
- Пишите в лог
log_idиз каждого ответа с ошибкой — его запрашивает поддержка TikTok.
Частые вопросы
Можно ли публиковать публичные видео в TikTok через API без аудита?
Нет. Клиент без аудита публикует только с видимостью SELF_ONLY, аккаунт автора в момент публикации должен быть приватным, а пользоваться клиентом могут до 5 пользователей за 24 часа. Для публичных постов приложение должно пройти аудит TikTok.
Почему TikTok возвращает url_ownership_unverified?
В режиме PULL_FROM_URL адрес медиа должен принадлежать домену или URL-префиксу, подтверждённому в URL properties консоли TikTok. Подтвердите домен или загрузите файл через FILE_UPLOAD — там подтверждение не требуется.
Сколько постов в день можно публиковать через Content Posting API?
Для Direct Post действует лимит на автора за 24 часа — обычно около 15 постов, у разных авторов по-разному. При достижении лимита приходит spam_risk_too_many_posts. В режиме Upload за любые 24 часа может быть не более 5 ожидающих отправок.
Нужно ли вызывать creator_info перед каждым постом?
Да. Правила TikTok требуют перед Direct Post получать актуальную информацию об авторе: доступные уровни приватности, отключённые взаимодействия и максимальная длительность видео могут меняться.
Можно ли загружать фото файлом?
Нет. Для фотопостов источник может быть только PULL_FROM_URL, то есть изображения берутся по HTTPS-ссылкам с подтверждённого домена. В одном посте может быть до 35 изображений.
Можно ли добавить на видео свой логотип или водяной знак?
Нет. Правила TikTok запрещают приложениям и интеграциям накладывать на контент, отправляемый в TikTok, название бренда, логотип, водяной знак, ссылку или рекламный текст.