Concepts
Addressing videos and creators
How to name a video, creator, hashtag or sound, whether you hold a URL or a platform id.
Almost everything you hold when you start is a URL somebody pasted into a chat. Almost everything the API is addressed by is a platform and an id. This page is the map between the two, per subject, so you never have to write a regular expression over a TikTok link again.
What takes what
| Subject | Addressed by | Endpoint |
|---|---|---|
| Video | platform + platform id, or a URL | GET /v1/videos/{platform}/{id} |
| Video by URL | `url` in the body | POST /v1/videos/lookup |
| Creator | platform + handle, `@handle` or a profile URL | GET /v1/creators/{platform}/{handle} |
| Hashtag | platform + tag, without the `#` | GET /v1/hashtags/{platform}/{tag} |
| Sound | platform + sound id | GET /v1/sounds/{platform}/{id} |
Videos
A video has two spellings. If you already know the platform and the id, put them in the path:
GET /v1/videos/{platform}/{id}·$0.005 fresh, $0.00125 cached. With `include=intelligence`: $0.02 fresh, $0.005 cached, instead of the lookup price (`refresh=true` is always fresh). With `include=transcript` only: a flat $0.005, cached or not.
curl "https://www.shortsintel.com/v1/videos/tiktok/7300000000000000000" \
-H "Authorization: Bearer sk_live_YOUR_KEY"const response = await fetch(
"https://www.shortsintel.com/v1/videos/tiktok/7300000000000000000",
{
method: "GET",
headers: {
"Authorization": "Bearer sk_live_YOUR_KEY",
},
},
);
if (!response.ok) {
throw new Error(`ShortsIntel ${response.status}`);
}
const data = await response.json();import requests
response = requests.get(
"https://www.shortsintel.com/v1/videos/tiktok/7300000000000000000",
headers={
"Authorization": "Bearer sk_live_YOUR_KEY",
},
)
response.raise_for_status()
data = response.json()If what you have is a link, hand over the link and let us parse it:
POST /v1/videos/lookup·$0.005 fresh, $0.00125 when a Snapshot under 24 h old can answer.
curl -X POST "https://www.shortsintel.com/v1/videos/lookup" \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://www.tiktok.com/@someone/video/7300000000000000000"}'const response = await fetch(
"https://www.shortsintel.com/v1/videos/lookup",
{
method: "POST",
headers: {
"Authorization": "Bearer sk_live_YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"url": "https://www.tiktok.com/@someone/video/7300000000000000000"
}),
},
);
if (!response.ok) {
throw new Error(`ShortsIntel ${response.status}`);
}
const data = await response.json();import requests
response = requests.post(
"https://www.shortsintel.com/v1/videos/lookup",
headers={
"Authorization": "Bearer sk_live_YOUR_KEY",
"Content-Type": "application/json",
},
json={
"url": "https://www.tiktok.com/@someone/video/7300000000000000000",
},
)
response.raise_for_status()
data = response.json()These are the URL shapes that parse:
https://www.tiktok.com/@someone/video/7300000000000000000
https://www.instagram.com/reel/C8xKq2mR1aB/
https://www.youtube.com/shorts/dQw4w9WgXcQ
https://www.youtube.com/watch?v=dQw4w9WgXcQ
https://youtu.be/dQw4w9WgXcQThe ids they yield are the platform’s own: TikTok’s numeric video id, Instagram’s reel shortcode, YouTube’s eleven character id. A link to something that is not a TikTok, Instagram or YouTube video is an unsupported_platform problem, and a link on a platform we know that has no id in it is invalid_request.
Why both spellings cost the same
$0.005 fresh, $0.00125 when a Snapshot under 24 h old can answer. The URL form is not a second operation. It parses the platform and id out of the link and then runs exactly the operation the path form runs, so both create the same kind of Run, both are charged the same, and both read and write the same stored Snapshot.
That matters more than it sounds. Look a video up by URL, then look the same video up by platform and id a minute later, and the second call is a cached read at a quarter of the price — the cache is keyed on the platform and the id, which is what the URL resolved to. Two teams holding the same video in two different notations never pay twice for the same fetch.
200 · The Run finished inside the wait budget: `{ run, result }`. The example was answered from a Snapshot under 24 h old, which is why `run.cached` is true and the charge is a quarter of `list_price_usd`.
{
"run": {
"id": "run_8f2c1ad04b",
"kind": "video_lookup",
"status": "completed",
"created_at": "2026-09-08T09:14:02.418Z",
"completed_at": "2026-09-08T09:14:04.930Z",
"charge_usd": 0.00125,
"list_price_usd": 0.005,
"cached": true
},
"result": {
"platform": "tiktok",
"id": "7300000000000000000",
"url": "https://www.tiktok.com/@someone/video/7300000000000000000",
"description": "3 minutes at 4°C every morning for 30 days. Here is what actually changed. #coldplunge #recovery",
"posted_at": "2026-09-05T06:32:11.000Z",
"duration_seconds": 41,
"thumbnail_url": "https://p16-sign.tiktokcdn.com/obj/tos-cover-7300000000000000000.jpeg",
"hashtags": [
"coldplunge",
"recovery",
"morningroutine"
],
"sound": {
"id": "7180000000000000000",
"title": "original sound - someone"
},
"creator": {
"id": "cre_9d4b2a7f1c",
"handle": "someone",
"name": "Someone",
"follower_count": 184300
},
"snapshot": {
"at": "2026-09-08T09:14:04.219Z",
"views": 1284000,
"likes": 143200,
"comments": 2810,
"shares": 9040,
"saves": 21600
},
"scores": {
"outlier": 4.21,
"outlier_p90": 1.6,
"percentile": 95,
"velocity": {
"views_per_hour": 6698.6,
"window": "snapshot"
},
"reason": null,
"baseline": {
"median_views": 305000,
"p90_views": 802000,
"engagement_rate": 0.1342,
"posts_per_week": 4.1,
"sample": 20,
"confidence": "high",
"source": "videos",
"computed_at": "2026-09-08T09:14:03.902Z",
"stale": false
}
}
}
}200 · With `estimate=true`: the price, no Run, no charge.
{
"estimate_usd": 0.005,
"cached": true
}run.cached is what says which you got: charge_usd below list_price_usd is the same answer served from what was already stored.
Creators
A creator is a platform and a handle. The handle segment accepts whatever the user pasted:
someone
@someone
https://www.tiktok.com/@someone
https://www.instagram.com/someone/
https://www.youtube.com/@someonePass a profile URL and URL-encode it, since it contains slashes. A leading @ and letter case are noise and are stripped before anything is looked up, so @Someone and someone are one creator. A URL that is not a profile URL for the platform in the path is rejected rather than guessed at.
GET /v1/creators/{platform}/{handle}·$0.02 fresh; 25% when a creator Snapshot under 24 h old and a baseline under 7 days old can answer.
curl "https://www.shortsintel.com/v1/creators/tiktok/someone?include=videos&wait=30" \
-H "Authorization: Bearer sk_live_YOUR_KEY"const response = await fetch(
"https://www.shortsintel.com/v1/creators/tiktok/someone?include=videos&wait=30",
{
method: "GET",
headers: {
"Authorization": "Bearer sk_live_YOUR_KEY",
},
},
);
if (!response.ok) {
throw new Error(`ShortsIntel ${response.status}`);
}
const data = await response.json();import requests
response = requests.get(
"https://www.shortsintel.com/v1/creators/tiktok/someone?include=videos&wait=30",
headers={
"Authorization": "Bearer sk_live_YOUR_KEY",
},
)
response.raise_for_status()
data = response.json()Hashtags and sounds
A hashtag is a platform and the tag without the #: coldplunge, not #coldplunge.
GET /v1/hashtags/{platform}/{tag}·$0.02 fresh, 25% when a Snapshot under 24 h old can answer.
curl "https://www.shortsintel.com/v1/hashtags/tiktok/coldplunge?wait=30" \
-H "Authorization: Bearer sk_live_YOUR_KEY"const response = await fetch(
"https://www.shortsintel.com/v1/hashtags/tiktok/coldplunge?wait=30",
{
method: "GET",
headers: {
"Authorization": "Bearer sk_live_YOUR_KEY",
},
},
);
if (!response.ok) {
throw new Error(`ShortsIntel ${response.status}`);
}
const data = await response.json();import requests
response = requests.get(
"https://www.shortsintel.com/v1/hashtags/tiktok/coldplunge?wait=30",
headers={
"Authorization": "Bearer sk_live_YOUR_KEY",
},
)
response.raise_for_status()
data = response.json()A sound is a platform and the sound id. On TikTok that is the clipId at the end of a /music/<slug>-<clipId> URL — not the music.id carried on a video payload, which is a different number and will not resolve.
GET /v1/sounds/{platform}/{id}·$0.02 fresh, 25% cached.
curl "https://www.shortsintel.com/v1/sounds/tiktok/7180000000000000000?wait=30" \
-H "Authorization: Bearer sk_live_YOUR_KEY"const response = await fetch(
"https://www.shortsintel.com/v1/sounds/tiktok/7180000000000000000?wait=30",
{
method: "GET",
headers: {
"Authorization": "Bearer sk_live_YOUR_KEY",
},
},
);
if (!response.ok) {
throw new Error(`ShortsIntel ${response.status}`);
}
const data = await response.json();import requests
response = requests.get(
"https://www.shortsintel.com/v1/sounds/tiktok/7180000000000000000?wait=30",
headers={
"Authorization": "Bearer sk_live_YOUR_KEY",
},
)
response.raise_for_status()
data = response.json()Trackers address the same way
POST /v1/trackers takes a kind, a platform and a target, where the target is the same string the matching lookup would take: a handle for a creator, a video id, a tag, a sound id. Targets are normalised before they are compared, so two people who write one creator differently end up on one Tracker and one daily fetch.
Once you can name a thing, pagination covers reading lists of them, and platforms and coverage covers what each platform can actually answer.