curl --request POST \
--url https://api.vidnavigator.com/v1/tiktok/live/search \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"query": "gaming",
"max_results": 0,
"country": "gb",
"include_stream_urls": true,
"webhook_url": "https://example.com/hooks/vidnavigator"
}
'import requests
url = "https://api.vidnavigator.com/v1/tiktok/live/search"
payload = {
"query": "gaming",
"max_results": 0,
"country": "gb",
"include_stream_urls": True,
"webhook_url": "https://example.com/hooks/vidnavigator"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
query: 'gaming',
max_results: 0,
country: 'gb',
include_stream_urls: true,
webhook_url: 'https://example.com/hooks/vidnavigator'
})
};
fetch('https://api.vidnavigator.com/v1/tiktok/live/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.vidnavigator.com/v1/tiktok/live/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => 'gaming',
'max_results' => 0,
'country' => 'gb',
'include_stream_urls' => true,
'webhook_url' => 'https://example.com/hooks/vidnavigator'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vidnavigator.com/v1/tiktok/live/search"
payload := strings.NewReader("{\n \"query\": \"gaming\",\n \"max_results\": 0,\n \"country\": \"gb\",\n \"include_stream_urls\": true,\n \"webhook_url\": \"https://example.com/hooks/vidnavigator\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.vidnavigator.com/v1/tiktok/live/search")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"gaming\",\n \"max_results\": 0,\n \"country\": \"gb\",\n \"include_stream_urls\": true,\n \"webhook_url\": \"https://example.com/hooks/vidnavigator\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vidnavigator.com/v1/tiktok/live/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"gaming\",\n \"max_results\": 0,\n \"country\": \"gb\",\n \"include_stream_urls\": true,\n \"webhook_url\": \"https://example.com/hooks/vidnavigator\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"task_id": "<string>",
"task_status": "processing",
"query": "<string>",
"country": "us",
"max_results": 123,
"include_stream_urls": true,
"expires_at": "2023-11-07T05:31:56Z",
"check_status_url": "/v1/tiktok/live/search/550e8400-e29b-41d4-a716-446655440000",
"message": "<string>",
"webhook_url": "<string>"
}
}{
"status": "error",
"error": "<string>",
"message": "<string>"
}{
"status": "error",
"error": "metadata_fetch_failed",
"message": "<string>"
}TikTok LIVE Search
Search TikTok LIVE rooms broadcasting right now for a keyword, and return a task_id immediately. Poll GET /tiktok/live/search/{task_id} for cursor-paginated results. Each room comes with its author, live counters (stats.viewers, likes, …), hashtags, category, game_tags and, unless include_stream_urls is false, its playback URLs (FLV and HLS by quality, plus an audio-only stream). Results are retained for ~1 hour.
Results keep TikTok’s ranking and are localized to country. TikTok serves about 50 rooms per keyword; some keywords have no live room in a given country at a given time, which completes with zero results.
Billing: 1 residential_request per 30 rooms TikTok returned (stats.rooms_fetched), minimum 1 per search. A keyword typically yields up to about 45 rooms, so 1 or 2 residential requests.
Usage disclosure: This endpoint does NOT accept include_usage. Pass include_usage=true on GET /tiktok/live/search/{task_id} instead.
curl --request POST \
--url https://api.vidnavigator.com/v1/tiktok/live/search \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"query": "gaming",
"max_results": 0,
"country": "gb",
"include_stream_urls": true,
"webhook_url": "https://example.com/hooks/vidnavigator"
}
'import requests
url = "https://api.vidnavigator.com/v1/tiktok/live/search"
payload = {
"query": "gaming",
"max_results": 0,
"country": "gb",
"include_stream_urls": True,
"webhook_url": "https://example.com/hooks/vidnavigator"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
query: 'gaming',
max_results: 0,
country: 'gb',
include_stream_urls: true,
webhook_url: 'https://example.com/hooks/vidnavigator'
})
};
fetch('https://api.vidnavigator.com/v1/tiktok/live/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.vidnavigator.com/v1/tiktok/live/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'query' => 'gaming',
'max_results' => 0,
'country' => 'gb',
'include_stream_urls' => true,
'webhook_url' => 'https://example.com/hooks/vidnavigator'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vidnavigator.com/v1/tiktok/live/search"
payload := strings.NewReader("{\n \"query\": \"gaming\",\n \"max_results\": 0,\n \"country\": \"gb\",\n \"include_stream_urls\": true,\n \"webhook_url\": \"https://example.com/hooks/vidnavigator\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.vidnavigator.com/v1/tiktok/live/search")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"gaming\",\n \"max_results\": 0,\n \"country\": \"gb\",\n \"include_stream_urls\": true,\n \"webhook_url\": \"https://example.com/hooks/vidnavigator\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vidnavigator.com/v1/tiktok/live/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"query\": \"gaming\",\n \"max_results\": 0,\n \"country\": \"gb\",\n \"include_stream_urls\": true,\n \"webhook_url\": \"https://example.com/hooks/vidnavigator\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"task_id": "<string>",
"task_status": "processing",
"query": "<string>",
"country": "us",
"max_results": 123,
"include_stream_urls": true,
"expires_at": "2023-11-07T05:31:56Z",
"check_status_url": "/v1/tiktok/live/search/550e8400-e29b-41d4-a716-446655440000",
"message": "<string>",
"webhook_url": "<string>"
}
}{
"status": "error",
"error": "<string>",
"message": "<string>"
}{
"status": "error",
"error": "metadata_fetch_failed",
"message": "<string>"
}task_id back immediately.
Overview
This endpoint searches TikTok’s LIVE tab: it returns the rooms that are live at the moment the search runs, not past videos. Each room comes with its author, live counters (current viewers, likes, …), hashtags, category and playback URLs (FLV and HLS by quality, plus an audio-only stream). It is asynchronous: you pollGET /tiktok/live/search/{task_id} for cursor-paginated rooms, or wait for the webhook. Results are retained for ~1 hour.
- Rooms keep TikTok’s ranking.
- Results are localized to
country(defaultus): the same keyword returns different rooms in different countries. - TikTok serves about 50 rooms per keyword. A keyword with no LIVE room in that country at that moment completes with zero results.
- A search usually takes 20 to 50 seconds.
id, author, stats, hashtags) plus LIVE-only fields.Billing
- 1
residential_requestper 30 rooms TikTok returned (stats.rooms_fetched, including rooms pastmax_results), minimum 1 per search. A keyword typically yields up to about 45 rooms, so 1 or 2 residential requests. - Stream URLs cost nothing extra: they come in the same TikTok response.
- No
search_requestis billed. A failed search is fully refunded.
include_usage. Billing happens after the 202 response is sent. Pass include_usage=true on GET /tiktok/live/search/{task_id} instead.Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Keyword phrase to search LIVE rooms for (min length 2), e.g. gaming, music, football. |
max_results | integer | No | Upper bound on rooms collected. 0 (default) or omitted means as many as TikTok serves, about 50. |
country | string | No | Two-letter country code (ISO 3166-1 alpha-2, lowercase or uppercase) whose LIVE rooms you get. Default us. The request schema above lists the accepted codes; an unsupported code returns 400 invalid_country with the list. |
include_stream_urls | boolean | No | Default true: each room carries its stream_urls. Pass false to leave them out. |
webhook_url | string | No | Where to POST a notification when the task finishes. Overrides the account default set in Studio → API; pass "" to opt this task out of the default. Must be a public https URL. The event carries stats and points you to check_status_url for the rooms. See Webhooks. |
Example Usage
curl -X POST "https://api.vidnavigator.com/v1/tiktok/live/search" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "gaming",
"country": "us",
"max_results": 20
}'
import requests
response = requests.post(
"https://api.vidnavigator.com/v1/tiktok/live/search",
headers={"X-API-Key": "YOUR_API_KEY", "Content-Type": "application/json"},
json={"query": "gaming", "country": "us", "max_results": 20},
)
task = response.json()
print("Task ID:", task["data"]["task_id"])
const response = await fetch('https://api.vidnavigator.com/v1/tiktok/live/search', {
method: 'POST',
headers: {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({ query: 'gaming', country: 'us', max_results: 20 })
});
const task = await response.json();
console.log('Task ID:', task.data.task_id);
Success Response (202 Accepted)
{
"status": "success",
"data": {
"task_id": "6f1c2a8e-4b3d-4e0a-9c55-2d7f1e8b9a10",
"task_status": "processing",
"query": "gaming",
"country": "us",
"max_results": 20,
"include_stream_urls": true,
"expires_at": "2026-11-05T15:00:00Z",
"check_status_url": "/v1/tiktok/live/search/6f1c2a8e-4b3d-4e0a-9c55-2d7f1e8b9a10",
"webhook_url": null,
"message": "Search started. Poll check_status_url for results."
}
}
Get the result
GET /tiktok/live/search/{task_id} — poll this endpoint with the task_id (or the check_status_url) returned above, or wait for the webhook notification. Polling does not consume credits.
Request Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
task_id | path | Yes | Task ID returned by POST /tiktok/live/search |
cursor | query | No | Opaque pagination cursor from a previous response |
limit | query | No | Maximum number of rooms to return, between 1 and 500. Default is 50. |
include_usage | query | No | When true and task_status=completed, attach a usage block: 1 residential_request per 30 rooms_fetched, minimum 1. |
Polling Lifecycle
processing: the search is still running (resultsis empty)completed: the rooms are readyfailed: the search ended with an error (all charges refunded)
Example Requests
curl "https://api.vidnavigator.com/v1/tiktok/live/search/6f1c2a8e-4b3d-4e0a-9c55-2d7f1e8b9a10?limit=25&include_usage=true" \
-H "X-API-Key: YOUR_API_KEY"
import requests
response = requests.get(
"https://api.vidnavigator.com/v1/tiktok/live/search/6f1c2a8e-4b3d-4e0a-9c55-2d7f1e8b9a10",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"limit": 25, "include_usage": "true"},
)
print(response.json())
const response = await fetch(
'https://api.vidnavigator.com/v1/tiktok/live/search/6f1c2a8e-4b3d-4e0a-9c55-2d7f1e8b9a10?limit=25&include_usage=true',
{ headers: { 'X-API-Key': 'YOUR_API_KEY' } }
);
const result = await response.json();
Example Completed Response
{
"status": "success",
"data": {
"task_id": "6f1c2a8e-4b3d-4e0a-9c55-2d7f1e8b9a10",
"task_status": "completed",
"query": "gaming",
"country": "us",
"stats": {
"pages_fetched": 1,
"rooms_fetched": 44,
"results_count": 20,
"has_more": true,
"country": "us"
},
"results": [
{
"id": "7704440240265644830",
"title": "Ranked grind #fortnite",
"url": "https://www.tiktok.com/@creator/live",
"started_at": "2026-11-04T22:04:46Z",
"author": {
"id": "6628548115959152646",
"unique_id": "creator",
"nickname": "Creator",
"sec_uid": "MS4wLjABAAAA...",
"followers": 576168,
"avatar_url": "https://p16-common-sign.tiktokcdn-us.com/..."
},
"stats": {
"viewers": 1296,
"total_viewers": 232347,
"likes": 301154,
"comments": 12839,
"shares": 360
},
"hashtags": ["fortnite"],
"category": "Gaming",
"game_tags": ["Fortnite"],
"cover_url": "https://p19-common-sign.tiktokcdn-us.com/...",
"stream_urls": {
"default_quality": "hd",
"flv": {
"hd": "https://pull-f5-tt01.tiktokcdn-us.com/game/stream-3867376775231964110_hd.flv?expire=1795089584&sign=..."
},
"hls": {
"hd": "https://pull-hls-f16-tt01.tiktokcdn-us.com/game/stream-3867376775231964110_hd/index.m3u8?expire=1795089584&sign=..."
},
"audio_only": "https://pull-f5-tt01.tiktokcdn-us.com/game/stream-3867376775231964110.flv?expire=1795089584&sign=...&only_audio=1",
"expires_at": "2026-11-19T11:59:44Z"
}
}
],
"pagination": {
"limit": 25,
"offset": 0,
"total_items": 20,
"has_next": false,
"has_prev": false,
"next_cursor": null,
"prev_cursor": null
},
"download_url": "https://storage.example.com/api/tiktok_live_searches/user/task.json?X-Amz-Signature=...",
"webhook": null,
"created_at": "2026-11-05T14:00:00Z",
"completed_at": "2026-11-05T14:00:31Z",
"expires_at": "2026-11-05T15:00:00Z"
},
"usage": {
"charges": [
{ "service_type": "residential_request", "quantity": 2, "credits": 0.01 }
],
"total_credits": 0.01
}
}
Understanding the Response
stats
rooms_fetched: distinct rooms TikTok returned, including any pastmax_results. This is what is billed (1residential_requestper 30).results_count: rooms inresultshas_more: whether TikTok had more rooms when the search stoppedpages_fetched: TikTok pages fetched (up to 50 rooms each)country: same ascountry
results
Each room is shaped like a keyword search result, plus LIVE-only fields:
| Field | Description |
|---|---|
id | LIVE room ID |
title | Title the streamer set, or null |
url | https://www.tiktok.com/@username/live |
started_at | When the LIVE started (UTC, ISO 8601) |
author | id, unique_id (username), nickname, sec_uid, followers, avatar_url |
stats | viewers (watching when the search ran), total_viewers (since the LIVE started), likes, comments, shares. Counters TikTok does not send are null. |
hashtags | Hashtags written in the title, without # |
category | The LIVE’s category on TikTok, e.g. Gaming |
game_tags | Games tagged on the LIVE, e.g. Fortnite |
cover_url | Cover image |
stream_urls | Playback URLs (see below). Omitted when include_stream_urls is false. |
stream_urls
| Field | Description |
|---|---|
default_quality | The quality TikTok plays by default |
flv | HTTP-FLV URLs keyed by quality: origin, uhd, hd, sd, ld (only those the LIVE offers) |
hls | HLS (.m3u8) URLs with the same keys, when TikTok provides them. Can be empty. |
audio_only | HTTP-FLV URL of the audio-only stream |
expires_at | Expiry of the URL signatures |
expires_at says. They work in players such as VLC, ffplay or ffmpeg (FLV and HLS), and in browsers through hls.js (HLS).
download_url
When present, a short-lived signed URL to the full result as one JSON file (same shape as the paginated results, but unsliced). Call the endpoint again to mint a fresh one.
error
Present when task_status is failed: an object with error (a code you can branch on, e.g. search_failed), message and http_status. See Errors.
webhook
Delivery state of the task’s webhook notification (status, attempts, response_status, last_error, delivered_at), or null when no webhook applies.
Error Cases
| Status | error | When |
|---|---|---|
400 | request_body_required | No JSON body |
400 | invalid_query | query missing or shorter than 2 characters |
400 | invalid_max_results | max_results negative or not an integer |
400 | invalid_country | country not supported; the message lists the accepted codes |
400 | invalid_include_stream_urls | include_stream_urls not a boolean |
400 | invalid_cursor | Bad cursor on the result endpoint |
404 | task_not_found | Task does not exist, expired, or belongs to another account |
Tips
- LIVE rooms change by the minute: run the search when you need the rooms, and read the stream URLs soon after.
- Try several keywords and countries: a keyword can have no LIVE room in one country and dozens in another.
stats.viewersis a snapshot taken when the search ran.
Authorizations
API key authentication. Include your VidNavigator API key in the X-API-Key header.
Body
Keyword phrase to search LIVE rooms for.
2"gaming"
Upper bound on rooms collected. 0 (the default) means as many as TikTok serves, about 50 per keyword.
x >= 0Country whose LIVE rooms you get (ISO 3166-1 alpha-2, lowercase or uppercase), one of the supported countries. Default us.
al, am, ao, ar, at, au, az, ba, be, bf, bg, bj, bo, br, bw, by, ca, cd, ch, ci, cl, cm, co, cr, cy, cz, de, dk, do, ec, ee, es, fi, fj, fr, gb, ge, gh, gr, gt, hn, hr, ht, hu, id, ie, il, is, it, jm, jp, ke, kg, kr, kz, lk, lt, lu, lv, md, me, mg, mk, ml, mn, mt, mu, mw, mx, my, mz, na, ne, ng, ni, nl, no, np, nz, pa, pe, pg, ph, pl, pr, pt, py, ro, rs, rw, se, sg, si, sk, sn, sv, tg, th, tj, tr, tt, tw, tz, ua, ug, us, uy, uz, ve, vn, za, zm, zw "gb"
Return each room's playback URLs in stream_urls (FLV and HLS by quality, plus an audio-only FLV). On by default; they come at no extra cost. Pass false to leave them out.
Where to POST the result when the task finishes. Overrides the account-level default configured in Studio → API; pass an empty string to opt this task out of that default. Must be a publicly reachable https URL. The event is a notification, not the payload: it carries stats and tells you to read the rooms from check_status_url. See https://docs.vidnavigator.com/guides/webhooks
"https://example.com/hooks/vidnavigator"

