Answers
TikTokApi keeps throwing EmptyResponseException. Is there a more reliable way to get TikTok data?
Short answer
EmptyResponseException means TikTok treated your automated browser session as a bot and sent back nothing. If you only need public data, a hosted API skips the browser, ms_token and proxies: okdata returns a video for $0.002, a profile for $0.002 and a search page of about 30 videos for $0.002, and failed calls cost nothing.
The script worked on your laptop on Friday. On Monday, running on a server, every video.info() and user.info() call ends in EmptyResponseException: TikTok returned an empty response. Nothing is wrong with your code. TikTokApi opens a headless browser through Playwright, and TikTok's bot detection decided that browser was not a person, so it answers with an empty body.
Things to try before switching
The library's own message lists them, and they do help for small jobs:
- Pass a fresh
ms_tokencopied from the cookies of a logged-in browser. - Run with
headless=False, or trybrowser="webkit". - Use a residential proxy instead of a data-center IP, and slow down.
The catch is upkeep. Tokens expire, proxies cost money, and each TikTok front-end change can break things again. If the job is reading public numbers, not automating an account, you can drop the browser.
The same calls without a browser
| TikTokApi | Endpoint | Price |
|---|---|---|
api.video(url=...).info() |
TikTok video detail | $0.002 |
api.user(username).info() |
TikTok account profile | $0.002 |
| Keyword video search, a common feature request | TikTok video search | $0.002 per page |
video.comments() |
TikTok video comments | $0.002 per page |
Not everything has a twin. There is no endpoint for a user's full video list, hashtag pages, trending feeds or video downloads.
import os, requests
API = "https://api.okdata.online/v1"
H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
def call(path, **params):
body = requests.get(f"{API}/{path}", headers=H, params=params, timeout=60).json()
if body["code"] != "ok":
# every error says whether you were charged and whether a retry makes sense
raise RuntimeError(f"{body['code']} charged={body['charged']} retry={body['retry']}")
return body["data"]
video = call("tiktok/videos/get", url="https://www.tiktok.com/@scout2015/video/6718335390845095173")["item"]
author = call("tiktok/users/get", username=video["author"]["username"])["item"]
print(video["metrics"]["views"], video["metrics"]["likes"], author["followers"])
What changes in your code
- Responses are plain JSON in one shape for every platform:
data.itemfor one object,data.itemspluspage.next_cursorfor lists. No async, no browser lifecycle. - Errors come back as codes.
upstream_busyandupstream_failedare worth retrying;not_foundmeans the video or account is gone. None of them are charged. The full table is on errors. - Field names differ from TikTok's raw JSON:
playCountbecomesmetrics.views,diggCountbecomesmetrics.likes.
When to stay with TikTokApi
Keep it if you need things this catalog does not offer, such as a profile's full video list, or if you run a few hundred requests a month from a home connection where it still works. It is free and open source. For research at scale with academic credentials, TikTok's Research API is the official route.
Updated