curl --request POST \
--url https://api.vidnavigator.com/v1/tiktok/profile \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"profile_url": "https://www.tiktok.com/@tiktok",
"max_posts": 100,
"after_datetime": "2024-01-01T00:00:00Z",
"before_datetime": "2024-12-31",
"min_likes": 1,
"max_likes": 1,
"webhook_url": "https://example.com/hooks/vidnavigator"
}
'import requests
url = "https://api.vidnavigator.com/v1/tiktok/profile"
payload = {
"profile_url": "https://www.tiktok.com/@tiktok",
"max_posts": 100,
"after_datetime": "2024-01-01T00:00:00Z",
"before_datetime": "2024-12-31",
"min_likes": 1,
"max_likes": 1,
"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({
profile_url: 'https://www.tiktok.com/@tiktok',
max_posts: 100,
after_datetime: '2024-01-01T00:00:00Z',
before_datetime: '2024-12-31',
min_likes: 1,
max_likes: 1,
webhook_url: 'https://example.com/hooks/vidnavigator'
})
};
fetch('https://api.vidnavigator.com/v1/tiktok/profile', 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/profile",
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([
'profile_url' => 'https://www.tiktok.com/@tiktok',
'max_posts' => 100,
'after_datetime' => '2024-01-01T00:00:00Z',
'before_datetime' => '2024-12-31',
'min_likes' => 1,
'max_likes' => 1,
'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/profile"
payload := strings.NewReader("{\n \"profile_url\": \"https://www.tiktok.com/@tiktok\",\n \"max_posts\": 100,\n \"after_datetime\": \"2024-01-01T00:00:00Z\",\n \"before_datetime\": \"2024-12-31\",\n \"min_likes\": 1,\n \"max_likes\": 1,\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/profile")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"profile_url\": \"https://www.tiktok.com/@tiktok\",\n \"max_posts\": 100,\n \"after_datetime\": \"2024-01-01T00:00:00Z\",\n \"before_datetime\": \"2024-12-31\",\n \"min_likes\": 1,\n \"max_likes\": 1,\n \"webhook_url\": \"https://example.com/hooks/vidnavigator\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vidnavigator.com/v1/tiktok/profile")
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 \"profile_url\": \"https://www.tiktok.com/@tiktok\",\n \"max_posts\": 100,\n \"after_datetime\": \"2024-01-01T00:00:00Z\",\n \"before_datetime\": \"2024-12-31\",\n \"min_likes\": 1,\n \"max_likes\": 1,\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",
"profile_url": "<string>",
"expires_at": "2023-11-07T05:31:56Z",
"check_status_url": "/v1/tiktok/profile/550e8400-e29b-41d4-a716-446655440000",
"message": "<string>",
"webhook_url": "<string>"
}
}{
"status": "error",
"error": "request_body_required",
"message": "<string>"
}{
"status": "error",
"error": "limit_exceeded",
"message": "<string>"
}{
"status": "error",
"error": "metadata_fetch_failed",
"message": "<string>"
}TikTok Profile Scrape
Start an asynchronous TikTok profile scrape and return a task_id immediately.
The scrape runs in the background with shallow extraction so it can stop early when filters are satisfied. Results are retained for 1 hour and, when cloud storage is configured, also uploaded as a single JSON file accessible through download_url.
Billing: Each TikTok API page fetched is billed as one standard request. One page is pre-charged when the task is accepted; remaining pages are reconciled when the scrape completes. If the scrape fails after charging additional pages, all charged pages are reversed. TikTok currently returns ~15 videos per page; this is an internal estimate and may change.
Filters: All filters are applied as the scraper iterates. max_posts and after_datetime allow the scraper to stop fetching new pages early; the other filters reduce result size but do not stop pagination.
Usage disclosure: This endpoint does NOT accept include_usage. Because billing happens in the background worker after the 202 response is sent, there is nothing to disclose at submit time. Pass include_usage=true on GET /tiktok/profile/{task_id} instead — the GET endpoint replays the final charges once task_status=completed.
Use GET /tiktok/profile/{task_id} to poll status and retrieve paginated results, or follow download_url for the complete JSON.
curl --request POST \
--url https://api.vidnavigator.com/v1/tiktok/profile \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"profile_url": "https://www.tiktok.com/@tiktok",
"max_posts": 100,
"after_datetime": "2024-01-01T00:00:00Z",
"before_datetime": "2024-12-31",
"min_likes": 1,
"max_likes": 1,
"webhook_url": "https://example.com/hooks/vidnavigator"
}
'import requests
url = "https://api.vidnavigator.com/v1/tiktok/profile"
payload = {
"profile_url": "https://www.tiktok.com/@tiktok",
"max_posts": 100,
"after_datetime": "2024-01-01T00:00:00Z",
"before_datetime": "2024-12-31",
"min_likes": 1,
"max_likes": 1,
"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({
profile_url: 'https://www.tiktok.com/@tiktok',
max_posts: 100,
after_datetime: '2024-01-01T00:00:00Z',
before_datetime: '2024-12-31',
min_likes: 1,
max_likes: 1,
webhook_url: 'https://example.com/hooks/vidnavigator'
})
};
fetch('https://api.vidnavigator.com/v1/tiktok/profile', 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/profile",
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([
'profile_url' => 'https://www.tiktok.com/@tiktok',
'max_posts' => 100,
'after_datetime' => '2024-01-01T00:00:00Z',
'before_datetime' => '2024-12-31',
'min_likes' => 1,
'max_likes' => 1,
'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/profile"
payload := strings.NewReader("{\n \"profile_url\": \"https://www.tiktok.com/@tiktok\",\n \"max_posts\": 100,\n \"after_datetime\": \"2024-01-01T00:00:00Z\",\n \"before_datetime\": \"2024-12-31\",\n \"min_likes\": 1,\n \"max_likes\": 1,\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/profile")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"profile_url\": \"https://www.tiktok.com/@tiktok\",\n \"max_posts\": 100,\n \"after_datetime\": \"2024-01-01T00:00:00Z\",\n \"before_datetime\": \"2024-12-31\",\n \"min_likes\": 1,\n \"max_likes\": 1,\n \"webhook_url\": \"https://example.com/hooks/vidnavigator\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.vidnavigator.com/v1/tiktok/profile")
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 \"profile_url\": \"https://www.tiktok.com/@tiktok\",\n \"max_posts\": 100,\n \"after_datetime\": \"2024-01-01T00:00:00Z\",\n \"before_datetime\": \"2024-12-31\",\n \"min_likes\": 1,\n \"max_likes\": 1,\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",
"profile_url": "<string>",
"expires_at": "2023-11-07T05:31:56Z",
"check_status_url": "/v1/tiktok/profile/550e8400-e29b-41d4-a716-446655440000",
"message": "<string>",
"webhook_url": "<string>"
}
}{
"status": "error",
"error": "request_body_required",
"message": "<string>"
}{
"status": "error",
"error": "limit_exceeded",
"message": "<string>"
}{
"status": "error",
"error": "metadata_fetch_failed",
"message": "<string>"
}task_id immediately.
Overview
This endpoint is designed for profile-level collection, not single-video analysis. It starts a background scrape job that iterates through a TikTok account, applies your filters as it goes, and stores the results temporarily so you can fetch them later. Use it when you want to:- collect recent posts from a TikTok creator
- filter videos by date or like count
- scrape large profiles without blocking on a long request
- export the full result set through a temporary
download_url
How the Async Flow Works
- Send
POST /tiktok/profilewith a public TikTokprofile_urland optional filters. - The API accepts the task and returns a
task_idwithtask_status: "processing". - Poll
GET /tiktok/profile/{task_id}until the task iscompletedorfailed. - Once completed, either page through
videosusing cursors or download the full JSON fromdownload_urlwhen available.
download_url availability.Filters You Can Apply
All filters are optional exceptprofile_url.
| Field | Type | Description |
|---|---|---|
profile_url | string | Public TikTok profile URL, such as https://www.tiktok.com/@username |
max_posts | integer | Maximum number of matching videos to return |
after_datetime | string | Include only videos published on or after this boundary. Accepts YYYY-MM-DD or an ISO datetime with timezone, such as 2024-01-01T00:00:00Z. |
before_datetime | string | Include only videos published on or before this boundary. Accepts YYYY-MM-DD or an ISO datetime with timezone, such as 2024-12-31T23:59:59+02:00. |
min_likes | integer | Include only videos with at least this many likes |
max_likes | integer | Include only videos with at most this many likes |
webhook_url | string | 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. See Webhooks. |
stats and tells you to read the results from check_status_url.max_posts and after_datetime are the most useful filters for keeping scrapes smaller and faster because they can stop pagination early.What You Get Back Immediately
The submit endpoint now returns more than just a task ID. The accepted response includes:task_idto identify the scrape jobtask_statusset toprocessingprofile_urlechoing the submitted profileexpires_atso you know when the task record will expirecheck_status_urlwith the relative API path to pollwebhook_url: the URL the completion notification will be POSTed to, ornullmessageconfirming the job was accepted
Example Request
curl -X POST "https://api.vidnavigator.com/v1/tiktok/profile" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile_url": "https://www.tiktok.com/@tiktok",
"max_posts": 50,
"after_datetime": "2024-01-01T00:00:00Z",
"min_likes": 10000
}'
import requests
response = requests.post(
"https://api.vidnavigator.com/v1/tiktok/profile",
headers={
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"profile_url": "https://www.tiktok.com/@tiktok",
"max_posts": 50,
"after_datetime": "2024-01-01T00:00:00Z",
"min_likes": 10000,
},
)
print(response.json())
const response = await fetch('https://api.vidnavigator.com/v1/tiktok/profile', {
method: 'POST',
headers: {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
profile_url: 'https://www.tiktok.com/@tiktok',
max_posts: 50,
after_datetime: '2024-01-01T00:00:00Z',
min_likes: 10000
})
});
const result = await response.json();
Success Response
The endpoint returns202 Accepted because the scrape runs in the background.
{
"status": "success",
"data": {
"task_id": "tt_profile_abc123",
"task_status": "processing",
"profile_url": "https://www.tiktok.com/@tiktok",
"expires_at": "2026-04-26T00:55:00Z",
"check_status_url": "/v1/tiktok/profile/tt_profile_abc123",
"webhook_url": null,
"message": "TikTok profile scrape accepted. Poll the status endpoint to retrieve results."
}
}
Billing Notes
TikTok profile scraping is billed by pages consumed during the scrape:- each TikTok API page is billed as one
standard_requestwhen no proxy is used - each TikTok API page is billed as one
residential_requestwhen a residential proxy is used - one page is pre-charged when the task is accepted
- final usage is reconciled when the scrape completes
- if the scrape fails after extra pages were charged, those charges are reversed
Tips for Large Profiles
- use
after_datetimeto avoid crawling older history you do not need - use
max_poststo cap the result size - prefer
download_urlafter completion if you expect a large result set - poll the result endpoint while the task is processing instead of resubmitting the same scrape
Common Errors
request_body_required: the JSON body was missingmissing_parameter:profile_urlwas not sentinvalid_url: the submittedprofile_urlis malformedinvalid_parameter: one of the filters is malformed
unsupported_platform for this endpoint.
Get the result
GET /tiktok/profile/{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/profile |
cursor | query | No | Opaque pagination cursor from a previous response |
limit | query | No | Maximum number of videos to return, between 1 and 500. Default is 50. |
How Pagination Works
Pagination is cursor-based, but the cursor encodes an absolute offset. That means you can safely changelimit between requests without skipping or duplicating videos.
Typical flow:
- Call the endpoint with no
cursor. - Read
data.pagination.next_cursor. - Pass that value back as the next request’s
cursor. - Stop when
has_nextisfalse.
Polling Lifecycle
Thetask_status field will be one of:
processing: the scrape is still runningcompleted: the scrape finished and results are readyfailed: the scrape ended with an error
profilemay benullvideoswill be emptydownload_urlwill usually benull
videoscontains a cursor-paginated page of matching postspaginationtells you whether more pages existdownload_urlmay contain a temporary signed URL for the full JSON result
Example Requests
curl "https://api.vidnavigator.com/v1/tiktok/profile/tt_profile_abc123?limit=25" \
-H "X-API-Key: YOUR_API_KEY"
import requests
response = requests.get(
"https://api.vidnavigator.com/v1/tiktok/profile/tt_profile_abc123",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"limit": 25},
)
print(response.json())
const response = await fetch(
'https://api.vidnavigator.com/v1/tiktok/profile/tt_profile_abc123?limit=25',
{
headers: {
'X-API-Key': 'YOUR_API_KEY'
}
}
);
const result = await response.json();
Fetch the Next Page
curl "https://api.vidnavigator.com/v1/tiktok/profile/tt_profile_abc123?limit=25&cursor=eyJvZmZzZXQiOjI1fQ==" \
-H "X-API-Key: YOUR_API_KEY"
Example Completed Response
{
"status": "success",
"data": {
"task_id": "tt_profile_abc123",
"task_status": "completed",
"profile_url": "https://www.tiktok.com/@tiktok",
"profile": {
"uploader": "tiktok",
"follower_count": 12345678
},
"filters": {
"max_posts": 50,
"after_datetime": "2024-01-01T00:00:00Z",
"before_datetime": null,
"min_likes": 10000,
"max_likes": null
},
"stats": {
"videos_scanned": 120,
"videos_matched": 50,
"pages_consumed": 8
},
"videos": [
{
"id": "7351234567890123456",
"track": "Original sound",
"artists": ["Creator Name"],
"duration": 28,
"title": "Example TikTok",
"description": "Video caption text here",
"timestamp": 1712345678,
"published_at": "2024-04-05T19:34:38Z",
"views": 250000,
"likes": 22000,
"reposts": 180,
"comments": 950,
"thumbnails": [],
"url": "https://www.tiktok.com/@tiktok/video/7351234567890123456"
}
],
"pagination": {
"limit": 25,
"offset": 0,
"total_items": 50,
"has_next": true,
"has_prev": false,
"next_cursor": "eyJvZmZzZXQiOjI1fQ==",
"prev_cursor": null
},
"download_url": "https://storage.googleapis.com/bucket/api/tiktok_profiles/user/task.json?...",
"error_message": null,
"error": null,
"webhook": null,
"created_at": "2026-04-25T20:00:00Z",
"completed_at": "2026-04-25T20:01:12Z",
"expires_at": "2026-04-25T21:00:00Z"
}
}
Understanding the Response
profile
Contains profile-level metadata returned by the scraper, such as uploader information and follower counts when available.
stats
Helps you understand what happened during the scrape:
videos_scanned: total TikTok entries iterated before filteringvideos_matched: entries that passed your filterspages_consumed: estimated number of TikTok API pages fetched during the scrape
videos
This is the current page of matched TikTok videos. Each item includes public metadata such as:
idtitledescriptiontimestamp: Unix timestamp in seconds when the video was uploadedpublished_at: UTC datetime string derived fromtimestamp, in ISO 8601 format with timezoneviewslikescommentsurl
download_url
When present, this is a short-lived signed URL pointing to the full scrape result as one JSON file. It is useful for large profiles, browser downloads, and no-code integrations.
If it is null, either:
- the task has not finished yet, or
- the full result wasn’t archived, and you should paginate through
videosinstead
error
Present when task_status is failed: an object with error (a code you can branch on), message and http_status. See Errors.
error_message is deprecated. It is kept for existing integrations, but new code should read error.error / error.message instead.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
400 invalid_cursor: the suppliedcursoris invalid404 task_not_found: task does not exist, expired, or does not belong to the current user
Tips
- poll every few seconds while
task_statusisprocessing - switch to
download_urlfor large completed jobs - do not assume tasks live forever; they expire after about 1 hour
- call the endpoint again later if you need a freshly minted
download_url - use the
expires_atvalue to avoid polling a task after it has already expired
Authorizations
API key authentication. Include your VidNavigator API key in the X-API-Key header.
Body
Public TikTok profile URL (e.g. https://www.tiktok.com/@username).
"https://www.tiktok.com/@tiktok"
Maximum number of matching videos to return. The scraper stops paginating once this is reached.
x >= 1100
Only include videos published on or after this boundary. Accepts either YYYY-MM-DD or an ISO datetime with timezone (e.g. 2024-01-01T00:00:00Z). Filtering uses per-video timestamp when available (UTC), with upload-date fallback otherwise.
"2024-01-01T00:00:00Z"
Only include videos published on or before this boundary. Accepts either YYYY-MM-DD or an ISO datetime with timezone (e.g. 2024-12-31T23:59:59+02:00). Filtering uses per-video timestamp when available (UTC), with upload-date fallback otherwise.
"2024-12-31"
Only include videos with at least this many likes.
x >= 0Only include videos with at most this many likes.
x >= 0Where 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: a scrape can hold thousands of videos, so it carries stats and tells you to read the result from check_status_url. See https://docs.vidnavigator.com/guides/webhooks
"https://example.com/hooks/vidnavigator"

