TikTok API endpoint
Translates one video's caption with the app's own translation route and returns the translated caption both as the app's raw markup (`translated_client_text.client_text_v2.markup_text`, where line breaks are `<br>` and hashtags are wrapped in `<h id="N">...</h>`) and as plain text in `text`. `item_id` is required; `target_lang` defaults to `en` and accepts the app's language codes (`en`, `ja`, `pt-BR`, `zh-Hans`, ...). 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 a stub when it is not.
/tiktok/mobile/translation/descriptionCalls the app's own caption-translation route — POST /aweme/v1/translation/description/ — and returns the
translated caption. The route was captured from the running 47.4.3 app over mitmproxy (chained to a US Surge
exit) and then reproduced end-to-end with the device-backed signer, so the request body and the response shape
below are measured, not inferred.
Requires device-backed request signing. The 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 a stub.
Query parameters:
item_id: required. The numeric video id whose caption is translated. A non-numeric id is rejected
before any upstream call.target_lang: optional, default en. A language code in the app's own format (en, ja, pt-BR,
zh-Hans, ...). Forwarded as-is.The app's request is a form POST carrying item_id, target_lang and translation_protocol=2; this endpoint
sends exactly that body.
Response fields:
translated_client_text.client_text_v2.markup_text: the translated caption in the app's markup, where
line breaks are <br> and hashtags are wrapped as <h id="N">#tag</h>. Returned unchanged.translated_client_text.client_text_v2.text_extra: opaque hashtag records (hashtag_id, hashtag_name,
is_commerce, tag_id), kept as any so nothing is lost.translated_client_text.translation_protocol: measured 2.text: convenience projection of markup_text with <br> turned into newlines and the hashtag wrappers
stripped.format, variant, extra, log_pb, perf_metrics, status_code, status_msg: the upstream envelope,
kept verbatim.host, fetched_at, item_id, target_lang: request echo.Measured 2026-10-21 with the device-backed signer:
target_lang=en: HTTP 200, 3,121 B, status_code 0, format 1, a 1,617-character English
markup_text and 4 text_extra hashtags.target_lang=en: status_code 3048002 "same source language and target language"; checkStatus maps a non-zero status_code to a typed upstream error, so this is reported as an
error rather than as an empty translation.Example request:
GET /api/v1/tiktok/mobile/translation/description?item_id=7650052642089225493&target_lang=en
Example response (truncated):
{
"code": 200,
"msg": "OK",
"data": {
"item_id": "7650052642089225493",
"target_lang": "en",
"translated_client_text": {
"client_text_v2": {
"markup_text": "Japan's most popular ramen stall...<br><br>...<h id=\"2\">#東京グルメ</h>",
"text_extra": [
{"hashtag_id": "1611500018038786", "hashtag_name": "東京グルメ", "is_commerce": false, "tag_id": "2"}
]
},
"translation_protocol": 2
},
"text": "Japan's most popular ramen stall...\n\n...#東京グルメ",
"format": 1,
"status_code": 0,
"host": "aggr32-normal.tiktokv.us"
}
}
| Parameter | Type | Required | Default | Description | Example |
|---|---|---|---|---|---|
| x-api-key (header) | string | Yes | API key required | ||
| item_id | string | Yes | Video id to translate | 7650052642089225493 | |
| target_lang | string | No | Target language code (default en) | en |
curl -X GET "https://api.crawlora.net/api/v1/tiktok/mobile/translation/description?item_id=7650052642089225493" \ -H "x-api-key: $CRAWLORA_API_KEY"
{
"code": 200,
"msg": "OK",
"data": {
"item_id": "7650052642089225493",
"target_lang": "en",
"translated_client_text": {
"client_text_v2": {
"markup_text": "Japan's most popular ramen stall...<br><br>...<h id=\"2\">#東京グルメ</h>",
"text_extra": [
{
"hashtag_id": "1611500018038786",
"hashtag_name": "東京グルメ",
"is_commerce": false,
"tag_id": "2"
}
]
},
"translation_protocol": 2
},
"text": "Japan's most popular ramen stall...\n\n...#東京グルメ",
"format": 1,
"status_code": 0,
"host": "aggr32-normal.tiktokv.us"
}
}Request schema
No body schema
Response schema
#/definitions/tiktokmobile.translationDescriptionResponseDoc
| Field | Type | Required | Enum | Bounds | Example | Description |
|---|---|---|---|---|---|---|
| extra | unknown | No | ||||
| fetched_at | string | No | ||||
| format | integer | No | 1 | |||
| host | string | No | aggr32-normal.tiktokv.us | |||
| item_id | string | No | 7650052642089225493 | |||
| log_pb | unknown | No | ||||
| perf_metrics | unknown | No | ||||
| status_code | integer | No | 0 | |||
| status_msg | string | No | ||||
| target_lang | string | No | en | |||
| text | string | No | ||||
| translated_client_text | unknown | No | ||||
| variant | 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/translation/description?item_id=7650052642089225493" \
-H "x-api-key: $CRAWLORA_API_KEY"