Use cases
What teams build with it. From one request.
- Monitor an account's new posts since a date
- Benchmark engagement across accounts
- Build datasets of an account's TikTok content
What you get
Posts in one schema. The same on every platform.
platformstring- Platform ID (tiktok, youtube, …).
idstring- Platform-native post ID (string; Instagram shortcode, Reddit without t3_).
urlstring- Canonical post URL.
typestring- Normalized content shape. New values may be added; treat unknown values as other.
platform_typestring- The platform's own product label, lowercase (reel, short, story, live, tweet, …).
titlestring- Headline (YouTube title, Reddit title); null on caption-only platforms.
textstring- Caption, description or body as plain text.
languagestring- BCP-47 language of text, when the platform states it.
Every field, its type and when it is null: the response reference and the post schema.
Example request
curl -G 'https://staging.socialtap.dev/v1/tiktok/posts' \
--data-urlencode 'handle=@khaby.lame' \
-H "x-api-key: $SOCIALTAP_API_KEY"const params = new URLSearchParams({
handle: "@khaby.lame",
});
const res = await fetch(`https://staging.socialtap.dev/v1/tiktok/posts?${params}`, {
headers: { "x-api-key": process.env.SOCIALTAP_API_KEY ?? "" },
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
const { data, page, meta } = body;
console.log(data, meta.credits_charged);import os
import requests
res = requests.get(
"https://staging.socialtap.dev/v1/tiktok/posts",
params={"handle": "@khaby.lame"},
headers={"x-api-key": os.environ["SOCIALTAP_API_KEY"]},
)
body = res.json()
if not res.ok:
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
data, page, meta = body["data"], body["page"], body["meta"]
print(data, meta["credits_charged"])package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
)
func main() {
q := url.Values{}
q.Set("handle", "@khaby.lame")
req, err := http.NewRequest("GET", "https://staging.socialtap.dev/v1/tiktok/posts?"+q.Encode(), nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("x-api-key", os.Getenv("SOCIALTAP_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var body struct {
Data json.RawMessage `json:"data"`
Page json.RawMessage `json:"page"`
Meta map[string]any `json:"meta"`
Error *struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
log.Fatal(err)
}
if body.Error != nil {
log.Fatalf("%s: %s", body.Error.Code, body.Error.Message)
}
fmt.Println(string(body.Data), body.Meta["credits_charged"])
}<?php
$query = http_build_query([
'handle' => '@khaby.lame',
]);
$ch = curl_init('https://staging.socialtap.dev/v1/tiktok/posts?' . $query);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['x-api-key: ' . getenv('SOCIALTAP_API_KEY')],
]);
$body = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_RESPONSE_CODE) >= 400) {
throw new RuntimeException($body['error']['code'] . ': ' . $body['error']['message']);
}
print_r($body['data']);
echo $body['meta']['credits_charged'], PHP_EOL;require "json"
require "net/http"
uri = URI("https://staging.socialtap.dev/v1/tiktok/posts")
uri.query = URI.encode_www_form('handle' => '@khaby.lame')
req = Net::HTTP::Get.new(uri)
req["x-api-key"] = ENV.fetch("SOCIALTAP_API_KEY")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }
body = JSON.parse(res.body)
raise "#{body["error"]["code"]}: #{body["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
pp body["data"]
puts body["meta"]["credits_charged"]import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) throws Exception {
String query = "handle=" + encode("@khaby.lame");
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://staging.socialtap.dev/v1/tiktok/posts?" + query))
.header("x-api-key", System.getenv("SOCIALTAP_API_KEY"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
// An error is {"error": {"code", "message", ...}}; success is {data, meta}, meta.credits_charged its price.
if (response.statusCode() >= 400) throw new RuntimeException(response.body());
System.out.println(response.body());
}
static String encode(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
}using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", Environment.GetEnvironmentVariable("SOCIALTAP_API_KEY"));
var query = string.Join("&",
"handle=" + Uri.EscapeDataString("@khaby.lame"));
var res = await http.GetAsync("https://staging.socialtap.dev/v1/tiktok/posts?" + query);
using var body = JsonDocument.Parse(await res.Content.ReadAsStringAsync());
if (!res.IsSuccessStatusCode)
{
var error = body.RootElement.GetProperty("error");
throw new Exception($"{error.GetProperty("code")}: {error.GetProperty("message")}");
}
Console.WriteLine(body.RootElement.GetProperty("data"));
Console.WriteLine(body.RootElement.GetProperty("meta").GetProperty("credits_charged"));// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }, serde_json = "1"
use std::env;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let res = reqwest::blocking::Client::new()
.get("https://staging.socialtap.dev/v1/tiktok/posts")
.query(&[("handle", "@khaby.lame")])
.header("x-api-key", env::var("SOCIALTAP_API_KEY")?)
.send()?;
let ok = res.status().is_success();
let body: serde_json::Value = res.json()?;
if !ok {
return Err(format!("{}: {}", body["error"]["code"], body["error"]["message"]).into());
}
println!("{} {}", body["data"], body["meta"]["credits_charged"]);
Ok(())
}{
"data": [
{
"approximate_fields": [
"metrics.view_count",
"metrics.like_count",
"metrics.share_count"
],
"author": {
"avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/08987e23b94057953fd4f1738694bf5f~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=07918010&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
"display_name": "Khabane lame",
"id": "127905465618821121",
"is_verified": true,
"url": "https://www.tiktok.com/@khaby.lame",
"username": "khaby.lame"
},
"coauthors": [],
"duration_ms": 9000,
"hashtags": [
"learnfromkhaby",
"comedy"
],
"id": "7697307919943060767",
"is_nsfw": null,
"is_pinned": null,
"is_sponsored": null,
"language": "en",
"media": [
{
"alt_text": null,
"duration_ms": 9000,
"expires_at": null,
"height": 1280,
"thumbnail_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-p-0068-tx2/oQbHQmFfa5VY3NlkEGDAQT00RzqAqBOETlEafj~tplv-tiktokx-origin.image?dr=9636&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5",
"type": "video",
"url": null,
"width": 720
}
],
"mentions": [],
"metrics": {
"comment_count": 7503,
"like_count": 305900,
"quote_count": null,
"repost_count": null,
"save_count": 16282,
"share_count": 32600,
"view_count": 4600000
},
"platform": "tiktok",
"platform_specific": {
"tiktok": {
"author_sec_uid": "MS4wLjABAAAAwAg0rSzO65WQfz4RzQgGv2Xdv108BgPXhRrrmNVIHQZ9PO8-flwwRtEppYTS0OjA",
"country_code": null,
"is_ad": false,
"is_ai_generated": true,
"location": null,
"music": {
"author_name": "Khabane lame",
"cover_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/08987e23b94057953fd4f1738694bf5f~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=07918010&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
"duration_ms": 9000,
"id": "7697308007327730462",
"is_original": true,
"play_url": "https://v16-webapp-prime.us.tiktok.com/video/tos/useast8/tos-useast8-v-27dcd7-tx2/oEEIAbfkyMVIeCnAjGGKYI9gqpqTImLAAfYo09/?a=1988&bti=ODszNWYuMDE6&&bt=125&ft=GcAInInz7Th~v-RGXq8Zmo&mime_type=audio_mpeg&rc=M2lybXI5cnE4ZTMzaTU8NEBpM2lybXI5cnE4ZTMzaTU8NEBxYG5tMmRrNjZhLS1kMTJzYSNxYG5t…",
"title": "original sound"
}
}
},
"platform_type": "video",
"published_at": "2026-10-16T16:46:21Z",
"text": "I guess is time to retire #learnfromkhaby #comedy 🥊",
"thumbnail_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-p-0068-tx2/oQbHQmFfa5VY3NlkEGDAQT00RzqAqBOETlEafj~tplv-tiktokx-origin.image?dr=9636&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5",
"title": null,
"type": "video",
"unknown_fields": [],
"url": "https://www.tiktok.com/@khaby.lame/video/7697307919943060767"
},
{
"approximate_fields": [
"metrics.view_count",
"metrics.like_count",
"metrics.comment_count"
],
"author": {
"avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/08987e23b94057953fd4f1738694bf5f~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=07918010&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
"display_name": "Khabane lame",
"id": "127905465618821121",
"is_verified": true,
"url": "https://www.tiktok.com/@khaby.lame",
"username": "khaby.lame"
},
"coauthors": [],
"duration_ms": 30000,
"hashtags": [
"learnfromkhaby",
"comedy"
],
"id": "7694335322922044703",
"is_nsfw": null,
"is_pinned": null,
"is_sponsored": null,
"language": "en",
"media": [
{
"alt_text": null,
"duration_ms": 30000,
"expires_at": null,
"height": 1280,
"thumbnail_url": "https://p19-common-sign.tiktokcdn-us.com/tos-useast8-p-0068-tx2/osAHzWAIipBAxVMie5CIDqxK0ArEREAAEKVSfn~tplv-tiktokx-origin.image?dr=9636&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5",
"type": "video",
"url": null,
"width": 720
}
],
"mentions": [],
"metrics": {
"comment_count": 12600,
"like_count": 690100,
"quote_count": null,
"repost_count": null,
"save_count": 40145,
"share_count": 20800,
"view_count": 6900000
},
"platform": "tiktok",
"platform_specific": {
"tiktok": {
"author_sec_uid": "MS4wLjABAAAAwAg0rSzO65WQfz4RzQgGv2Xdv108BgPXhRrrmNVIHQZ9PO8-flwwRtEppYTS0OjA",
"country_code": null,
"is_ad": false,
"is_ai_generated": true,
"location": null,
"music": {
"author_name": "Khabane lame",
"cover_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/08987e23b94057953fd4f1738694bf5f~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=07918010&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
"duration_ms": 30000,
"id": "7694335437137087262",
"is_original": true,
"play_url": "https://v16-webapp-prime.us.tiktok.com/video/tos/useast8/tos-useast8-v-27dcd7-tx2/o4fHr3qIArT5AEXEiCLVExDGAeqCAIOVe4joPR/?a=1988&bti=ODszNWYuMDE6&&bt=125&ft=GcAInInz7Th~v-RGXq8Zmo&mime_type=audio_mpeg&rc=M3lzeG45cm92ZTMzaTU8NEBpM3lzeG45cm92ZTMzaTU8NEBzNGtiMmRjXjBhLS1kMTJzYSNzNGti…",
"title": "original sound"
}
}
},
"platform_type": "video",
"published_at": "2026-10-08T16:31:16Z",
"text": "Was the ball out, or was it just my imagination? 🤨#learnfromkhaby #comedy",
"thumbnail_url": "https://p19-common-sign.tiktokcdn-us.com/tos-useast8-p-0068-tx2/osAHzWAIipBAxVMie5CIDqxK0ArEREAAEKVSfn~tplv-tiktokx-origin.image?dr=9636&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5",
"title": null,
"type": "video",
"unknown_fields": [],
"url": "https://www.tiktok.com/@khaby.lame/video/7694335322922044703"
},
{
"approximate_fields": [
"metrics.view_count",
"metrics.like_count",
"metrics.share_count"
],
"author": {
"avatar_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/08987e23b94057953fd4f1738694bf5f~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=07918010&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
"display_name": "Khabane lame",
"id": "127905465618821121",
"is_verified": true,
"url": "https://www.tiktok.com/@khaby.lame",
"username": "khaby.lame"
},
"coauthors": [],
"duration_ms": 34000,
"hashtags": [
"learnfromkhaby",
"comedy"
],
"id": "7691740195418377502",
"is_nsfw": null,
"is_pinned": null,
"is_sponsored": null,
"language": "en",
"media": [
{
"alt_text": null,
"duration_ms": 34000,
"expires_at": null,
"height": 1280,
"thumbnail_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-p-0068-tx2/o0jRDQRMAbfFAlZX8eqGQcARBqerRfAHR7gVSU~tplv-tiktokx-origin.image?dr=9636&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5",
"type": "video",
"url": null,
"width": 720
}
],
"mentions": [],
"metrics": {
"comment_count": 7325,
"like_count": 418500,
"quote_count": null,
"repost_count": null,
"save_count": 20087,
"share_count": 23900,
"view_count": 5000000
},
"platform": "tiktok",
"platform_specific": {
"tiktok": {
"author_sec_uid": "MS4wLjABAAAAwAg0rSzO65WQfz4RzQgGv2Xdv108BgPXhRrrmNVIHQZ9PO8-flwwRtEppYTS0OjA",
"country_code": null,
"is_ad": false,
"is_ai_generated": true,
"location": null,
"music": {
"author_name": "Khabane lame",
"cover_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-avt-0068-tx2/08987e23b94057953fd4f1738694bf5f~tplv-tiktokx-cropcenter:1080:1080.jpeg?dr=9640&refresh_token=07918010&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=81f88b70&idc=useast5",
"duration_ms": 34000,
"id": "7691740326570019615",
"is_original": true,
"play_url": "https://v16-webapp-prime.us.tiktok.com/video/tos/useast8/tos-useast8-v-27dcd7-tx2/ogcDFBGWoU0A8F0eIAAREf8XVjvDqICFsEDJAo/?a=1988&bti=ODszNWYuMDE6&&bt=125&ft=GcAInInz7Th~v-RGXq8Zmo&mime_type=audio_mpeg&rc=M3hmanQ5cjg7ZDMzaTU8NEBpM3hmanQ5cjg7ZDMzaTU8NEBjMGgyMmRzbnNhLS1kMTJzYSNjMGgy…",
"title": "original sound"
}
}
},
"platform_type": "video",
"published_at": "2026-10-01T16:40:54Z",
"text": "Just give me 5 more minutes 😭#learnfromkhaby #comedy",
"thumbnail_url": "https://p16-common-sign.tiktokcdn-us.com/tos-useast8-p-0068-tx2/o0jRDQRMAbfFAlZX8eqGQcARBqerRfAHR7gVSU~tplv-tiktokx-origin.image?dr=9636&x-expires=4070908800&x-signature=SCRUBBED&t=4d5b0474&ps=13740610&shp=81f88b70&shcp=43f4a2f9&idc=useast5",
"title": null,
"type": "video",
"unknown_fields": [],
"url": "https://www.tiktok.com/@khaby.lame/video/7691740195418377502"
}
],
"page": {
"has_more": true,
"next_cursor": "c1.EXAMPLE_CURSOR",
"next_cursor_expires_at": "2026-10-26T12:00:00Z",
"end_reason": null
},
"meta": {
"request_id": "req_3f9c2a7d1e8b4c6a9f0e2d4b6a8c0e1f",
"platform": "tiktok",
"source": "api",
"is_cached": false,
"credits_charged": 1,
"latency_ms": 0,
"fetched_at": "2026-10-25T12:00:00Z",
"input": {
"kind": "profile",
"id": "127905465618821121",
"url": "https://www.tiktok.com/@khaby.lame"
},
"credits_remaining": 9999,
"client_request_id": null,
"warnings": [],
"mode": "live"
}
}Long arrays and strings are shortened for display; real responses are complete.
Pricing
Pay per page. Only when it succeeds.
| Source | Price per successful page | Per 1,000 pages |
|---|---|---|
| Any source | 1 credit | $0.75–$1.45 |
- Failed requests are free: errors cost 0 credits.
- Cache hits are free: responses served from the cache cost 0 credits.
- Credits never expire.
- One credit balance for every endpoint and platform.
Credit packs · How this endpoint is priced · How credits work
FAQ
Questions, answered.
How much does the TikTok Posts API cost?
1 credit per page of up to 15 items, charged for every successful page, empty pages included. SocialTap credits cost $0.75–$1.45 per 1,000 depending on the pack.
How do I get the next page?
Repeat the same request with cursor set to page.next_cursor from the previous response, keeping the other parameters. When page.has_more is false there are no more items. Cursors expire after 24 hours.
Am I charged for failed requests?
No. Requests that return an error cost 0 credits. Responses served from the cache are free too.