openapi: 3.0.3
info:
  title: TikTok Public Metadata API
  version: 1.2.6
  description: |
    Self-hosted read-only access to public TikTok metadata. The service does not
    use TikTok's official API or a paid third-party data API. Video and profile
    metadata are read from public web pages. Music metadata is extracted from a
    caller-supplied public source video without starting Chromium. Comment,
    comment-reply, hashtag, and music-list requests use direct HTTP first and
    start the on-demand browser only if TikTok rejects that lightweight path;
    endpoints that require current web signatures use the same bounded browser
    fallback. List endpoints return
    tamper-resistant opaque cursors and never return more than the requested
    count. Add `raw=true` to upstream-backed GET endpoints (or set it in the
    batch JSON body) to include native upstream JSON; the pure URL resolver has
    no native payload. Raw responses bypass the server cache and have a two-megabyte
    serialized response budget to keep memory and bandwidth bounded.

    TikTok can change its public web responses or apply account, cookie, or IP
    restrictions at any time. In that case, the API returns a structured 503
    response instead of fabricated or partial data.
servers:
  - url: https://tiktok-api.comfyui8g.cn
    description: Production endpoint behind Cloudflare
tags:
  - name: Service
    description: Service discovery and readiness endpoints.
  - name: Videos
    description: Public video metadata, creator video batches, and comments.
  - name: Users
    description: Public TikTok profile metadata.
  - name: Music
    description: Public sound metadata and videos using a TikTok sound.
  - name: Discovery
    description: Public hashtag and keyword discovery results.

paths:
  /:
    get:
      tags: [Service]
      operationId: getServiceLandingPage
      summary: Open the service landing page
      description: Returns a small HTML page confirming that the public API service is running.
      responses:
        '200':
          description: HTML service landing page.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            text/html:
              schema:
                type: string
              example: '<!doctype html><html lang="en"><head><meta charset="utf-8"><title>TikTok API</title></head><body><h1>TikTok Public Metadata API</h1><p>Service is running.</p></body></html>'

  /health:
    get:
      tags: [Service]
      operationId: getHealth
      summary: Check service health
      description: Lightweight readiness check that does not start Chromium or contact TikTok.
      responses:
        '200':
          description: The API process is ready to accept requests.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
              example:
                request_id: 52c3156e-69de-442a-a816-b10f6411c80c
                data:
                  status: ok
                count: 1

  /v1/videos/info:
    get:
      tags: [Videos]
      operationId: getVideoInfo
      summary: Get public video metadata
      description: |
        Reads one public TikTok video page and returns normalized video, creator,
        engagement, and music metadata. Results are cached for five minutes by
        default. Private, removed, region-blocked, or unavailable videos can
        return 404 or 503.
      parameters:
        - $ref: '#/components/parameters/VideoUrl'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: Normalized metadata for the requested public video.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoInfoResponse'
              example:
                request_id: c6142678-dba5-457d-ae7f-653727636232
                data: &videoExample
                  id: '7658435999478992148'
                  url: https://www.tiktok.com/@sophiegamergirl/video/7658435999478992148
                  share_url: https://www.tiktok.com/@sophiegamergirl/video/7658435999478992148
                  description: Public TikTok video
                  created_at: '2026-07-18T12:00:00.000Z'
                  region: US
                  language: en
                  is_ad: false
                  is_private: false
                  is_pinned: false
                  ai_generated: false
                  ai_label_visible: true
                  ai_description: null
                  duet_enabled: true
                  stitch_enabled: true
                  share_enabled: true
                  is_reviewing: false
                  is_translatable: true
                  is_original: false
                  is_official: false
                  takedown_status: 0
                  comment_status: 0
                  duration: 15
                  width: 1080
                  height: 1920
                  cover_url: https://p16-sign.tiktokcdn-us.com/example.jpeg
                  play_url: https://v16-webapp-prime.us.tiktok.com/video/example
                  download_url: null
                  view_count: 1000
                  like_count: 100
                  comment_count: 10
                  share_count: 5
                  save_count: 25
                  statistics:
                    view_count: 1000
                    like_count: 100
                    comment_count: 10
                    share_count: 5
                    save_count: 25
                  video:
                    duration: 15
                    width: 1080
                    height: 1920
                    size_bytes: 8942643
                    bitrate: 476939
                    codec_type: h264
                    definition: 540p
                    encoded_type: normal
                    quality: lowest
                    vq_score: 69.51
                    video_id: native-video-id
                    ratio: 720p
                    format: mp4
                    cover_url: https://p16-sign.tiktokcdn-us.com/example.jpeg
                    origin_cover_url: https://p16-sign.tiktokcdn-us.com/example-origin.jpeg
                    dynamic_cover_url: https://p16-sign.tiktokcdn-us.com/example-dynamic.webp
                    play_url: https://v16-webapp-prime.us.tiktok.com/video/example
                    download_url: null
                    volume:
                      loudness: -14.3
                      peak: 0.58884
                    bitrate_variants:
                      - bitrate: 544129
                        fps: 30
                        codec_type: h265_hvc1
                        format: mp4
                        gear_name: adapt_lower_720_1
                        quality_type: 14
                        play_url: https://v16-webapp-prime.us.tiktok.com/video/example-720
                    subtitles:
                      - language_code: en
                        language_id: '1'
                        format: webvtt
                        url: https://v16-webapp-prime.us.tiktok.com/subtitle/example.vtt
                  author:
                    id: '7300000000000000000'
                    sec_uid: MS4wLjABAAAA-example
                    unique_id: sophiegamergirl
                    nickname: Sophie
                    avatar_url: https://p16-sign.tiktokcdn-us.com/avatar.jpeg
                    signature: Public TikTok creator
                    verified: false
                    private_account: false
                    follower_count: 1000
                    following_count: 100
                    heart_count: 10000
                    video_count: 50
                  music:
                    id: '7500000000000000000'
                    title: original sound
                    author: Sophie
                    duration: 15
                    original: true
                    play_url: null
                    cover_url: https://p16-sign.tiktokcdn-us.com/music.jpeg
                  hashtags:
                    - id: '7000000000000000000'
                      name: bleachcosplay
                      description: Public hashtag
                      cover_url: null
                count: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/videos:
    get:
      tags: [Videos]
      operationId: listUserVideos
      summary: List a creator's public videos
      description: |
        Returns normalized public videos from a creator profile. The response
        `count` always equals `data.length` and never exceeds the requested
        `count`. When `has_more` is true, pass the returned opaque `cursor`
        unchanged to retrieve the next page without skipping items. The server
        can merge up to 10 consecutive native TikTok windows to fill `count`,
        while requesting them sequentially so only one browser page is active.
        It uses short, byte-bounded caching and an on-demand browser only when
        a current TikTok web signature is required.
      parameters:
        - $ref: '#/components/parameters/Username'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Count'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: One normalized creator-video page and an opaque next cursor when more results are available.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoListResponse'
              example:
                request_id: e044c2bb-2932-41eb-838f-e609f90b73e1
                data:
                  - *videoExample
                count: 1
                cursor: WzEsIjE3NzgxODY4NzY4NzAiLDNd.example-signature
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/users/info:
    get:
      tags: [Users]
      operationId: getUserInfo
      summary: Get public profile metadata
      description: |
        Reads a public TikTok profile page without starting Chromium and returns
        normalized identity, account status, and audience counters. Results are
        cached for five minutes by default.
      parameters:
        - $ref: '#/components/parameters/Username'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: Normalized public profile metadata.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserInfoResponse'
              example:
                request_id: 172cf8fd-1d49-46c9-aed7-ef488705044c
                data:
                  id: '7300000000000000000'
                  unique_id: sophiegamergirl
                  nickname: Sophie
                  signature: Public TikTok creator
                  avatar_url: https://p16-sign.tiktokcdn-us.com/avatar.jpeg
                  verified: false
                  private_account: false
                  comment_setting: 0
                  download_setting: 3
                  duet_setting: 0
                  stitch_setting: 0
                  following_visibility: 2
                  open_favorite: false
                  is_organization: false
                  is_embed_banned: false
                  profile_embed_permission: 1
                  is_seller: false
                  is_commerce_user: true
                  profile_tabs:
                    music: true
                    playlists: true
                    questions: false
                  follower_count: 1000
                  following_count: 100
                  heart_count: 10000
                  video_count: 50
                count: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/music/info:
    get:
      tags: [Music]
      operationId: getMusicInfo
      summary: Get public music or sound metadata
      description: |
        Extracts the native sound attached to one public TikTok video. Supply a
        full video URL that uses the sound you want to inspect. This endpoint
        reads the video HTML directly and does not start Chromium. It returns
        normalized identity, author, duration, playback, cover, copyright, and
        commerce fields. Results are cached for five minutes by default.
      parameters:
        - $ref: '#/components/parameters/VideoUrl'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: Normalized public music metadata extracted from the source video.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MusicInfoResponse'
              example:
                request_id: 98d5cd35-ce41-4689-b490-7035ae822271
                data:
                  id: '7658436057389648660'
                  title: sonido original
                  author: sophiegamergirl
                  duration: 9
                  original: true
                  private: false
                  collected: false
                  copyrighted: false
                  commerce: true
                  unlimited: false
                  play_url: https://v16m.tiktokcdn-us.com/audio.mp3
                  cover_urls:
                    large: https://p16-common-sign.tiktokcdn-us.com/music-large.jpeg
                    medium: https://p16-common-sign.tiktokcdn-us.com/music-medium.jpeg
                    thumb: https://p16-common-sign.tiktokcdn-us.com/music-thumb.jpeg
                  precise_duration:
                    total: 9.822
                    shoot: 9.822
                    audition: 9.822
                    video: 9.822
                  shoot_duration: 9
                  url: https://www.tiktok.com/music/sonido-original-7658436057389648660
                  source_video_url: https://www.tiktok.com/@sophiegamergirl/video/7658435999478992148
                count: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/music/videos:
    get:
      tags: [Music]
      operationId: listMusicVideos
      summary: List public videos using a sound
      description: |
        Returns a count-bounded normalized page of public videos using the
        requested full TikTok music URL. The server parses its numeric music ID
        and requests TikTok's lightweight
        native item-list JSON first. Only when TikTok blocks or changes that
        response does it use the existing serialized, on-demand browser flow.
        The API can merge up to 10 native windows and never returns more than
        `count`; response `count` always equals `data.length`. When `has_more`
        is true, pass the returned opaque `cursor` unchanged.
      parameters:
        - $ref: '#/components/parameters/MusicUrl'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Count'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: One normalized music-video page and an opaque next cursor when more results are available.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoListResponse'
              example:
                request_id: 292e7646-4727-4abb-8e3f-c81310ee038f
                data:
                  - *videoExample
                count: 1
                cursor: WzEsIjMwIiwxXQ.example-signature
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/videos/comments:
    get:
      tags: [Videos]
      operationId: listVideoComments
      summary: Get public video comments
      description: |
        Requests TikTok's native comment JSON over direct server HTTP first,
        without starting Chromium. If TikTok rejects or changes that lightweight
        response, the endpoint automatically retries through the existing
        on-demand browser page context. TikTok can filter comments inside a
        native window, so this endpoint reads subsequent windows with only the
        remaining count and merges them without skipping comments. At most 10
        native windows are read per request to bound latency and server work.
        Identical pages are cached briefly to avoid duplicate upstream work.
      parameters:
        - $ref: '#/components/parameters/VideoUrl'
        - $ref: '#/components/parameters/CommentCursor'
        - $ref: '#/components/parameters/CommentCount'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: A count-bounded normalized page of public comments.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommentListResponse'
              example:
                request_id: 071a2ec6-c61b-47b6-9d0e-0ccabe0337d2
                data:
                  - id: '7520000000000000000'
                    text: Nice video!
                    parent_comment_id: null
                    video_id: '7658435999478992148'
                    reply_id: '0'
                    reply_to_reply_id: '0'
                    thread_id: '7520000000000000000'
                    thread_has_more: false
                    create_time: '2026-07-20T08:30:00.000Z'
                    digg_count: 2
                    reply_count: 0
                    is_author_liked: false
                    user_liked: false
                    is_translatable: true
                    collect_status: 0
                    status: 1
                    sort_tags: null
                    images: []
                    share:
                      url: https://www.tiktok.com/share/comment/7520000000000000000
                      title: Nice video!
                      description: Public TikTok comment
                    author:
                      id: '7200000000000000000'
                      unique_id: publicviewer
                      nickname: Viewer
                      avatar_url: https://p16-sign.tiktokcdn-us.com/comment-avatar.jpeg
                count: 1
                cursor: 20
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/videos/comments/replies:
    get:
      tags: [Videos]
      operationId: listVideoCommentReplies
      summary: Get replies to a public video comment
      description: |
        Returns a count-bounded normalized page of replies for one public
        comment. The server requests TikTok's native reply JSON directly over
        HTTP first and starts the bounded on-demand browser only if that response
        is rejected or structurally invalid. Underfilled native windows are read
        sequentially, deduplicated, and merged up to 10 windows so response
        `count` always equals `data.length` and never exceeds the requested
        `count`. Native `total` is available only through `raw=true` because
        TikTok can report a value that differs from the current visible window.
      parameters:
        - $ref: '#/components/parameters/VideoUrl'
        - $ref: '#/components/parameters/CommentId'
        - $ref: '#/components/parameters/CommentCursor'
        - $ref: '#/components/parameters/CommentCount'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: A count-bounded normalized page of public comment replies.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CommentListResponse'
              example:
                request_id: 5b9771c5-45ab-4c7c-a6d0-dd50af881a0f
                data:
                  - id: '7664246000000000001'
                    text: Thank you!
                    parent_comment_id: '7664243548862563102'
                    video_id: '7658435999478992148'
                    reply_id: '7664243548862563102'
                    reply_to_reply_id: '7664243548862563102'
                    thread_id: '7664243548862563102'
                    thread_has_more: false
                    create_time: '2026-07-20T09:00:00.000Z'
                    digg_count: 1
                    reply_count: 0
                    language: en
                    pinned: false
                    is_author_liked: false
                    user_liked: false
                    is_translatable: true
                    collect_status: 0
                    status: 1
                    sort_tags: null
                    images: []
                    share:
                      url: null
                      title: null
                      description: null
                    author:
                      id: '7200000000000000001'
                      unique_id: publicviewer
                      nickname: Viewer
                      avatar_url: https://p16-sign.tiktokcdn-us.com/comment-avatar.jpeg
                count: 1
                cursor: 1
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/hashtag/info:
    get:
      tags: [Discovery]
      operationId: getHashtagInfo
      summary: Get public hashtag metadata
      description: |
        Returns TikTok's public hashtag identity, commerce marker, artwork,
        aggregate video and view counts, announcement, and share metadata.
        TikTok's challenge-detail JSON is requested directly over HTTP first;
        the bounded on-demand browser is used only if the lightweight response
        is rejected or invalid. Normalized results are cached for five minutes
        by default, while `raw=true` bypasses the raw-payload cache.
      parameters:
        - $ref: '#/components/parameters/Hashtag'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: Normalized metadata for the requested public hashtag.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HashtagInfoResponse'
              example:
                request_id: 45d3ffdd-4c8b-4872-9d5b-0ce77ce230e5
                data:
                  id: '5054'
                  title: dance
                  description: ''
                  canonical_url: https://www.tiktok.com/tag/dance
                  is_commerce: false
                  profile_images:
                    large: null
                    medium: null
                    thumb: null
                  cover_images:
                    large: null
                    medium: null
                    thumb: null
                  statistics:
                    video_count: 150199497
                    view_count: 1782652749764
                  announcement:
                    title: ''
                    body: ''
                  share:
                    title: '#dance on TikTok'
                    description: Watch awesome short videos created with trending hashtag #dance
                count: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/hashtag/videos:
    get:
      tags: [Discovery]
      operationId: listHashtagVideos
      summary: List public videos for a hashtag
      description: |
        Returns a count-bounded normalized page from TikTok's public hashtag
        results. The API can merge up to 10 short native windows to fill the
        requested count. When `has_more` is true, pass the returned opaque
        `cursor` unchanged to continue without overlap.
      parameters:
        - $ref: '#/components/parameters/Hashtag'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Count'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: One normalized hashtag-video page and an opaque next cursor when more results are available.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoListResponse'
              example:
                request_id: 13a810af-af86-46e2-96d6-da40d6947986
                data:
                  - *videoExample
                count: 1
                cursor: WzEsIjMwIiwzXQ.example-signature
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/search/videos:
    get:
      tags: [Discovery]
      operationId: searchVideos
      summary: Search public TikTok videos
      description: |
        Returns a count-bounded normalized page from TikTok's public video
        search. The API can merge up to 10 short native windows to fill the
        requested count. When `has_more` is true, pass the returned opaque
        `cursor` unchanged. Search pagination scrolls TikTok's internal results
        container so TikTok generates a signature bound to each native offset.
      parameters:
        - $ref: '#/components/parameters/SearchKeyword'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Count'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: One normalized video-search page and an opaque next cursor when more results are available.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
            X-Cache:
              $ref: '#/components/headers/CacheStatus'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoListResponse'
              example:
                request_id: f2d87f41-642a-49dd-89fa-b9082bf6ed24
                data:
                  - *videoExample
                count: 1
                cursor: WzEsIjEyIiwzXQ.example-signature
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/videos/batch:
    post:
      tags: [Videos]
      operationId: getVideosBatch
      summary: Get metadata for up to 20 videos
      description: |
        Reads 1 to 20 canonical public TikTok video URLs sequentially and
        preserves input order. Sequential collection keeps peak memory and
        upstream concurrency bounded. The request fails as a unit if any URL is
        invalid or unavailable. With `raw=true`, the `raw` array is aligned
        with `data` and the input `urls`.
      requestBody:
        required: true
        description: Canonical video URLs and whether native upstream video objects should be included.
        content:
          application/json:
            schema:
              type: object
              required: [urls]
              additionalProperties: false
              properties:
                urls:
                  type: array
                  minItems: 1
                  maxItems: 20
                  description: One to 20 full canonical TikTok video URLs in the desired response order.
                  items:
                    type: string
                    format: uri
                  example:
                    - https://www.tiktok.com/@sophiegamergirl/video/7658435999478992148
                    - https://www.tiktok.com/@tiktok/video/7106594312292453675
                raw:
                  type: boolean
                  default: false
                  description: Include each video's native TikTok object in an order-aligned top-level raw array.
                  example: false
            example:
              urls:
                - https://www.tiktok.com/@sophiegamergirl/video/7658435999478992148
                - https://www.tiktok.com/@tiktok/video/7106594312292453675
              raw: false
      responses:
        '200':
          description: Ordered metadata for every requested video.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoBatchResponse'
              example:
                request_id: 0a54ff92-95c4-4643-b02f-16f429ca15db
                data:
                  - *videoExample
                count: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/search/users:
    get:
      tags: [Users, Discovery]
      operationId: searchUsers
      summary: Search public TikTok users
      description: |
        Returns normalized public profiles from TikTok's current dedicated web
        user-search response. If that request is suppressed for the active web
        session, the endpoint falls back to real author objects attached to
        TikTok's public video-search results; fallback rows can contain fewer
        profile fields. Up to 10 native windows may be merged to honor `count`;
        continue with the returned opaque cursor.
      parameters:
        - $ref: '#/components/parameters/SearchKeyword'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Count'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: One count-bounded page of public user-search results.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserListResponse'
              example:
                request_id: 69ea0867-12ed-4ac3-abfb-15b07cbeb7aa
                data:
                  - id: '107955'
                    sec_uid: MS4wLjABAAAA
                    unique_id: tiktok
                    nickname: TikTok
                    signature: Make Your Day
                    avatar_url: https://p16-sign.tiktokcdn-us.com/example.jpeg
                    verified: true
                    private_account: false
                    follower_count: 90000000
                    following_count: 1
                    heart_count: 300000000
                    video_count: 1200
                count: 1
                cursor: WzEsIjEyIiwzXQ.example-signature
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/search/hashtags:
    get:
      tags: [Discovery]
      operationId: searchHashtags
      summary: Discover hashtags in public video search
      description: |
        Extracts and deduplicates real native hashtag references attached to
        videos in TikTok's current public web search results. This is an honest
        web-search projection, not the broader mobile-app hashtag index.
      parameters:
        - $ref: '#/components/parameters/SearchKeyword'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Count'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: One count-bounded page of native hashtag references.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HashtagListResponse'
              example:
                request_id: 59267003-8b67-421b-9086-8bc49af40975
                data:
                  - id: '5054'
                    title: dance
                    description: Dance videos
                    canonical_url: https://www.tiktok.com/tag/dance
                    is_commerce: false
                    profile_images:
                      large: null
                      medium: null
                      thumb: https://p16-sign.tiktokcdn-us.com/example.jpeg
                count: 1
                cursor: WzEsIjEyIiwzXQ.example-signature
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/search/music:
    get:
      tags: [Music, Discovery]
      operationId: searchMusic
      summary: Discover sounds in public video search
      description: |
        Extracts and deduplicates native sound objects attached to videos in
        TikTok's current public web search results. Each normalized sound
        includes the source video from which TikTok returned it.
      parameters:
        - $ref: '#/components/parameters/SearchKeyword'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Count'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: One count-bounded page of native sound references.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MusicListResponse'
              example:
                request_id: d19993bd-9a23-4e4a-855e-bad6bc15f898
                data:
                  - id: '7510207963801373458'
                    title: original sound
                    author: TikTok
                    duration: 30
                    original: true
                    url: https://www.tiktok.com/music/original-sound-7510207963801373458
                    source_video_url: https://www.tiktok.com/@tiktok/video/7106594312292453675
                count: 1
                cursor: WzEsIjEyIiwzXQ.example-signature
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/trending/videos:
    get:
      tags: [Videos, Discovery]
      operationId: getTrendingVideos
      summary: List public explore videos
      description: |
        Returns TikTok's current public web Explore feed. Lightweight direct
        HTTP is attempted first; a single bounded on-demand browser page is
        used only when TikTok requires a browser-generated signature.
      parameters:
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Count'
        - $ref: '#/components/parameters/Raw'
      responses:
        '200':
          description: One count-bounded page from TikTok's public Explore feed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoListResponse'
              example:
                request_id: 8e2c7235-18c7-43d2-915f-c32438c562e4
                data:
                  - *videoExample
                count: 1
                cursor: WzEsIjgiLDNd.example-signature
                has_more: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

  /v1/resolve:
    get:
      tags: [Discovery]
      operationId: resolveTikTokUrl
      summary: Resolve and classify a TikTok URL
      description: |
        Resolves TikTok short links with bounded server-side HTTP redirects and
        classifies canonical video, user, sound, or hashtag URLs. Redirects are
        never followed outside TikTok and Chromium is never started.
      parameters:
        - $ref: '#/components/parameters/ResolveUrl'
      responses:
        '200':
          description: Canonical URL and resource-specific identifiers.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResolveResponse'
              example:
                request_id: 2bfb276c-a70e-43e2-8924-5c9b6229fc3f
                data:
                  type: video
                  input_url: https://vm.tiktok.com/ZMexample/
                  canonical_url: https://www.tiktok.com/@tiktok/video/7106594312292453675
                  video_id: '7106594312292453675'
                  username: tiktok
                count: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '413':
          $ref: '#/components/responses/RawResponseTooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ProviderUnavailable'

components:
  parameters:
    VideoUrl:
      name: url
      in: query
      required: true
      description: Full canonical HTTPS TikTok video URL containing `/video/{numeric-id}`. Short redirect URLs are not accepted.
      example: https://www.tiktok.com/@sophiegamergirl/video/7658435999478992148
      schema:
        type: string
        format: uri
    Username:
      name: username
      in: query
      required: true
      description: TikTok username, with or without the leading `@`. It may contain letters, numbers, periods, and underscores.
      example: sophiegamergirl
      schema:
        type: string
        minLength: 1
        maxLength: 24
        pattern: '^@?[A-Za-z0-9._]{1,24}$'
    Hashtag:
      name: tag
      in: query
      required: true
      description: Hashtag text, with or without the leading `#`. Spaces and punctuation other than underscores are not accepted.
      example: bleachcosplay
      schema:
        type: string
        minLength: 1
        maxLength: 101
    SearchKeyword:
      name: keyword
      in: query
      required: true
      description: Search phrase after trimming surrounding whitespace; control characters are rejected.
      example: bleach cosplay trend
      schema:
        type: string
        minLength: 1
        maxLength: 100
    MusicUrl:
      name: url
      in: query
      required: true
      description: Full canonical HTTPS TikTok music URL ending in the numeric music ID. Use `data.url` returned by the music-info endpoint so the browser fallback receives TikTok's exact title slug.
      example: https://www.tiktok.com/music/sonido-original-7658436057389648660
      schema:
        type: string
        format: uri
    Cursor:
      name: cursor
      in: query
      required: false
      description: Use `0` or omit for the first page. For the next page, copy the opaque cursor returned by the preceding response without decoding or modifying it. Cursors are signed and bound to the endpoint and search/profile input.
      example: '0'
      schema:
        type: string
        maxLength: 512
        default: '0'
    Count:
      name: count
      in: query
      required: false
      description: Maximum number of normalized items to return, from 1 to 50. The API can merge up to 10 consecutive native TikTok windows to fill this value. The response `count` always equals `data.length` and is never greater than this value. A final, unknown, or safety-capped page can contain fewer items.
      example: 3
      schema:
        type: integer
        minimum: 1
        maximum: 50
        default: 20
    Raw:
      name: raw
      in: query
      required: false
      description: When `true`, includes TikTok's native upstream JSON in the top-level `raw` field. If the API reads multiple native windows, every payload is returned in order as `raw.pages[]`. Raw payloads can be hundreds of kilobytes and are never cached; only their small normalized window is allowed to seed the bounded short-term cache so the returned cursor remains reliable. The serialized native payload budget is 2 MiB; larger results return 413 and should be retried without `raw=true`. Request raw only when normalized fields are insufficient.
      example: false
      schema:
        type: boolean
        default: false
    ResolveUrl:
      name: url
      in: query
      required: true
      description: Full HTTPS TikTok URL, including supported vm.tiktok.com and vt.tiktok.com short redirect links.
      example: https://vm.tiktok.com/ZMexample/
      schema:
        type: string
        format: uri
    CommentId:
      name: comment_id
      in: query
      required: true
      description: Numeric identifier of the parent TikTok comment whose public replies should be returned.
      example: '7664243548862563102'
      schema:
        type: string
        minLength: 1
        maxLength: 32
        pattern: '^[1-9][0-9]{0,31}$'
    CommentCursor:
      name: cursor
      in: query
      required: false
      description: TikTok comment cursor returned by the preceding comment response. Use `0` for the first page and pass each returned value unchanged to continue without overlap.
      example: 0
      schema:
        type: integer
        minimum: 0
        maximum: 100000
        default: 0
    CommentCount:
      name: count
      in: query
      required: false
      description: Maximum number of normalized comments to return, from 1 to 50. The API can merge up to 10 consecutive native TikTok windows to fill this value. A final, unknown, or safety-capped page can contain fewer comments; response `count` always equals `data.length`.
      example: 10
      schema:
        type: integer
        minimum: 1
        maximum: 50
        default: 20

  headers:
    RequestId:
      description: Unique request correlation identifier. A caller-supplied `X-Request-Id` may be echoed when valid.
      schema:
        type: string
      example: c6142678-dba5-457d-ae7f-653727636232
    CacheStatus:
      description: "MISS means upstream data was loaded; HIT means a cached or coalesced normalized result was used; BYPASS means raw=true returned uncached native JSON. A BYPASS request may seed only its small normalized window for reliable follow-up pagination."
      schema:
        type: string
        enum: [HIT, MISS, BYPASS]
      example: MISS

  schemas:
    HealthResponse:
      type: object
      required: [request_id, data, count]
      properties:
        request_id:
          type: string
        data:
          type: object
          required: [status]
          properties:
            status:
              type: string
              enum: [ok]
        count:
          type: integer
          enum: [1]
    Author:
      type: object
      required: [id, unique_id, nickname, avatar_url]
      properties:
        id:
          type: string
          nullable: true
        sec_uid:
          type: string
          nullable: true
        unique_id:
          type: string
          nullable: true
        nickname:
          type: string
          nullable: true
        avatar_url:
          type: string
          format: uri
          nullable: true
        signature:
          type: string
          nullable: true
        verified:
          type: boolean
          nullable: true
          description: Whether TikTok explicitly marks the author as verified, including comment verification markers; null only when the selected upstream surface provides no verification signal.
        private_account:
          type: boolean
          nullable: true
          description: Whether TikTok explicitly marks the author as private; null when omitted upstream.
        follower_count:
          type: number
          nullable: true
        following_count:
          type: number
          nullable: true
        heart_count:
          type: number
          nullable: true
        video_count:
          type: number
          nullable: true
    Music:
      type: object
      required: [id, title, author, cover_url]
      properties:
        id:
          type: string
          nullable: true
        title:
          type: string
          nullable: true
        author:
          type: string
          nullable: true
        duration:
          type: number
          nullable: true
        original:
          type: boolean
          nullable: true
          description: Whether TikTok explicitly marks the sound as original; null when omitted upstream.
        play_url:
          type: string
          format: uri
          nullable: true
        cover_url:
          type: string
          format: uri
          nullable: true
    MusicCoverUrls:
      type: object
      required: [large, medium, thumb]
      properties:
        large:
          type: string
          format: uri
          nullable: true
        medium:
          type: string
          format: uri
          nullable: true
        thumb:
          type: string
          format: uri
          nullable: true
    MusicPreciseDuration:
      type: object
      required: [total, shoot, audition, video]
      properties:
        total:
          type: number
          nullable: true
        shoot:
          type: number
          nullable: true
        audition:
          type: number
          nullable: true
        video:
          type: number
          nullable: true
    MusicInfo:
      type: object
      required: [id, title, author, duration, original, private, collected, copyrighted, commerce, unlimited, play_url, cover_urls, precise_duration, shoot_duration, url, source_video_url]
      properties:
        id:
          type: string
        title:
          type: string
          nullable: true
        author:
          type: string
          nullable: true
        duration:
          type: number
          nullable: true
        original:
          type: boolean
          nullable: true
          description: TikTok's native original-sound marker; null only when omitted upstream.
        private:
          type: boolean
          nullable: true
          description: TikTok's native private-sound marker; null only when omitted upstream.
        collected:
          type: boolean
          nullable: true
          description: TikTok's public-web collection marker for the active server context; null when omitted upstream.
        copyrighted:
          type: boolean
          nullable: true
          description: TikTok's native copyright marker; null when omitted upstream.
        commerce:
          type: boolean
          nullable: true
          description: TikTok's native commercial-music marker; null when omitted upstream.
        unlimited:
          type: boolean
          nullable: true
          description: TikTok's native unlimited-music marker; null when omitted upstream.
        play_url:
          type: string
          format: uri
          nullable: true
        cover_urls:
          $ref: '#/components/schemas/MusicCoverUrls'
        precise_duration:
          $ref: '#/components/schemas/MusicPreciseDuration'
        shoot_duration:
          type: number
          nullable: true
        url:
          type: string
          format: uri
        source_video_url:
          type: string
          format: uri
    VideoStatistics:
      type: object
      required: [view_count, like_count, comment_count, share_count, save_count]
      properties:
        view_count:
          type: number
          nullable: true
        like_count:
          type: number
          nullable: true
        comment_count:
          type: number
          nullable: true
        share_count:
          type: number
          nullable: true
        save_count:
          type: number
          nullable: true
    VideoVolume:
      type: object
      description: Loudness metadata supplied by TikTok for the selected media rendition.
      required: [loudness, peak]
      properties:
        loudness:
          type: number
          nullable: true
        peak:
          type: number
          nullable: true
    VideoBitrateVariant:
      type: object
      description: One native TikTok bitrate rendition. Nullable values mean the upstream rendition omitted that attribute.
      required: [bitrate, fps, codec_type, format, gear_name, quality_type, play_url]
      properties:
        bitrate:
          type: number
          nullable: true
        fps:
          type: number
          nullable: true
        codec_type:
          type: string
          nullable: true
        format:
          type: string
          nullable: true
        gear_name:
          type: string
          nullable: true
        quality_type:
          type: number
          nullable: true
        play_url:
          type: string
          format: uri
          nullable: true
    VideoSubtitle:
      type: object
      description: One native subtitle track advertised by TikTok.
      required: [language_code, language_id, format, url]
      properties:
        language_code:
          type: string
          nullable: true
        language_id:
          type: string
          nullable: true
        format:
          type: string
          nullable: true
        url:
          type: string
          format: uri
          nullable: true
    VideoMedia:
      type: object
      required: [duration, width, height, size_bytes, bitrate, codec_type, definition, encoded_type, quality, vq_score, video_id, ratio, format, cover_url, origin_cover_url, dynamic_cover_url, play_url, download_url, volume, bitrate_variants, subtitles]
      properties:
        duration:
          type: number
          nullable: true
        width:
          type: number
          nullable: true
        height:
          type: number
          nullable: true
        size_bytes:
          type: number
          nullable: true
          description: Native media size in bytes, when supplied by TikTok.
        bitrate:
          type: number
          nullable: true
        codec_type:
          type: string
          nullable: true
        definition:
          type: string
          nullable: true
        encoded_type:
          type: string
          nullable: true
        quality:
          type: string
          nullable: true
        vq_score:
          type: number
          nullable: true
          description: TikTok's native video-quality score.
        video_id:
          type: string
          nullable: true
          description: TikTok's media-level video identifier, distinct from the post id when provided.
        ratio:
          type: string
          nullable: true
        format:
          type: string
          nullable: true
        cover_url:
          type: string
          format: uri
          nullable: true
        origin_cover_url:
          type: string
          format: uri
          nullable: true
        dynamic_cover_url:
          type: string
          format: uri
          nullable: true
        play_url:
          type: string
          format: uri
          nullable: true
        download_url:
          type: string
          format: uri
          nullable: true
        volume:
          $ref: '#/components/schemas/VideoVolume'
        bitrate_variants:
          type: array
          items:
            $ref: '#/components/schemas/VideoBitrateVariant'
        subtitles:
          type: array
          items:
            $ref: '#/components/schemas/VideoSubtitle'
    HashtagSummary:
      type: object
      required: [id, name, description, cover_url]
      properties:
        id:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        cover_url:
          type: string
          format: uri
          nullable: true
    VideoInfo:
      type: object
      required: [id, url, share_url, description, created_at, duration, width, height, cover_url, play_url, download_url, view_count, like_count, comment_count, share_count, save_count, statistics, video, author, music, hashtags]
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        share_url:
          type: string
          format: uri
        description:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true
        region:
          type: string
          nullable: true
        language:
          type: string
          nullable: true
        is_ad:
          type: boolean
          nullable: true
          description: Whether TikTok explicitly marks the video as an advertisement; null when omitted upstream.
        is_private:
          type: boolean
          nullable: true
          description: Whether TikTok explicitly marks the video as private; null when omitted upstream.
        is_pinned:
          type: boolean
          nullable: true
          description: Whether TikTok explicitly marks the video as pinned; null when omitted upstream.
        ai_generated:
          type: boolean
          nullable: true
          description: TikTok's native AI-generated-content marker; null when omitted upstream.
        ai_label_visible:
          type: boolean
          nullable: true
          description: Whether TikTok indicates that the AI-content label should be shown; null when omitted upstream.
        ai_description:
          type: string
          nullable: true
          description: TikTok's native AI-content description, when present.
        duet_enabled:
          type: boolean
          nullable: true
        stitch_enabled:
          type: boolean
          nullable: true
        share_enabled:
          type: boolean
          nullable: true
        is_reviewing:
          type: boolean
          nullable: true
        is_translatable:
          type: boolean
          nullable: true
        is_original:
          type: boolean
          nullable: true
        is_official:
          type: boolean
          nullable: true
          description: TikTok's native official-item marker (including its legacy `officalItem` spelling).
        takedown_status:
          type: number
          nullable: true
        comment_status:
          type: number
          nullable: true
        duration:
          type: integer
          nullable: true
        width:
          type: integer
          nullable: true
        height:
          type: integer
          nullable: true
        cover_url:
          type: string
          format: uri
          nullable: true
        play_url:
          type: string
          format: uri
          nullable: true
        download_url:
          type: string
          format: uri
          nullable: true
        view_count:
          type: number
          nullable: true
        like_count:
          type: number
          nullable: true
        comment_count:
          type: number
          nullable: true
        share_count:
          type: number
          nullable: true
        save_count:
          type: number
          nullable: true
        statistics:
          $ref: '#/components/schemas/VideoStatistics'
        video:
          $ref: '#/components/schemas/VideoMedia'
        author:
          $ref: '#/components/schemas/Author'
        music:
          $ref: '#/components/schemas/Music'
        hashtags:
          type: array
          items:
            $ref: '#/components/schemas/HashtagSummary'
    VideoSummary:
      allOf:
        - $ref: '#/components/schemas/VideoInfo'
    ProfileTabs:
      type: object
      description: Profile tabs explicitly exposed by TikTok; each value is null when the upstream profile omits that setting.
      required: [music, playlists, questions]
      properties:
        music:
          type: boolean
          nullable: true
        playlists:
          type: boolean
          nullable: true
        questions:
          type: boolean
          nullable: true
    UserInfo:
      type: object
      required: [id, unique_id, nickname, signature, avatar_url, verified, private_account, follower_count, following_count, heart_count, video_count]
      properties:
        id:
          type: string
        sec_uid:
          type: string
          nullable: true
        unique_id:
          type: string
          nullable: true
        short_id:
          type: string
          nullable: true
        nickname:
          type: string
          nullable: true
        signature:
          type: string
          nullable: true
        avatar_url:
          type: string
          format: uri
          nullable: true
        verified:
          type: boolean
          nullable: true
          description: Whether TikTok explicitly marks the user as verified; null when omitted upstream.
        private_account:
          type: boolean
          nullable: true
          description: Whether TikTok explicitly marks the account as private; null when omitted upstream.
        comment_setting:
          type: number
          nullable: true
          description: TikTok's native profile comment-permission code.
        download_setting:
          type: number
          nullable: true
          description: TikTok's native download-permission code.
        duet_setting:
          type: number
          nullable: true
          description: TikTok's native duet-permission code.
        stitch_setting:
          type: number
          nullable: true
          description: TikTok's native stitch-permission code.
        following_visibility:
          type: number
          nullable: true
          description: TikTok's native following-list visibility code.
        open_favorite:
          type: boolean
          nullable: true
        is_organization:
          type: boolean
          nullable: true
        is_embed_banned:
          type: boolean
          nullable: true
        profile_embed_permission:
          type: number
          nullable: true
        is_seller:
          type: boolean
          nullable: true
        is_commerce_user:
          type: boolean
          nullable: true
        profile_tabs:
          $ref: '#/components/schemas/ProfileTabs'
        follower_count:
          type: number
          nullable: true
        following_count:
          type: number
          nullable: true
        heart_count:
          type: number
          nullable: true
        video_count:
          type: number
          nullable: true
        friend_count:
          type: number
          nullable: true
        digg_count:
          type: number
          nullable: true
        language:
          type: string
          nullable: true
        create_time:
          type: string
          format: date-time
          nullable: true
        bio_url:
          type: string
          format: uri
          nullable: true
        room_id:
          oneOf:
            - type: string
            - type: number
          nullable: true
    CommentImage:
      type: object
      description: Image attached to a public TikTok comment.
      required: [url, width, height]
      properties:
        url:
          type: string
          format: uri
          nullable: true
        width:
          type: number
          nullable: true
        height:
          type: number
          nullable: true
    HashtagImageSet:
      type: object
      required: [large, medium, thumb]
      properties:
        large:
          type: string
          format: uri
          nullable: true
        medium:
          type: string
          format: uri
          nullable: true
        thumb:
          type: string
          format: uri
          nullable: true
    HashtagStatistics:
      type: object
      required: [video_count, view_count]
      properties:
        video_count:
          type: number
          nullable: true
          description: Public video count from TikTok `statsV2`, falling back to legacy `stats`.
        view_count:
          type: number
          nullable: true
          description: Public view count from TikTok `statsV2`, falling back to legacy `stats`.
    HashtagAnnouncement:
      type: object
      required: [title, body]
      properties:
        title:
          type: string
          nullable: true
        body:
          type: string
          nullable: true
    HashtagShare:
      type: object
      required: [title, description]
      properties:
        title:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
    HashtagInfo:
      type: object
      required: [id, title, description, canonical_url, is_commerce, profile_images, cover_images, statistics, announcement, share]
      properties:
        id:
          type: string
          nullable: true
        title:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        canonical_url:
          type: string
          format: uri
        is_commerce:
          type: boolean
          nullable: true
          description: TikTok's native commerce-hashtag marker; false is preserved when explicitly returned.
        profile_images:
          $ref: '#/components/schemas/HashtagImageSet'
        cover_images:
          $ref: '#/components/schemas/HashtagImageSet'
        statistics:
          $ref: '#/components/schemas/HashtagStatistics'
        announcement:
          $ref: '#/components/schemas/HashtagAnnouncement'
        share:
          $ref: '#/components/schemas/HashtagShare'
    CommentShare:
      type: object
      description: TikTok's native share metadata for the comment.
      required: [url, title, description]
      properties:
        url:
          type: string
          format: uri
          nullable: true
        title:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
    Comment:
      type: object
      required: [id, text, create_time, digg_count, reply_count, author]
      properties:
        id:
          type: string
          nullable: true
        text:
          type: string
          nullable: true
        parent_comment_id:
          type: string
          nullable: true
          description: Parent comment requested by the reply endpoint; null for a top-level comment page.
        video_id:
          type: string
          nullable: true
        reply_id:
          type: string
          nullable: true
        reply_to_reply_id:
          type: string
          nullable: true
        thread_id:
          type: string
          nullable: true
          description: Returned as a string to preserve identifiers larger than JavaScript's safe integer range.
        thread_has_more:
          type: boolean
          nullable: true
        create_time:
          type: string
          format: date-time
          nullable: true
        digg_count:
          type: number
        reply_count:
          type: number
        language:
          type: string
          nullable: true
        pinned:
          type: boolean
          nullable: true
          description: Whether TikTok's `is_pinned` or `author_pin` marker identifies the comment as pinned; null only when both markers are omitted upstream.
        is_author_liked:
          type: boolean
          nullable: true
          description: TikTok's native marker indicating that the video author liked the comment.
        user_liked:
          type: boolean
          nullable: true
          description: TikTok's native marker for whether the active public-web context liked the comment.
        is_translatable:
          type: boolean
          nullable: true
        collect_status:
          type: number
          nullable: true
        status:
          type: number
          nullable: true
        sort_tags:
          type: string
          nullable: true
          description: TikTok's native comment sort metadata. Its upstream type can vary.
        images:
          type: array
          items:
            $ref: '#/components/schemas/CommentImage'
        share:
          $ref: '#/components/schemas/CommentShare'
        author:
          $ref: '#/components/schemas/Author'
    VideoInfoResponse:
      type: object
      required: [request_id, data, count]
      properties:
        request_id:
          type: string
        data:
          $ref: '#/components/schemas/VideoInfo'
        count:
          type: integer
          enum: [1]
        raw:
          $ref: '#/components/schemas/RawPayload'
    UserInfoResponse:
      type: object
      required: [request_id, data, count]
      properties:
        request_id:
          type: string
        data:
          $ref: '#/components/schemas/UserInfo'
        count:
          type: integer
          enum: [1]
        raw:
          $ref: '#/components/schemas/RawPayload'
    MusicInfoResponse:
      type: object
      required: [request_id, data, count]
      properties:
        request_id:
          type: string
        data:
          $ref: '#/components/schemas/MusicInfo'
        count:
          type: integer
          enum: [1]
        raw:
          $ref: '#/components/schemas/RawPayload'
    HashtagInfoResponse:
      type: object
      required: [request_id, data, count]
      properties:
        request_id:
          type: string
        data:
          $ref: '#/components/schemas/HashtagInfo'
        count:
          type: integer
          enum: [1]
        raw:
          $ref: '#/components/schemas/RawPayload'
    VideoListResponse:
      type: object
      required: [request_id, data, count, has_more]
      properties:
        request_id:
          type: string
        data:
          type: array
          items:
            $ref: '#/components/schemas/VideoSummary'
        count:
          type: integer
          minimum: 0
          maximum: 50
        cursor:
          type: string
          description: Opaque signed next-page cursor. Present only when `has_more` is true.
        has_more:
          type: boolean
          nullable: true
          description: Whether another usable page is known to exist; null when TikTok omits the native flag.
        raw:
          $ref: '#/components/schemas/RawPayload'
    CommentListResponse:
      type: object
      required: [request_id, data, count, cursor, has_more]
      properties:
        request_id:
          type: string
        data:
          type: array
          items:
            $ref: '#/components/schemas/Comment'
        count:
          type: integer
          minimum: 0
        cursor:
          oneOf:
            - type: integer
            - type: string
          nullable: true
          description: Cursor immediately after the last native TikTok window consumed by this response. Pass it unchanged to request the next page.
        has_more:
          type: boolean
          nullable: true
          description: Whether another page is available; null when TikTok omits the native flag.
        raw:
          $ref: '#/components/schemas/RawPayload'
    VideoBatchResponse:
      type: object
      required: [request_id, data, count]
      properties:
        request_id:
          type: string
        data:
          type: array
          minItems: 1
          maxItems: 20
          description: Normalized videos in the same order as the request URLs.
          items:
            $ref: '#/components/schemas/VideoInfo'
        count:
          type: integer
          minimum: 1
          maximum: 20
          description: Number of videos returned; always equals both data.length and the number of input URLs.
        raw:
          type: array
          minItems: 1
          maxItems: 20
          description: Native video objects aligned with data and included only when raw=true.
          items:
            $ref: '#/components/schemas/RawPayload'
    UserListResponse:
      type: object
      required: [request_id, data, count, has_more]
      properties:
        request_id:
          type: string
        data:
          type: array
          items:
            $ref: '#/components/schemas/UserInfo'
        count:
          type: integer
          minimum: 0
          maximum: 50
        cursor:
          type: string
          description: Opaque signed next-page cursor, present only when has_more is true.
        has_more:
          type: boolean
          nullable: true
        raw:
          $ref: '#/components/schemas/RawPayload'
    HashtagListResponse:
      type: object
      required: [request_id, data, count, has_more]
      properties:
        request_id:
          type: string
        data:
          type: array
          items:
            $ref: '#/components/schemas/HashtagInfo'
        count:
          type: integer
          minimum: 0
          maximum: 50
        cursor:
          type: string
          description: Opaque signed next-page cursor, present only when has_more is true.
        has_more:
          type: boolean
          nullable: true
        raw:
          $ref: '#/components/schemas/RawPayload'
    MusicListResponse:
      type: object
      required: [request_id, data, count, has_more]
      properties:
        request_id:
          type: string
        data:
          type: array
          items:
            $ref: '#/components/schemas/MusicInfo'
        count:
          type: integer
          minimum: 0
          maximum: 50
        cursor:
          type: string
          description: Opaque signed next-page cursor, present only when has_more is true.
        has_more:
          type: boolean
          nullable: true
        raw:
          $ref: '#/components/schemas/RawPayload'
    ResolvedTikTokUrl:
      type: object
      required: [type, input_url, canonical_url]
      properties:
        type:
          type: string
          enum: [video, user, music, hashtag]
        input_url:
          type: string
          format: uri
        canonical_url:
          type: string
          format: uri
        video_id:
          type: string
        username:
          type: string
        music_id:
          type: string
        slug:
          type: string
        tag:
          type: string
      description: Resource-specific properties are present only when applicable; irrelevant fields are omitted rather than returned as null.
    ResolveResponse:
      type: object
      required: [request_id, data, count]
      properties:
        request_id:
          type: string
        data:
          $ref: '#/components/schemas/ResolvedTikTokUrl'
        count:
          type: integer
          enum: [1]
    RawPayload:
      description: Native TikTok upstream JSON, included only when `raw=true`. Its provider-owned shape can change independently from normalized fields.
      oneOf:
        - type: object
          additionalProperties: true
        - type: array
          items: {}
    ErrorResponse:
      type: object
      required: [request_id, data, count]
      properties:
        request_id:
          type: string
        data:
          type: object
          required: [error]
          properties:
            error:
              type: object
              required: [code, message]
              properties:
                code:
                  type: string
                message:
                  type: string
        count:
          type: integer
          enum: [1]

  responses:
    BadRequest:
      description: A required parameter is missing, malformed, out of range, or requests unsupported pagination.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            request_id: 9ca225c6-92a4-411b-9702-f4bc83bcb1db
            data:
              error:
                code: INVALID_REQUEST
                message: count must be an integer between 1 and 50.
            count: 1
    Unauthorized:
      description: The deployment requires a RapidAPI proxy secret and the request did not contain the matching header. RapidAPI injects this header for marketplace calls.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            request_id: 09368621-d83e-45b9-9932-aa096debb59d
            data:
              error:
                code: UNAUTHORIZED_PROXY
                message: Missing or invalid RapidAPI proxy secret.
            count: 1
    NotFound:
      description: The requested public TikTok resource was not found, was removed, is private, or is unavailable in the server region.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            request_id: 28cde50e-926f-4bcf-9497-78946c45413f
            data:
              error:
                code: VIDEO_NOT_FOUND
                message: The TikTok video was not found or is not public.
            count: 1
    RawResponseTooLarge:
      description: The serialized native TikTok payload requested with `raw=true` exceeds the service's two-megabyte response budget. Retry without `raw=true`.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            request_id: 3642044e-56f6-40cc-9aad-e986702a7bb9
            data:
              error:
                code: RAW_RESPONSE_TOO_LARGE
                message: The raw upstream response exceeds the 2097152-byte response limit.
            count: 1
    RateLimited:
      description: The per-client request limit was exceeded. Retry after the current one-minute window.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            request_id: 5e1709f1-777f-4467-a1c2-da380e43b742
            data:
              error:
                code: RATE_LIMITED
                message: Rate limit exceeded. Please retry later.
            count: 1
    ProviderUnavailable:
      description: TikTok did not return a usable public web response, the web session was rejected, or the on-demand browser could not complete the request.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            request_id: 748989d1-2862-422b-9d3d-01a8e7a4ad2a
            data:
              error:
                code: PROVIDER_UNAVAILABLE
                message: TikTok is temporarily unavailable. Please retry later.
            count: 1
