TikTok API endpoint
Searches TikTok photo posts with the app's own Photos-tab call and returns the page unmodified. Rows live in `search_item_list` (each row's `aweme_info` carries `image_post_info.images`, the carousel images) while `aweme_list` is an empty array; `feedback_type` is `"photo_tab"`. `keyword` is required; `count` is bounded at 50 (default 10, the app's value) and `offset` is forwarded as the app's row offset. `sort_type` (0 relevance, 1 like count, 3 date posted) and `publish_time` (recency window in days) are the app's filter-sheet controls. The route is signature-validated, so it needs device-backed request signing to be configured for the deployment and fails with an error instead of returning an empty page when it is not.
/tiktok/mobile/search/photosKeyword search over photo posts — the mobile search screen's Photos tab. The route was captured from the
running 47.4.3 app over mitmproxy (the Photos tab fires POST /aweme/v1/search/photo/) and then reproduced
end-to-end with the device-backed signer, so the request body and the response shape are measured.
Read the envelope split. Like the video tab, the page lives in search_item_list: each row is
{aweme_info, search_aweme_info}, and photo posts carry their carousel in
aweme_info.image_post_info.images (1-8 images measured per row). aweme_list is present but empty; that is
the normal successful shape on this build, not "no results". feedback_type is the tab discriminator (measured
"photo_tab").
Requires device-backed request signing. This route validates signature values, so the deployment must have
the device-backed signer configured (TIKTOK_MOBILE_SIGNER_URL). Without it the endpoint fails closed with a
typed error instead of returning an empty page.
Query parameters:
keyword: required. The search keyword. Example: lofi.count: optional. Rows per page; defaults to the app's 10. Bounded at 50.offset: optional. Forwarded as the app's row offset (the app sends 0). First-page-only in guest mode:
see the caveat below.sort_type: optional. The app's filter-sheet sort order: 0 = Relevance (default), 1 = Like count,
3 = Date posted. 2 is rejected (the app never sends it).publish_time: optional. The app's Date-posted window in days: 0 = All time (default), 1 = Past 24
hours, 7 = This week, 30 = This month, 90 = Last 3 months, 180 = Last 6 months.search_source: optional. Forwarded as the app forwards it; defaults to the app's tab_search value.The app's captured body is a form POST carrying keyword, count, offset, search_source,
query_correct_type=1, is_filter_search, sort_type and publish_time; this endpoint sends that same body.
is_filter_search becomes 1 whenever sort_type or publish_time is non-zero.
Notable response behavior:
[]any), so a richer photo post never loses fields to an incomplete schema.cursor, has_more, feedback_type, response_cropped (measured 1),
extra, log_pb, backtrace, status_code and status_msg.has_more: 1, but offset=10 returned a 1,218 B empty page
(cursor: 20, has_more: 0). Treat this as a first-page-only route in guest mode until a live capture shows
the real page token.Measured 2026-10-21 with the device-backed signer (aggr16-normal.tiktokv.us edge /
api16-normal-useast5.tiktokv.us origin):
keyword=lofi&count=10 → 201,415 B, 10 rows, feedback_type: "photo_tab", cursor: 10, has_more: 1.keyword=lofi&count=30 → 709,255 B, 29 rows (the page size is honoured; one row was dropped upstream),
cursor: 30.keyword=ラーメン&count=5 → 159,322 B, 5 rows; the first row is an 8-image carousel.offset=10 → the empty page described above.Example request:
GET /api/v1/tiktok/mobile/search/photos?keyword=lofi&count=10
Example response (truncated):
{
"code": 200,
"msg": "OK",
"data": {
"search_item_list": [
{
"aweme_info": {
"aweme_id": "7516855864041540878",
"desc": "#fyp #foryoupage #quran #lofi",
"image_post_info": {
"images": [
{"display_image": {"url_list": ["https://p16-sign.example/...jpeg"]}}
]
}
},
"search_aweme_info": {"has_creation_intention": false}
}
],
"aweme_list": [],
"cursor": 10,
"has_more": 1,
"feedback_type": "photo_tab",
"response_cropped": 1,
"status_code": 0
}
}
| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| x-api-key (header) | string | Yes | API key required | ||
| keyword | string | Yes | Search keyword | lofi | |
| count | integer | No | Rows per page, bounded at 50 (default 10) | 10 | |
| offset | integer | No | Row offset forwarded to the app route (first page only in guest mode) | 0 | |
| sort_type | integer | No | Sort: 0 relevance, 1 like count, 3 date posted | 0 | |
| publish_time | integer | No | Recency window in days: 0 all, 1, 7, 30, 90, 180 | 0 | |
| search_source | string | No | Forwarded search source (default tab_search) | tab_search |
curl -X GET "https://api.crawlora.net/api/v1/tiktok/mobile/search/photos?keyword=lofi&count=10" \ -H "x-api-key: $CRAWLORA_API_KEY"
{
"code": 200,
"msg": "OK",
"data": {
"search_item_list": [
{
"aweme_info": {
"aweme_id": "7516855864041540878",
"desc": "#fyp #foryoupage #quran #lofi",
"image_post_info": {
"images": [
{
"display_image": {
"url_list": [
"https://p16-sign.example/...jpeg"
]
}
}
]
}
},
"search_aweme_info": {
"has_creation_intention": false
}
}
],
"aweme_list": [],
"cursor": 10,
"has_more": 1,
"feedback_type": "photo_tab",
"response_cropped": 1,
"status_code": 0
}
}Request schema
No body schema
Response schema
#/definitions/tiktokmobile.searchPhotosResponseDoc
| Field | Type | Required | Enum | Bounds | Example | Description |
|---|---|---|---|---|---|---|
| aweme_list | array | No | ||||
| backtrace | unknown | No | ||||
| cursor | integer | No | 10 | |||
| extra | unknown | No | ||||
| feedback_type | string | No | photo_tab | |||
| has_more | integer | No | 1 | |||
| log_pb | unknown | No | ||||
| response_cropped | integer | No | 1 | |||
| search_item_list | array | No | ||||
| status_code | integer | No | 0 | |||
| status_msg | string | No |
| Status | Description | Schema |
|---|---|---|
| 400 | Invalid input | #/definitions/app.Response |
| 429 | Rate limit exceeded | #/definitions/app.Response |
| 500 | Internal server error | #/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/search/photos?keyword=lofi&count=10" \
-H "x-api-key: $CRAWLORA_API_KEY"