TikTok API Python Tutorial: OAuth, Upload, Read Videos

By Nick ReyesPublished 5 min read

The short answer

To use the TikTok API from Python, call TikTok's official endpoints with the requests library. Exchange the Login Kit code at open.tiktokapis.com/v2/oauth/token/, upload video through the Content Posting API with FILE_UPLOAD chunks or PULL_FROM_URL, and read profiles and videos with the user info and video list endpoints. Adeli publishes to TikTok through TikTok API for Business in one POST.

You use the TikTok API from Python by calling TikTok's official HTTP endpoints with the requests library. There are three pieces: Login Kit's OAuth flow for a user access token, the Content Posting API to upload video, and the user info and video list endpoints (often called the Display API) to read a profile and its videos. This tutorial has working Python for each, written against TikTok's documented requests.

Everything below is TikTok for Developers. TikTok API for Business is a separate portal with its own tokens; TikTok API for Business vs. Developers explains which one you need.

Is there an official TikTok Python library?

No official Python SDK covers TikTok for Developers, so you call it with requests. TikTok's docs for Login Kit, the Content Posting API, and the Display API are curl examples, and each one maps to a requests.get or requests.post. The requests docs, from the library created by Kenneth Reitz (release 2.34.2, May 2026), describe it like this:

"Requests is an elegant and simple HTTP library for Python, built for human beings."

Source: Requests documentation

With plain requests, a new TikTok field is one more string in fields, with no SDK release to wait for. Before you search PyPI, know two things:

  • TikTok's official SDK is for the Business API. TikTok's tiktok-business-api-sdk (pip install tiktok-business-api-sdk-official) covers Marketing API areas such as authentication, campaign creation, reporting, and Business Center. It doesn't document Login Kit or the Content Posting API.
  • Scraping libraries aren't the TikTok API. Packages that read TikTok by imitating its web app skip OAuth and the account owner's consent. They break when TikTok changes its app, and they can't publish on a customer's behalf.

What do you need before you start?

  • Python 3 and requests. pip install requests.
  • A TikTok for Developers app with Login Kit and the Content Posting API added. Our guide on how to get TikTok API access covers the review.
  • Scopes. user.info.basic for the profile, user.info.stats for counts, video.list to read videos, and video.publish (direct post) or video.upload (drafts) to post.
  • A registered redirect URI. HTTPS, static, with no query string or fragment, per TikTok's Login Kit for Web guide.

Keep secrets in environment variables:

import os
import secrets
import urllib.parse
 
import requests
 
CLIENT_KEY = os.environ["TIKTOK_CLIENT_KEY"]
CLIENT_SECRET = os.environ["TIKTOK_CLIENT_SECRET"]
REDIRECT_URI = "https://yourapp.example/tiktok/callback"
API = "https://open.tiktokapis.com/v2"

Step 1: Send the user to TikTok's authorize page

Build the authorize URL with your client key, scopes, response_type=code, the redirect URI, and a random state.

def authorize_url() -> tuple[str, str]:
    state = secrets.token_urlsafe(16)
    params = {
        "client_key": CLIENT_KEY,
        "scope": "user.info.basic,user.info.stats,video.list,video.publish",
        "response_type": "code",
        "redirect_uri": REDIRECT_URI,
        "state": state,
    }
    return "https://www.tiktok.com/v2/auth/authorize/?" + urllib.parse.urlencode(params), state

Store state in the user's session. When TikTok redirects back, check that the returned state matches before you use the code. That's TikTok's CSRF protection. If a scope is toggleable, the user can deny it and still grant the others, so read the granted scope from the token response rather than assuming.

Step 2: Exchange the code for an access token

POST the code to https://open.tiktokapis.com/v2/oauth/token/ as form data, not JSON. TikTok's token management guide requires Content-Type: application/x-www-form-urlencoded, which requests sets when you pass data=:

def exchange_code(code: str) -> dict:
    resp = requests.post(
        f"{API}/oauth/token/",
        data={
            "client_key": CLIENT_KEY,
            "client_secret": CLIENT_SECRET,
            "code": code,  # URL-decoded, which your web framework does for you
            "grant_type": "authorization_code",
            "redirect_uri": REDIRECT_URI,
        },
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()

The response carries access_token, expires_in, open_id, refresh_token, refresh_expires_in, scope, and token_type. The access token lasts 24 hours and the refresh token lasts 365 days, so refresh on a schedule:

def refresh(refresh_token: str) -> dict:
    resp = requests.post(
        f"{API}/oauth/token/",
        data={
            "client_key": CLIENT_KEY,
            "client_secret": CLIENT_SECRET,
            "grant_type": "refresh_token",
            "refresh_token": refresh_token,
        },
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()  # save BOTH tokens: the refresh token can change

code_verifier (PKCE) is only for mobile and desktop apps, per TikTok's guide. Our TikTok API access token post covers the Business API token and the errors that mean a token is dead.

Step 3: Upload a video with FILE_UPLOAD chunks

Initialize the post, then PUT the file to upload_url in order, one chunk at a time. TikTok's media transfer guide sets the chunk rules:

  • Chunks are 5 MB to 64 MB. The final chunk can be up to 128 MB, to absorb the leftover bytes.
  • Files under 5 MB go in one piece, with chunk_size equal to the file size.
  • total_chunk_count is video_size // chunk_size, from 1 to 1,000 chunks.
  • Each PUT returns 206 until the last one, which returns 201.

First ask TikTok which privacy levels this creator allows. Your app must use one of them:

def creator_info(token: str) -> dict:
    resp = requests.post(
        f"{API}/post/publish/creator_info/query/",
        headers={"Authorization": f"Bearer {token}",
                 "Content-Type": "application/json; charset=UTF-8"},
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()["data"]  # privacy_level_options, max_video_post_duration_sec, ...

Then initialize and upload:

CHUNK = 10 * 1024 * 1024  # 10 MB
 
def upload_video(token: str, path: str, title: str, privacy_level: str) -> str:
    size = os.path.getsize(path)
    chunk_size = min(size, CHUNK)
    count = size // chunk_size
 
    init = requests.post(
        f"{API}/post/publish/video/init/",
        headers={"Authorization": f"Bearer {token}",
                 "Content-Type": "application/json; charset=UTF-8"},
        json={
            "post_info": {"title": title, "privacy_level": privacy_level},
            "source_info": {
                "source": "FILE_UPLOAD",
                "video_size": size,
                "chunk_size": chunk_size,
                "total_chunk_count": count,
            },
        },
        timeout=30,
    )
    init.raise_for_status()
    data = init.json()["data"]
 
    with open(path, "rb") as f:
        for i in range(count):
            start = i * chunk_size
            # the last chunk takes every remaining byte
            end = size - 1 if i == count - 1 else start + chunk_size - 1
            f.seek(start)
            body = f.read(end - start + 1)
            put = requests.put(
                data["upload_url"],
                headers={"Content-Type": "video/mp4",
                         "Content-Range": f"bytes {start}-{end}/{size}"},
                data=body,
                timeout=300,
            )
            if put.status_code not in (201, 206):
                raise RuntimeError(f"chunk {i}: {put.status_code} {put.text}")
 
    return data["publish_id"]

Retry a chunk on a 5xx, which TikTok's guide says to do. A 403 means upload_url expired, and a 416 means your Content-Range doesn't match what TikTok received. The direct post init is limited to 6 requests per minute per user token.

To send a draft to the creator's inbox instead, use /post/publish/inbox/video/init/ with the video.upload scope and no post_info. TikTok Direct Post API vs Upload to Inbox compares the two.

Step 4: Or let TikTok pull the video from a URL

Set source to PULL_FROM_URL and pass a video_url; TikTok downloads the file, so there's no upload loop.

"source_info": {
    "source": "PULL_FROM_URL",
    "video_url": "https://media.yourapp.example/clips/launch.mp4",
}
FILE_UPLOAD PULL_FROM_URL
Your code Chunked PUTs to upload_url One init call
Setup None Verify the domain or URL prefix in the developer portal
URL rules n/a HTTPS, no redirects, reachable for the download (it times out after one hour)
Typical error 416 on a bad Content-Range url_ownership_unverified on an unverified domain

Use PULL_FROM_URL when the file already sits in storage you control. Presigned object-storage URLs only work if their host is a domain or prefix you verified.

Step 5: Check the publish status

Poll /post/publish/status/fetch/ with the publish_id until it reads PUBLISH_COMPLETE or FAILED.

import time
 
def wait_for_publish(token: str, publish_id: str) -> dict:
    for _ in range(20):
        resp = requests.post(
            f"{API}/post/publish/status/fetch/",
            headers={"Authorization": f"Bearer {token}",
                     "Content-Type": "application/json; charset=UTF-8"},
            json={"publish_id": publish_id},
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()["data"]
        if data["status"] in ("PUBLISH_COMPLETE", "FAILED", "SEND_TO_USER_INBOX"):
            return data
        time.sleep(5)
    raise TimeoutError(publish_id)

Until TikTok audits your app, every direct post is SELF_ONLY, and only private accounts can post. Our guide to posting to TikTok via API covers the audit, privacy rules, and the posting screen TikTok checks.

How do you read a TikTok profile and videos with Python?

Call GET /v2/user/info/ with a fields list, and POST /v2/video/list/ with fields in the query string and paging in the JSON body. Each field needs its scope: per TikTok's user info reference, follower_count, following_count, likes_count, and video_count need user.info.stats.

def user_info(token: str) -> dict:
    resp = requests.get(
        f"{API}/user/info/",
        headers={"Authorization": f"Bearer {token}"},
        params={"fields": "open_id,display_name,avatar_url,follower_count,video_count"},
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()["data"]["user"]
 
def all_videos(token: str):
    cursor = None
    while True:
        body = {"max_count": 20}
        if cursor:
            body["cursor"] = cursor
        resp = requests.post(
            f"{API}/video/list/",
            headers={"Authorization": f"Bearer {token}"},
            params={"fields": "id,title,create_time,share_url,view_count,like_count,comment_count"},
            json=body,
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()["data"]
        yield from data["videos"]
        if not data["has_more"]:
            break
        cursor = data["cursor"]

max_count tops out at 20 per page, per the video list reference, and cursor is a Unix timestamp in milliseconds. cover_image_url links expire after 6 hours, so don't store them. These are lifetime counters with no history; the TikTok analytics API post covers the Business API endpoints that return daily metrics.

How do you publish to TikTok with Adeli in Python?

One POST to https://app.tryadeli.com/api/v1/posts replaces Steps 1 to 5 and the refresh job. Adeli publishes to TikTok through TikTok API for Business, holds the TikTok app, and refreshes every connected account's tokens. Your code sends one API key:

import os
 
import requests
 
resp = requests.post(
    "https://app.tryadeli.com/api/v1/posts",
    headers={"Authorization": f"Bearer {os.environ['ADELI_API_KEY']}"},
    json={
        "platform": "tiktok",
        "accountId": "00000000-0000-0000-0000-000000000000",
        "post_mode": "DIRECT_POST",
        "tiktok_title": "Launch day #buildinpublic",
        "privacy_level": "PUBLIC_TO_EVERYONE",
        "music_usage_confirmed": True,
        "video": {"url": "https://media.example/video.mp4"},
    },
    timeout=60,
)
print(resp.status_code, resp.json())

What changes compared with the direct calls:

  • No domain verification on your side. Send base64, multipart, or a URL, and Adeli stages it on its own verified media host before TikTok pulls it.
  • No private-until-audit phase. A DIRECT_POST video publishes publicly; post_mode: "MEDIA_UPLOAD" sends it to the creator's inbox as a draft.
  • Status and creator info are endpoints too: TikTok publish status and TikTok creator info.

TikTok's limit of 15 API posts per account per day still applies. Every field is in the Publish to TikTok docs, and the same endpoint in Adeli's posting API publishes to Instagram, YouTube, Facebook, X, and Bluesky. Your first 2 user profiles are free on pricing.

faq

Frequently asked questions

Is there an official TikTok API library for Python?

Not for TikTok for Developers. Login Kit, the Content Posting API, and the Display API are documented as HTTP requests, so Python code calls them with requests. TikTok publishes an official Business API SDK on GitHub, installed as tiktok-business-api-sdk-official, which covers Marketing API areas such as authentication, campaigns, and reporting.

Can I upload a video to TikTok with Python?

Yes. Initialize the post with the Content Posting API, then PUT the file to the returned upload_url in chunks of 5 MB to 64 MB, or pass a URL on a verified domain with PULL_FROM_URL. Until TikTok audits your app, every post is private to the creator.

Are Python TikTok scraping libraries the official API?

No. Packages that read TikTok by imitating the web or mobile app aren't TikTok's API and don't use OAuth. They can break when TikTok changes its app, and they don't give you consent from the account owner, which a product acting on customers' accounts needs.

How long does a TikTok access token last?

A TikTok for Developers user access token lasts 24 hours and the refresh token lasts 365 days. Refresh with grant_type set to refresh_token at the same token endpoint, and save the new refresh token if it changes.

Power your next project
with one ads & social media API

Give your users publishing, scheduling, and analytics through one integration your team can actually maintain.

Try with agents

Start with free credits. No credit card required.