If you run a restaurant, a gym, a hotel or a venue, TikTok is already carrying content about you that never appears in any review platform. Some of it is tagged with your location, and tagged content is readable through the API. This guide is a practical workflow for that: use place-info to identify a location and size it, place-posts to pull the videos filmed there, and post-detail to attach real engagement numbers. It also covers the caveat that decides whether your analysis is honest or misleading: you are measuring tagged videos, not everything people post about the place.
What a TikTok place is
A place on TikTok is a point of interest with a numeric ID. It can be a single business, but it can just as easily be a district, a neighbourhood or a broad area - the example location used throughout this guide, 22535796481538024, is District 12 in Ho Chi Minh City. That granularity difference matters more than anything else in your setup. A district-level place ID aggregates thousands of unrelated videos; a venue-level ID is tight enough to read one by one. Check what you are holding before you build a dashboard on it.
There is no search-by-name call for locations. You obtain place IDs from the poi.id field attached to video items that were tagged somewhere - place-posts items carry a poi block, and so do items from feed endpoints such as user-repost. The practical bootstrap is: find one video you know was filmed at the venue, read its poi.id, then store that ID permanently. If you are new to the Places category, the companion piece on getting TikTok location data via API walks through the identifier plumbing in more detail.
Step 1: identify and size the location
The base URL is https://api.primeapi.co/ and authentication is one header, X-PrimeAPI-Key. place-info takes a single required parameter, placeId.
curl -s "https://api.primeapi.co/place-info?placeId=22535796481538024" \
-H "X-PrimeAPI-Key: YOUR_API_KEY"
Everything useful sits under poiInfo. The poi object describes the place and stats.videoCount tells you how much tagged content exists.
| Field | Type | What it holds |
|---|---|---|
poi.id | string | The place ID. Your primary key. |
poi.name | string | Display name, e.g. "District 12". |
poi.address | string | Address line as TikTok stores it. |
poi.city / poi.cityCode | string | City name and TikTok's internal city code. |
poi.country / poi.countryCode | string | Country, often empty on sub-city places. |
poi.province | string | Region, also frequently empty. |
poi.category | string | Category label, e.g. "District". |
poi.ttTypeNameSuper | string | Broadest taxonomy tier, e.g. "Place and Address". |
poi.ttTypeNameMedium | string | Middle tier, e.g. "Places". |
poi.ttTypeNameTiny | string | Narrowest tier - the one worth storing. |
poi.ttTypeCode / poi.typeCode / poi.type | string / int | Machine-readable type identifiers. |
poi.fatherPoiId / poi.fatherPoiName | string | Parent place, when this one sits inside another. |
poi.isClaimed | bool | Whether the listing has been claimed. |
poi.indexEnabled / poi.isCollected | bool | Discoverability and collection flags. |
poi.phoneInfo.exist | bool | Whether a phone number is attached. |
poi.pictureAlbum.totalCount | int | Images held against the place. |
poi.poiDetailTags | array | Extra descriptive tags. |
stats.videoCount | int | Videos tagged at this place. Your volume metric. |
Two of these carry business meaning immediately. isClaimed separates venues that treat TikTok as a channel from venues that are only being filmed by other people. And fatherPoiId saves you from double counting: if a venue place sits inside a mall place, both return videos, and some of them are the same content at different granularity.
The envelope also returns shareMeta.title and shareMeta.desc, plus statusCode, status_code, status_msg, an extra object with logid and now, and log_pb.impr_id. Write the two log identifiers into your own request log; they are the fastest way to point support at one specific upstream response.
Step 2: pull the tagged videos
place-posts requires placeId and accepts count and cursor. Send cursor=0 first, then return whatever cursor string the previous response gave you and stop when hasMore goes false.
curl
curl -s "https://api.primeapi.co/place-posts?placeId=22535796481538024&count=20&cursor=0" \
-H "X-PrimeAPI-Key: YOUR_API_KEY"
Node.js (Axios)
const axios = require("axios");
const KEY = "YOUR_API_KEY";
const BASE = "https://api.primeapi.co";
async function placePosts(placeId, maxPages = 5, count = 20) {
const items = [];
let cursor = "0";
for (let page = 0; page < maxPages; page++) {
const res = await axios.get(BASE + "/place-posts", {
params: { placeId, count, cursor },
headers: { "X-PrimeAPI-Key": KEY }
});
const body = res.data;
items.push(...(body.itemList || []));
console.log("balance:", res.headers["x-primeapi-balance"]);
cursor = body.cursor;
if (body.hasMore !== true) break;
}
return items;
}
placePosts("22535796481538024")
.then(items => items.forEach(it =>
console.log(it.id, it.author.uniqueId, it.poi && it.poi.name, it.desc)))
.catch(err => console.error(err.response ? err.response.data : err.message));
Each entry in itemList is a video record. The fields you will actually use are id (the post ID), desc (the caption), createTime (Unix seconds as an int), the author block, authorStats and authorStatsV2, the music object, challenges for the hashtags on the clip, and the item's own poi block. Flags worth branching on include isAd, originalItem, privateItem, secret, isReviewing and duetEnabled.
The author object gives you id, uniqueId, nickname, secUid, signature, three avatar sizes and the verified and privateAccount flags. authorStats adds followerCount, followingCount, heart, heartCount, videoCount, diggCount and friendCount. That is enough to rank the people filming at your location without a single extra call.
Check the per-item poi.id against the place ID you queried. On broad places the feed can include items tagged at nested points of interest, and the item's own poi.name, poi.address and poi.fatherPoiName tell you which is which. Filtering on an exact poi.id match is the difference between "videos at my restaurant" and "videos somewhere in my neighbourhood".
Step 3: attach engagement with post-detail
post-detail takes one required parameter, postId, and returns the full record for a single video under itemInfo.itemStruct. This is where the numbers live: stats holds playCount, diggCount, commentCount, shareCount and collectCount, and statsV2 repeats those and adds repostCount.
import requests
BASE = "https://api.primeapi.co"
HEADERS = {"X-PrimeAPI-Key": "YOUR_API_KEY"}
def place_posts(place_id, max_pages=5, count=20):
items, cursor = [], "0"
for _ in range(max_pages):
r = requests.get(
f"{BASE}/place-posts",
params={"placeId": place_id, "count": count, "cursor": cursor},
headers=HEADERS,
timeout=20,
)
r.raise_for_status()
body = r.json()
items.extend(body.get("itemList", []))
cursor = body.get("cursor")
if not body.get("hasMore"):
break
return items
def post_stats(post_id):
r = requests.get(
f"{BASE}/post-detail",
params={"postId": post_id},
headers=HEADERS,
timeout=20,
)
r.raise_for_status()
item = r.json()["itemInfo"]["itemStruct"]
s = item.get("stats", {})
return {
"id": item["id"],
"author": item["author"]["uniqueId"],
"createTime": int(item["createTime"]),
"playCount": s.get("playCount", 0),
"diggCount": s.get("diggCount", 0),
"commentCount": s.get("commentCount", 0),
"shareCount": s.get("shareCount", 0),
"collectCount": s.get("collectCount", 0),
}
def venue_report(place_id, exact_only=True):
rows = []
for it in place_posts(place_id):
if exact_only and (it.get("poi") or {}).get("id") != place_id:
continue
if it.get("isAd"):
continue
rows.append(post_stats(it["id"]))
plays = sum(r["playCount"] for r in rows)
print(f"{place_id}: {len(rows)} videos, {plays} plays")
return rows
Two shape differences catch people out. place-posts reports createTime as an integer while post-detail returns it as a string of seconds, so cast before you sort. And the post-detail envelope uses statusCode and statusMsg in camel case, where the place endpoints return statusCode, status_code and status_msg together. The guide to TikTok video data covers the rest of itemStruct, including the video and music blocks.
Measuring share of voice
Share of voice for a venue is your slice of the tagged conversation in a defined competitive set. Build the set deliberately: five to twenty place IDs for comparable businesses in the same catchment, all at the same granularity. Mixing your single restaurant against a whole district produces a number that means nothing.
Run it in two tiers. The cheap tier is volume share: call place-info once per venue and divide your stats.videoCount by the sum across the set. Twenty venues cost twenty credits. The expensive tier is attention share, where you page place-posts, enrich with post-detail and divide your total playCount by the set total. Volume share tells you who gets tagged most; attention share tells you whose tagged content travels, and the two often disagree - a venue with forty low-reach videos can lose to one with six that landed. For per-video normalisation against creator size, the method in the post on calculating TikTok engagement rate applies unchanged here.
Finding creators who post nearby
The author blocks in place-posts are a ready-made local creator list. Collect items across your competitive set, group by author.id, and for each creator record how many videos they tagged in the area, which venues they tagged, their authorStats.followerCount and whether verified is true. Filter out privateAccount and anything with isAd set, since paid placements tell you about a competitor's budget rather than about organic interest.
Sorting that table by follower count gives the obvious names. Sorting by distinct venues tagged gives something more useful: people who genuinely document the local scene rather than posting once about their own dinner. Those are your outreach targets. Each item also hands you author.secUid, which is the key the User endpoints need if you want to go deeper into any creator's full history.
The sampling caveat - read this before you report
Everything above measures tagged videos. A location tag is an optional action the poster has to take, and most do not take it. That produces three biases you should state openly in any deck built on this data.
First, undercounting. The true volume of TikTok content about a venue is larger than videoCount, often by a wide margin, so never phrase the output as total mentions. Second, tagging habits differ by venue type. Places that are visually distinctive or that actively prompt tagging accumulate tags faster, so a low count can reflect signage rather than silence. Third, fragmentation: one business can be reachable through several place IDs, and a competitor with a tidy single listing looks better than one whose traffic is split across three. Check fatherPoiId, fatherPoiName and address before concluding that anyone is winning.
The honest framing is that place data is a directional, low-cost indicator of local attention, not a census. If you need broader coverage of a venue that is discussed more than it is tagged, run a parallel collection over the hashtags people use for it - the approach in getting TikTok hashtag videos via API covers that path - and reconcile the two sets on post id.
Credits, limits and errors
Every request costs 1 credit regardless of what it returns, and the remaining balance comes back in the X-PrimeAPI-Balance header. The default rate limit is 100 requests per minute and responses are live rather than cached, averaging around a second. A twenty-venue weekly run with three place-posts pages each and detail calls on the top fifty videos lands near 130 credits per pass, so a season of tracking fits comfortably inside the entry tier on the pricing page (2,500 credits for $9.90). New accounts get 50 free credits, which is enough to profile a handful of locations before committing.
Four failure modes come back as a JSON message: "Please sign up to primeapi.co" when the key header is missing, "PrimeAPI-Key is not available" for a wrong or unactivated key, the per-minute limit message when you exceed 100 requests in a window, and "Your balance has been exhausted..." when credits run out. Branch on the body. A quiet location is not an error - it returns an empty itemList with hasMore false, which is a real finding about the venue. Full parameter tables are in the API documentation, which is also where you can try all three endpoints against a live place ID before writing code.
The routine, condensed
Store a place ID per venue in your competitive set. Once a week, call place-info on each for videoCount and log the series; page place-posts for anything that moved; filter to exact poi.id matches and drop isAd items; enrich the survivors with post-detail for plays and comments; and roll the authors into a standing local creator table keyed on author.id. The trend line matters more than any single reading, because week-over-week change in tagged volume is far less affected by the sampling bias than the absolute number is.
PrimeApi is an independent service and is not affiliated with or endorsed by TikTok or ByteDance. Create an account and the 50 free credits will cover a first pass over your own venue and its nearest competitors.
Frequently asked questions
Where do I get a TikTok place ID?
From the poi.id field on any video item that carries one. Feed responses such as place-posts and user-repost attach a poi object to items that were tagged at a location, and its id is exactly the value place-info and place-posts expect as placeId. There is no name-to-ID lookup call, so in practice you harvest IDs from video items first and keep them in your own table.
Does place-posts return every video filmed at that location?
No, and this is the single most important limit to understand. It returns videos whose author chose to attach the location tag. Most people who film at a venue never tag it, so what you get is a sample of tagged content, not a census of all content about the place. Treat the numbers as a directional signal and never present them as total mentions.
Why are there no play counts on place-posts items?
The item objects in itemList carry identity and context - id, desc, createTime, the author block, authorStats, music and the poi block - and the reliable place to read per-video engagement is post-detail, which returns a full stats object with playCount, diggCount, commentCount, shareCount and collectCount. So you page cheaply first, then spend one extra credit per video you actually care about.
What does videoCount in place-info measure?
It is poiInfo.stats.videoCount, TikTok's own count of videos tagged at that place ID. It is the cheapest volume metric available - one credit gives you the number without paging anything - which makes it the right field for a first-pass share of voice comparison across a set of venues. It counts tagged videos, not views and not total mentions of the business.
How does paging work on place-posts?
Send cursor=0 on the first call, then echo back the cursor string from each response verbatim and stop when hasMore is false. The cursor is opaque - in captured responses it looks like "644602", which is neither an offset nor a timestamp - so never try to compute or increment it yourself. count sets the page size.
How many credits does a local sweep cost?
One credit per request across all three endpoints. A volume-only comparison of twenty venues is twenty place-info calls, so twenty credits. Adding three pages of place-posts per venue is sixty more, and enriching two hundred videos with post-detail is another two hundred. New accounts start with 50 free credits, which covers a full pass over a handful of locations.