Skip to main content
POST
/
tiktok
/
search
Submit a TikTok keyword search (async)
curl --request POST \
  --url https://api.vidnavigator.com/v1/tiktok/search \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <api-key>' \
  --data '
{
  "query": "ai tools",
  "max_results": 0,
  "parallel_search_slices": 2,
  "after_datetime": "<string>",
  "before_datetime": "<string>",
  "min_likes": 1,
  "max_likes": 1,
  "min_views": 1,
  "max_views": 1
}
'
import requests

url = "https://api.vidnavigator.com/v1/tiktok/search"

payload = {
"query": "ai tools",
"max_results": 0,
"parallel_search_slices": 2,
"after_datetime": "<string>",
"before_datetime": "<string>",
"min_likes": 1,
"max_likes": 1,
"min_views": 1,
"max_views": 1
}
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: 'ai tools',
max_results: 0,
parallel_search_slices: 2,
after_datetime: '<string>',
before_datetime: '<string>',
min_likes: 1,
max_likes: 1,
min_views: 1,
max_views: 1
})
};

fetch('https://api.vidnavigator.com/v1/tiktok/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/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' => 'ai tools',
'max_results' => 0,
'parallel_search_slices' => 2,
'after_datetime' => '<string>',
'before_datetime' => '<string>',
'min_likes' => 1,
'max_likes' => 1,
'min_views' => 1,
'max_views' => 1
]),
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/search"

payload := strings.NewReader("{\n \"query\": \"ai tools\",\n \"max_results\": 0,\n \"parallel_search_slices\": 2,\n \"after_datetime\": \"<string>\",\n \"before_datetime\": \"<string>\",\n \"min_likes\": 1,\n \"max_likes\": 1,\n \"min_views\": 1,\n \"max_views\": 1\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/search")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"query\": \"ai tools\",\n \"max_results\": 0,\n \"parallel_search_slices\": 2,\n \"after_datetime\": \"<string>\",\n \"before_datetime\": \"<string>\",\n \"min_likes\": 1,\n \"max_likes\": 1,\n \"min_views\": 1,\n \"max_views\": 1\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.vidnavigator.com/v1/tiktok/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\": \"ai tools\",\n \"max_results\": 0,\n \"parallel_search_slices\": 2,\n \"after_datetime\": \"<string>\",\n \"before_datetime\": \"<string>\",\n \"min_likes\": 1,\n \"max_likes\": 1,\n \"min_views\": 1,\n \"max_views\": 1\n}"

response = http.request(request)
puts response.read_body
{
  "status": "success",
  "data": {
    "task_id": "<string>",
    "task_status": "processing",
    "query": "<string>",
    "max_results": 123,
    "parallel_search_slices": 2,
    "filters": {},
    "expires_at": "2023-11-07T05:31:56Z",
    "check_status_url": "/v1/tiktok/search/550e8400-e29b-41d4-a716-446655440000",
    "message": "<string>"
  }
}
{
"status": "error",
"error": "<string>",
"message": "<string>"
}
{
"status": "error",
"error": "internal_server_error",
"message": "<string>"
}
Start an asynchronous TikTok keyword search and get a task_id back immediately.

Overview

This endpoint runs an actual keyword search across TikTok (not a single profile) and returns multiple matching videos. It is asynchronous: the search runs in a background worker, and you poll GET /tiktok/search/{task_id} for cursor-paginated results. Results are retained for ~1 hour. Results are returned sorted by published_at descending (newest first) across pagination, full retrieval, and the signed download_url payload.
This is different from the TikTok Profile scrape, which walks a single account’s videos. Use this endpoint to discover videos across TikTok by keyword.

Billing

Charges happen in the background worker and are disclosed on the polling endpoint (see below), not here.
  • Billing is pages_fetched × residential_request. No search_request is billed for TikTok keyword search.
  • parallel_search_slices scales the cost linearly. By default (1) the search runs a single paginated query chain, naturally capped near ~120 items by TikTok. Setting it to 2..4 runs that many concurrent chains and dedups by video id, lifting the ceiling — but an N-slice request bills up to N× the residential pages of a single search.
Usage disclosure: this endpoint does not accept include_usage. Because billing happens after the 202 response is sent, there is nothing to disclose at submit time. Pass include_usage=true on GET /tiktok/search/{task_id} instead — it replays the final charges once task_status=completed.

Request Parameters

ParameterTypeRequiredDescription
querystringYesKeyword phrase to search on TikTok (min length 2).
max_resultsintegerNoUpper bound on results the task will try to collect. 0 (default) or omitted means unlimited — pagination stops only at TikTok’s has_more=false or the upstream page cap. No hard server-side cap is enforced; you are billed per residential page actually fetched.
parallel_search_slicesintegerNoHow many concurrent paginated query chains to run and dedup (14, default 1). Higher values lift the result ceiling at up to ~N× the residential pages of a single search. Not a time filter — use after_datetime / before_datetime for time windows.
after_datetimestringNoOnly include videos published on or after this boundary. Accepts YYYY-MM-DD or an ISO datetime with timezone.
before_datetimestringNoOnly include videos published on or before this boundary. Accepts YYYY-MM-DD or an ISO datetime with timezone.
min_likesintegerNoOnly include videos with at least this many likes.
max_likesintegerNoOnly include videos with at most this many likes.
min_viewsintegerNoOnly include videos with at least this many views.
max_viewsintegerNoOnly include videos with at most this many views.
Diminishing returns on slices: on a representative query, 2 slices yield ~+43% unique items, 3 slices ~+22%, and 4 slices ~+13% — so 4 already saturates TikTok’s natural ~480-item dedup ceiling for most queries.

Example Usage

curl -X POST "https://api.vidnavigator.com/v1/tiktok/search" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "ai tools",
    "parallel_search_slices": 2,
    "after_datetime": "2024-01-01T00:00:00Z",
    "min_likes": 1000
  }'
import requests

url = "https://api.vidnavigator.com/v1/tiktok/search"
headers = {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json"
}
data = {
    "query": "ai tools",
    "parallel_search_slices": 2,
    "after_datetime": "2024-01-01T00:00:00Z",
    "min_likes": 1000
}

response = requests.post(url, headers=headers, json=data)
task = response.json()
print("Task ID:", task["data"]["task_id"])
const response = await fetch('https://api.vidnavigator.com/v1/tiktok/search', {
  method: 'POST',
  headers: {
    'X-API-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    query: 'ai tools',
    parallel_search_slices: 2,
    after_datetime: '2024-01-01T00:00:00Z',
    min_likes: 1000
  })
});

const task = await response.json();
console.log('Task ID:', task.data.task_id);

Success Response (202 Accepted)

{
  "status": "success",
  "data": {
    "task_id": "550e8400-e29b-41d4-a716-446655440000",
    "task_status": "processing",
    "query": "ai tools",
    "max_results": 0,
    "parallel_search_slices": 2,
    "filters": {
      "after_datetime": "2024-01-01T00:00:00Z",
      "before_datetime": null,
      "min_likes": 1000,
      "max_likes": null,
      "min_views": null,
      "max_views": null
    },
    "expires_at": "2026-04-26T13:00:00Z",
    "check_status_url": "/v1/tiktok/search/550e8400-e29b-41d4-a716-446655440000",
    "message": "TikTok search task accepted and queued."
  }
}

Next Steps

Poll GET /tiktok/search/{task_id} to retrieve paginated results, or follow the download_url for the complete JSON once the task is completed.

Authorizations

X-API-Key
string
header
required

API key authentication. Include your VidNavigator API key in the X-API-Key header.

Body

application/json
query
string
required

Keyword phrase to search on TikTok.

Minimum string length: 2
Example:

"ai tools"

max_results
integer
default:0

Optional upper bound on results the background task will try to collect. 0 (the default) or omitted means unlimited — pagination only stops at TikTok's has_more=false or the upstream page cap. With parallel_search_slices=1 that naturally yields ~89-120 items (TikTok serves ~10 pages of ~12 items per chain); with parallel_search_slices=4 up to ~480 unique items. A positive integer caps the merged result count. No hard server-side cap is enforced — a caller asking for 3000 will be billed per residential page actually fetched (~10 pages per slice naturally) and receive whatever TikTok delivers.

Required range: x >= 0
parallel_search_slices
integer
default:1

How many concurrent paginated query chains to run and dedup by video id. 1 (the default) is a single chain naturally capped near 120 items. 2..4 runs that many additional chains in parallel, lifting the effective ceiling at the cost of up to ~Nx the residential pages of a single search. Diminishing returns: 2 slices yield ~+43% unique items, 3 slices ~+22%, 4 slices ~+13% on a representative query, so 4 already saturates TikTok's natural ~480-item dedup ceiling for most queries. parallel_search_slices is not a time filter — use after_datetime / before_datetime for time windows.

Required range: 1 <= x <= 4
Example:

2

after_datetime
string

Only include videos published on or after this boundary. Accepts YYYY-MM-DD or ISO datetime with timezone.

before_datetime
string

Only include videos published on or before this boundary. Accepts YYYY-MM-DD or ISO datetime with timezone.

min_likes
integer
Required range: x >= 0
max_likes
integer
Required range: x >= 0
min_views
integer
Required range: x >= 0
max_views
integer
Required range: x >= 0

Response

Task accepted and queued.

status
enum<string>
Available options:
success
data
object