TikTok Content Posting API Daily Limits, Quotas, and Features
When using TikTok, understanding the TikTok API daily limits is fundamental for automating your workflow. In this guide, I’ll help you set up and understand these API limitations. TikTok offers multiple APIs, and choosing the right one depends on your objective. This guide focuses on uploading and downloading your own content, as well as extracting information from your personal user account. More advanced APIs are available, but they require a lengthy verification and approval process. Those are typically used for more complex tasks such as market analysis, large-scale data extraction, or advanced ad targeting.
In this guide, we’ll only focus on using the Content Posting API for your personal account. The API Limit is 15 video uploads per 24 hours.
This tutorial is a simplified version of the work done in Repostit.io, you can skip all the troublesome parts and use our software for automation if you’re interested.

Authentication and User Access Token
The TikTok API requires an authorization token to access any endpoint. Obtaining this token is necessary to authenticate your requests and enable communication with the API, and here is how.
- Register your app: create an app on the TikTok developer portal and copy your client_key and client_secret.
- Run a local callback receiver: run a simple HTTP server on your machine (e.g.,
http://localhost:5000/callback) to catch the redirect and read thecodequery parameter. (Any lightweight server will do.) - Expose an HTTPS redirect URI: TikTok requires HTTPS. For local testing, create an HTTPS tunnel that forwards to your local server (for example, using a tunneling tool) and register the resulting HTTPS URL as the redirect URI in your app settings. Example redirect to register:
https://<your-tunnel-id>.example-tunnel.io/callback - Open the authorize URL: in your browser go to the TikTok authorize URL with your values:
https://www.tiktok.com/v2/auth/authorize/
?client_key=YOUR_CLIENT_KEY
&scope=user.info.basic,video.upload
&response_type=code
&redirect_uri=YOUR_HTTPS_TUNNEL_CALLBACK
&state=RANDOM_STRING
Log in and approve.
- Capture the code: after consent TikTok redirects to your registered redirect URI with
?code=AUTH_CODE&state=.... Your local server (or the browser URL) will contain theAUTH_CODE. - Exchange code for tokens (server-side): on your machine, send the
AUTH_CODEalong withclient_key,client_secret,grant_type=authorization_code, and the sameredirect_urito TikTok’s token endpoint. You will receiveaccess_tokenandrefresh_token. - Store and use tokens: keep tokens and
client_secreton your machine/server (securely). UseAuthorization: Bearer <access_token>in API requests. - Refresh when needed: when the access token expires, use the
refresh_tokento obtain a new access token.
Managing User Access Tokens with OAuth v2
TikTok uses OAuth v2 for authentication, handled through the TikTok Login Kit, which manages the entire token lifecycle. This system lets you integrate TikTok login and authorization directly into your app. Once a user successfully authorizes access, TikTok provides refreshable access tokens that allow your app to call API endpoints with their granted permissions.
Each endpoint on the TikTok API requires explicit user consent, granted at the scope level. Scopes define what your app can access, for example, user.info.basic lets you read a user’s avatar and display name, while video.list allows access to their public videos. Tokens must be managed securely, ideally stored and refreshed server-side. The access token is what your app uses to make API requests on behalf of the user, while the refresh token is used to generate a new access token when it expires. For most integrations, you’ll authorize users through: https://www.tiktok.com/v2/auth/authorize/
This URL is used across web and desktop apps, and it requires a registered redirect URI on your TikTok Developer dashboard. If you’re developing for mobile, TikTok recommends using the OpenSDK for iOS or Android, which manages the same process natively.
If you’re still using older endpoints (like /auth/authorize/), TikTok advises migrating to the new v2 endpoints as soon as possible.
TikTok Content Posting API Structure
The TikTok API is part of TikTok for Developers, a platform that allows developers to integrate TikTok features, such as posting videos, retrieving user data, or analyzing content, directly into their own applications. Just like most REST APIs, TikTok’s API uses HTTP requests and JSON-formatted data to send and receive information. Each request must include specific headers (like the Authorization token) and a JSON body containing the required fields for the chosen endpoint.
When making a request, the necessary fields in the JSON payload depend on the endpoint being used. For example, when uploading a video, you typically need to include:
- privacy_level – Determines who can see the uploaded video (e.g.,
PUBLIC,FRIENDS, orPRIVATE). - access_token – The user’s authorization token, obtained through the
/v2/oauth/token/endpoint.
Below is a simple example of how you could upload a video to TikTok using the API:
# PULL_FROM_URL example
import requests
import json
# === CONFIG – replace with your values ===
ACCESS_TOKEN = "YOUR_USER_ACCESS_TOKEN" # token from OAuth, with video.upload scope
VIDEO_URL = "https://your-verified-domain.com/path/to/video.mp4"
API_ENDPOINT = "https://open.tiktokapis.com/v2/post/publish/inbox/video/init/"
# === Prepare request ===
headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/json; charset=UTF-8"
}
body = {
"source_info": {
"source": "PULL_FROM_URL",
"video_url": VIDEO_URL
}
}
# === Execute request ===
resp = requests.post(API_ENDPOINT, headers=headers, json=body, timeout=20)
print("Status code:", resp.status_code)
try:
resp_json = resp.json()
except ValueError:
print("No JSON response:", resp.text)
resp_json = None
print("Response JSON:", json.dumps(resp_json, indent=2))Headers Structure of the API
The header structure in the TikTok API is presented here. First, the process for obtaining authentication was shown in the previous steps. The following table lists the essential headers required for requests to the TikTok API, including their purpose, value format, and whether they are mandatory.
| Field Name | Description | Value | Required |
|---|---|---|---|
| Authorization | The token that bears the authorization of the TikTok user, obtained through /oauth/access_token/. |
Bearer {$UserAccessToken} |
true |
| Content-Type | The content format of the body of this HTTP request. | application/json; charset=UTF-8 |
true |
Body Structure of the Content Posting API
The TikTok API supports multiple types of content and operations. You can use it to post videos or photos, upload content for users to complete in TikTok, query creator information, fetch post status, and receive webhook notifications about content publishing. It also provides access to metadata, engagement metrics, and content analytics.
For example, you can structure a request to upload a video directly to a creator’s account with the Content Posting API:
# FILE_UPLOAD example (use real numeric values for sizes/counts)
import requests
import json
url = "https://open.tiktokapis.com/v2/post/publish/inbox/video/init/"
headers = {
"Authorization": "Bearer act.example12345Example12345Example",
"Content-Type": "application/json; charset=UTF-8"
}
# Replace these example values with real numbers (bytes/chunk counts)
example_video_size = 12345678
example_chunk_size = 5242880
example_total_chunk_count = 3
data = {
"source_info": {
"source": "FILE_UPLOAD",
"video_size": example_video_size,
"chunk_size": example_chunk_size,
"total_chunk_count": example_total_chunk_count
}
}
response = requests.post(url, headers=headers, json=data)
print(response.status_code)
try:
print(json.dumps(response.json(), indent=2))
except ValueError:
print("No JSON response:", response.text)and the response will be structured as follows.
// Example fixed response JSON
{
"data": {
"publish_id": "v_inbox_file~v2.123456789",
"upload_url": "https://open-upload.tiktokapis.com/video/?upload_id=12345&upload_token=Xza123"
},
"error": {
"code": "ok",
"message": "",
"log_id": "202210112248442CB9319E1FB30C1073F3"
}
}All the possible body inputs and response fields are described in the tables below.
Video Direct Post
Headers
| Field Name | Description | Value | Required |
|---|---|---|---|
| Authorization | The token that bears the authorization of the TikTok user, obtained through /oauth/access_token/. |
Bearer {$UserAccessToken} |
true |
| Content-Type | The content format of the body of this HTTP request. | application/json; charset=UTF-8 |
true |
Request Body Fields
| Field Name | Nested Field Name | Type | Description | Required |
|---|---|---|---|---|
| post_info | privacy_level | string | Options: PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY | true |
| post_info | title | string | The video caption. Max 2200 UTF-16 characters. Optional. | false |
| post_info | disable_duet | bool | If true, prevents others from making Duets. | false |
| post_info | disable_stitch | bool | If true, prevents others from making Stitches. | false |
| post_info | disable_comment | bool | If true, disables comments on this post. | false |
| post_info | video_cover_timestamp_ms | int32 | Specifies which frame is used as the video cover. | false |
| post_info | brand_content_toggle | bool | Set true if video is a paid partnership. | true |
| post_info | brand_organic_toggle | bool | Set true if promoting creator’s own business. | false |
| post_info | is_aigc | bool | Set true if video is AI generated content. | false |
| source_info | source | string | Choose from: PULL_FROM_URL, FILE_UPLOAD | true |
| source_info | video_url | string | URL for PULL_FROM_URL method. | Conditional |
| source_info | video_size | int64 | Video size in bytes for FILE_UPLOAD method. | Conditional |
| source_info | chunk_size | int64 | Size of each chunk in bytes. | Conditional |
| source_info | total_chunk_count | int64 | Total number of chunks. | Conditional |
Response Fields
| Field Name | Nested Field | Type | Description |
|---|---|---|---|
| data | publish_id | string | Identifier to track the posting action (max 64 characters). |
| data | upload_url | string | URL provided by TikTok to upload the video (only for FILE_UPLOAD). |
| error | code | string | Error code. “ok” indicates success. |
| error | message | string | Human-readable description of the error. |
| error | logid | string | Unique identifier for the execution of this request. |
Upload Video Headers
| Field Name | Description | Value | Required |
|---|---|---|---|
| Content-Type | The content format of the body of this HTTP request. | video/mp4 | video/quicktime | video/webm | true |
| Content-Length | Byte size of this chunk. | {BYTE_SIZE_OF_THIS_CHUNK} | true |
| Content-Range | Metadata describing the portion of the overall file contained in this chunk. | bytes {FIRST_BYTE}-{LAST_BYTE}/{TOTAL_BYTE_LENGTH} | true |
Video Upload
Headers
| Field Name | Description | Value | Required |
|---|---|---|---|
| Authorization | The token that bears the authorization of the TikTok user, obtained through /oauth/access_token/. |
Bearer {$UserAccessToken} |
true |
| Content-Type | The content format of the body of this HTTP request. | application/json; charset=UTF-8 |
true |
Request Body Fields
| Field Name | Nested Field | Type | Description | Required |
|---|---|---|---|---|
| source_info | source | string | Mechanism to provide the video: FILE_UPLOAD or PULL_FROM_URL | true |
| source_info | video_size | int64 | Video size in bytes (required for FILE_UPLOAD) | Conditional |
| source_info | chunk_size | int64 | Size of each chunk in bytes (required for FILE_UPLOAD) | Conditional |
| source_info | total_chunk_count | int64 | Total number of chunks (required for FILE_UPLOAD) | Conditional |
| source_info | video_url | string | URL of the video to upload (required for PULL_FROM_URL, must be verified) | Conditional |
Response Fields
| Field Name | Nested Field | Type | Description |
|---|---|---|---|
| data | publish_id | string | Identifier to track the upload action (max 64 characters) |
| data | upload_url | string | URL provided by TikTok for file upload (only for FILE_UPLOAD) |
| error | code | string | Error code. “ok” indicates success |
| error | message | string | Human-readable description of the error |
| error | logid | string | Unique identifier for this request execution |
Upload Video Headers
| Field Name | Description | Value | Required |
|---|---|---|---|
| Content-Type | The content format of this HTTP request body | video/mp4 | video/quicktime | video/webm | true |
| Content-Length | Byte size of this chunk | {BYTE_SIZE_OF_THIS_CHUNK} | true |
| Content-Range | Metadata describing the portion of the overall file in this chunk | bytes {FIRST_BYTE}-{LAST_BYTE}/{TOTAL_BYTE_LENGTH} | true |
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | invalid_param | Check error message for details |
| 403 | spam_risk_too_many_pending_share | Daily upload cap reached. Max 5 pending shares in 24h. |
| 403 | spam_risk_user_banned_from_posting | User is banned from posting |
| 403 | url_ownership_unverified | URL prefix/domain must be verified for PULL_FROM_URL |
| 401 | access_token_invalid | Access token is invalid or expired |
| 401 | scope_not_authorized | Access token does not have video.upload scope |
| 429 | rate_limit_exceeded | Request blocked due to exceeding API rate limit |
| 5xx | – | TikTok server or network error. Try again later |
Photo
Headers
| Field Name | Description | Value | Required |
|---|---|---|---|
| Authorization | The token that bears the authorization of the TikTok user, obtained through /oauth/access_token/. |
Bearer {$UserAccessToken} |
true |
| Content-Type | The content format of the body of this HTTP request | application/json; charset=UTF-8 |
true |
Request Body Fields
| Field | Type | Description | Required |
|---|---|---|---|
| media_type | string | Currently only PHOTO is allowed | true |
| post_mode | string | DIRECT_POST: post directly; MEDIA_UPLOAD: upload for user to complete in TikTok | true |
| post_info | Post Info Object | The post information | true |
| source_info | Source Info Object | The media source information | true |
Post Info Object
| Field | Type | Description | Required |
|---|---|---|---|
| title | string | The post title (max 90 UTF-16 characters) | false |
| description | string | The post description (max 4000 UTF-16 characters) | false |
| privacy_level | string | PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY | Required for DIRECT POST |
| disable_comment | bool | Prevents comments (only for DIRECT POST) | false |
| auto_add_music | bool | Adds recommended music automatically (only for DIRECT POST) | false |
| brand_content_toggle | bool | Set true if content is a paid partnership (only for DIRECT POST) | true |
| brand_organic_toggle | bool | Set true if promoting creator’s own business (only for DIRECT POST) | true |
Source Info Object
| Field | Type | Description | Required |
|---|---|---|---|
| source | string | Only PULL_FROM_URL is allowed | true |
| photo_images | list<string> | Array of up to 35 publicly accessible photo URLs | true |
| photo_cover_index | int | Index of the photo to use as cover (starting from 0) | true |
Response Fields
| Field Name | Nested Field | Type | Description |
|---|---|---|---|
| data | publish_id | string | Identifier to track the post (max 64 characters) |
| error | code | string | Error code. “ok” indicates success |
| error | message | string | Human-readable description of the error |
| error | logid | string | Unique identifier for request execution |
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | invalid_param | Check error message for details |
| 400 | app_version_check_failed | To use MEDIA_UPLOAD post_mode, users’ TikTok app version must be >= 31.8 |
| 403 | spam_risk_too_many_posts | The daily post cap from the API is reached for the current user. |
| 403 | spam_risk_user_banned_from_posting | The user is banned from making new posts. |
| 403 | spam_risk_too_many_pending_share | Daily upload cap reached for pending shares. Max 5 pending shares within any 24-hour period. |
| 403 | reached_active_user_cap | The daily quota for active publishing users from your client is reached. |
| 403 | unaudited_client_can_only_post_to_private_accounts | Unaudited clients can only post to private accounts; publish attempts will be blocked for public posts. |
| 403 | url_ownership_unverified | To use PULL_FROM_URL, the developer must verify the URL prefix or domain ownership. |
| 403 | privacy_level_option_mismatch | privacy_level is unspecified or not among the options returned by /publish/creator_info/query/. |
| 401 | access_token_invalid | The access token is invalid or has expired. |
| 401 | scope_not_authorized | The access token does not include the required video.publish or video.upload scope. |
| 429 | rate_limit_exceeded | Your request is blocked due to exceeding the API rate limit. |
| 5xx | internal_error | TikTok server or network error. Try again later. |
Query Creator Info
Headers
| Field Name | Description | Value | Required |
|---|---|---|---|
| Authorization | The token that bears the authorization of the TikTok user, obtained through /oauth/access_token/. |
Bearer {$UserAccessToken} |
true |
| Content-Type | The content format of the body of this HTTP request. | application/json; charset=UTF-8 |
true |
Response Fields
| Field Name | Nested Field | Type | Description |
|---|---|---|---|
| data | creator_avatar_url | string | URL of the TikTok creator’s avatar (TTL 2 hours) |
| data | creator_username | string | Unique ID of the TikTok creator |
| data | creator_nickname | string | Nickname of the TikTok creator |
| data | privacy_level_options | list<string> | Available privacy options based on account type:
|
| data | comment_disabled | boolean | True if creator disables comments |
| data | duet_disabled | boolean | True if creator disables Duet interaction (ignored for photo-only clients) |
| data | stitch_disabled | boolean | True if creator disables Stitch interaction (ignored for photo-only clients) |
| data | max_video_post_duration_sec | int32 | Max video duration in seconds the creator can post (ignored for photo-only clients) |
| error | code | string | Error code. “ok” indicates success |
| error | message | string | Human-readable description of the error |
| error | logid | string | Unique identifier for request execution |
Error Codes
| HTTP Status | Error Code | Description |
|---|---|---|
| 200 | ok | Request was successful |
| 200 (intentional) | spam_risk_too_many_posts | Daily post cap reached |
| 200 (intentional) | spam_risk_user_banned_from_posting | User is banned from making new posts |
| 200 (intentional) | reached_active_user_cap | Daily quota for active publishing users reached |
| 401 | access_token_invalid | Access token is invalid or expired |
| 401 | scope_not_authorized | Access token does not bear user’s grant on video.publish scope |
| 429 | rate_limit_exceeded | Request blocked due to exceeding API rate limit |
| 5xx | – | TikTok server or network error. Try again later |
Get Post Status
Fetch Status Response Fields
| Field | Type | Description |
|---|---|---|
| status | string |
Available statuses:
|
| fail_reason | string | See fail_reason table to determine the issue |
| publicaly_available_post_id | list<int64> | Post ID returned only if content is publicly viewable and approved by moderation |
| uploaded_bytes | int64 | Number of bytes uploaded for FILE_UPLOAD |
| downloaded_bytes | int64 | Number of bytes downloaded for PULL_FROM_URL |
Nested Error Fields
| HTTP Status | Error Code | Description |
|---|---|---|
| 200 | ok | Request was successful |
| 400 | invalid_publish_id | The publish_id does not exist |
| 400 | token_not_authorized_for_specified_publish_id | Access token does not have authorization for this publish_id |
| 401 | access_token_invalid | Access token is invalid or expired |
| 401 | scope_not_authorized | Access token does not bear grant on video.upload or video.publish |
| 429 | rate_limit_exceeded | Request blocked due to exceeding API rate limit |
| 5xx | internal_error | TikTok server or network error. Try again later |
Content Posting Webhooks
| Event Name | Event Values | Description |
|---|---|---|
| post.publish.failed | publish_id, reason, publish_type | Publishing not successful. Failure reason provided as enum. publish_type = INBOX_SHARE for Upload endpoint |
| post.publish.complete | publish_id, publish_type | Content uploaded successfully by user from inbox (Upload endpoint) |
| post.publish.inbox_delivered | publish_id, publish_type | Notification sent to creator’s inbox to complete draft post. publish_type = INBOX_SHARE |
| post.publish.publicly_available | publish_id, post_id, publish_type | Post has become publicly viewable on TikTok |
| post.publish.no_longer_publicaly_available | publish_id, post_id, publish_type | Post is no longer publicly viewable |
Fail Reasons
| fail_reason | Guidance |
|---|---|
| file_format_check_failed | Unsupported media format. See Video/Photo Restrictions |
| duration_check_failed | Video duration violates restrictions |
| frame_rate_check_failed | Unsupported frame rate |
| picture_size_check_failed | Unsupported picture size |
| internal | Temporary server issue. Retryable |
| video_pull_failed | TikTok server failed downloading video. Retry recommended if URL valid |
| photo_pull_failed | TikTok server failed downloading photo. Retry recommended if URL valid |
| publish_cancelled | Developer cancelled ongoing download/upload. Webhook sent |
| auth_removed | Creator removed developer access during upload/download. Do not retry |
| spam_risk_too_many_posts | Creator posted too many times in 24 hours. Use TikTok app |
| spam_risk_user_banned_from_posting | Creator banned from posting. Do not retry |
| spam_risk_text | Text content considered risky/spam. Publishing attempt terminated |
| spam_risk | Publishing request considered risky. Attempt terminated |
TikTok API Daily Limits
TikTok imposes limits on API usage to prevent abuse and ensure fair access for all creators. These limits apply to both the number of requests per minute and the total content posted per 24-hour period.
- API Limit: 15 video uploads per 24 hours per account (varies depending on account tier).
- Regular creators will likely hit the standard limit, some business accounts may have extended limits.
- If you exceed this, TikTok queues or rejects new uploads until reset.
The TikTok guide states that: “Request Restriction: Each user access_token is limited to 6 requests per minute” and “The daily upload cap from the API is reached for the current user. To reduce spamming, TikTok limits the number of videos that can be uploaded via API that are not pending approval and posting by the creator. There may be at most 5 pending shares within any 24-hour period.”
In the guideline presented on the TikTok Creator Guide page, they outline the following structure “There is a limit on the number of posts that can be made to a creator account in a 24-hour window via Direct Post API. The upper limit may vary among creators (typically around 15 posts per day/ creator account) and is shared across all API Clients using Direct Post.”. An can be summarize in the following table:
| Account / API Type | Max Posts per 24h |
|---|---|
| Creator account (typical) | ~15 |
| Unaudited API client (users) | 5 users total |
| Audited API client | Varies (depends on audit) |
Multiple Accounts, One Less Hassle
Managing multiple TikTok accounts can be tricky, as each API key has a limited number of requests and switching between accounts can be cumbersome. To make your life easier, you can use Repostit.io, which handles multiple accounts and workflows seamlessly, without the usual hassle.
Conclusion
This tutorial provided a clear example of how to use the TikTok API and explained the daily limits for content posting. For a simpler approach, you can use Repostit.io, which offers the API along with a frontend to manage multiple channels, sources, and platforms, all in a single streamlined workflow.