TikTok API endpoint
Returns one place's photo album, keyed by the `poi_id` that the place search endpoint returns. The upstream is `/tiktok/poi/picture_album/v1`: `categories` holds each album tab (`name`, `pic_tab_typecode`, `pictures` of bare image URL lists and `pictures_v2`, the same photos with `author` attribution, `pic_id`, `source` and origin/compressed URL info), `photo_count` sums the categories' pictures, and `title` is the app's own header. Measured 2026-10-21 with the pure-Go (device-free) signer: one place answered 5,368 B, title "Gallery (3)", 1 category with 3 photos whose `pictures_v2` entries name the Google-places contributor; another answered 24,196 B with more photos. The album is one-shot: `count` and `pic_tab_typecode` were measured and did not change the page. A nonexistent id answers 500 (the upstream answers status 4 "Server is currently unavailable" on this route, unlike the poi-not-exist code the other place routes use). The route is not signature-gated, so it does not need the device-backed signer.
/tiktok/mobile/place/photosOne place's photo album, keyed by the poi_id the place search endpoint returns. The upstream is
GET /tiktok/poi/picture_album/v1, captured from the running 47.4.3 app on the place detail screen and reproduced
end-to-end against the live gateway. This is the photo tab of the place page: the albums the app groups the
place's pictures into, with per-photo attribution.
Album categories, with attribution. categories holds each album tab. A measured category carries name,
pic_tab_typecode, pictures (bare image URL lists) and pictures_v2 (the same photos with author
attribution, pic_id, source and origin/compressed URL info). For the measured places the photos are
Google-places images, and pictures_v2[].author names the contributor (keyword like @Ohhitskel) plus their
Google Maps contribution link. photo_count sums the categories' pictures and title is the app's own header
(e.g. "Gallery (3)"). category_tab_info is the tab strip the app renders.
One-shot. count and pic_tab_typecode were measured and did not change the page, so the album is returned
whole; there is no cursor to page.
Device-free. The pure-Go placeholder signer returned the same album, so a deployment needs
TIKTOK_MOBILE_PARAMS but not the device-backed signer (TIKTOK_MOBILE_SIGNER_URL). It answers on the core
aggr16-normal.tiktokv.us / api16-normal-useast5.tiktokv.us pair.
Query parameters:
poi_id: required. The numeric place id from /api/v1/tiktok/mobile/search/place.Errors:
poi_id is missing or not numeric.Measured 2026-10-21 (placeholder/device-free signer, core pair):
| call | result |
|---|---|
poi_id=42203861086875992 | 5,368 B; title "Gallery (3)"; 1 category ("Other", typecode 080000) with 3 photos, all 3 attributed in pictures_v2 |
poi_id=21568226355448582 | 24,196 B (more photos) |
count=50 / pic_tab_typecode=080000 | same page (both parameters measured as no-ops) |
poi_id=1 | upstream status 4 "Server is currently unavailable" → 500 |
| shipped-service canary | title="Gallery (3)" categories=1 photos=3, 0.525 s |
Example request:
GET /api/v1/tiktok/mobile/place/photos?poi_id=42203861086875992
Example response (truncated):
{
"code": 200,
"msg": "OK",
"data": {
"poi_id": "42203861086875992",
"title": "Gallery (3)",
"categories": [
{
"name": "Other",
"pic_tab_typecode": "080000",
"pictures": [{"url_list": ["https://lh3.googleusercontent.com/grass-cs/...=s1600-w720"]}],
"pictures_v2": [
{
"pic_id": "19e8e8efc0aecb623c7c9665f5cecec5",
"source": "google",
"author": {"keyword": "@Ohhitskel", "link": {"url_list": ["https://maps.google.com/maps/contrib/..."]}},
"origin_url_info": {"url_list": ["..."]},
"compressed_url_info": {"url_list": ["..."]}
}
]
}
],
"category_tab_info": [{"name": "Other", "pic_tab_typecode": "080000"}],
"photo_count": 3,
"host": "aggr16-normal.tiktokv.us",
"fetched_at": "2026-10-21T16:31:40Z"
}
}
Notes for callers:
photo_count counts pictures when present, falling back to pictures_v2; the two arrays describe the same
photos.| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| x-api-key (header) | string | Yes | API key required | ||
| poi_id | string | Yes | Place id from the place search endpoint | 42203861086875992 |
curl -X GET "https://api.crawlora.net/api/v1/tiktok/mobile/place/photos?poi_id=42203861086875992" \ -H "x-api-key: $CRAWLORA_API_KEY"
{
"code": 200,
"msg": "OK",
"data": {
"poi_id": "42203861086875992",
"title": "Gallery (3)",
"categories": [
{
"name": "Other",
"pic_tab_typecode": "080000",
"pictures": [
{
"url_list": [
"https://lh3.googleusercontent.com/grass-cs/...=s1600-w720"
]
}
],
"pictures_v2": [
{
"pic_id": "19e8e8efc0aecb623c7c9665f5cecec5",
"source": "google",
"author": {
"keyword": "@Ohhitskel",
"link": {
"url_list": [
"https://maps.google.com/maps/contrib/..."
]
}
},
"origin_url_info": {
"url_list": [
"..."
]
},
"compressed_url_info": {
"url_list": [
"..."
]
}
}
]
}
],
"category_tab_info": [
{
"name": "Other",
"pic_tab_typecode": "080000"
}
],
"photo_count": 3,
"host": "aggr16-normal.tiktokv.us",
"fetched_at": "2026-10-21T16:31:40Z"
}
}Request schema
No body schema
Response schema
#/definitions/tiktokmobile.placePhotosResponseDoc
| Field | Type | Required | Enum | Bounds | Example | Description |
|---|---|---|---|---|---|---|
| categories | array | No | ||||
| category_tab_info | unknown | No | ||||
| fetched_at | string | No | ||||
| host | string | No | aggr16-normal.tiktokv.us | |||
| photo_count | integer | No | 3 | |||
| poi_id | string | No | 42203861086875992 | |||
| title | string | No | Gallery (3) |
| Status | Description | Schema |
|---|---|---|
| 400 | Invalid input | #/definitions/app.Response |
| 429 | Rate limit exceeded | #/definitions/app.Response |
| 500 | Internal server error, including a place id that does not resolve | #/definitions/app.Response |
Use environment variables for secrets and keep Crawlora API keys server-side.
curl -X GET "https://api.crawlora.net/api/v1/tiktok/mobile/place/photos?poi_id=42203861086875992" \
-H "x-api-key: $CRAWLORA_API_KEY"