crawlora-tiktok-mobile 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +21 -0
- data/README.md +103 -0
- data/lib/crawlora/tiktok-mobile/client.rb +448 -0
- data/lib/crawlora/tiktok-mobile/version.rb +5 -0
- data/lib/crawlora/tiktok-mobile.rb +2 -0
- metadata +52 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5780bf9916c767a09b772e3a065a5fffbfe0c4f5f6327373dcd9fe1faf93f6d3
|
|
4
|
+
data.tar.gz: 8d1df4092b14f72a86fcc4238f8bd22ede07e4f2c24ecab1f78e2ef68d8f2fad
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4ab30d22251d8a664ec30f2b265bc2638d3e1d8c22678d2539853c8164263a148d803ff632f91b8cf128a5973e6bb87ca67fa89d7b7ba10b73dab13939af075f
|
|
7
|
+
data.tar.gz: cfc332c1486593e1469bc41ef6bec748dc79fc313562c57981b8135bd0c2ab66d3ffe238873feb25f92a52ab1c7d4e876c8453527c8d3917cf52b77b499496ab
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Crawlora
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Crawlora TikTok Mobile API client for Ruby (`crawlora-tiktok-mobile`)
|
|
2
|
+
|
|
3
|
+
This Ruby gem calls Crawlora's hosted TikTok Mobile API at `https://api.crawlora.net/api/v1`. It exposes 63 endpoints covering mobile feed, search, video, creator, music, LIVE, and place endpoints; it does not call or scrape TikTok Mobile directly. Requests require your Crawlora API key and use your account's service plan.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
gem "crawlora-tiktok-mobile"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Create an account at [crawlora.net](https://crawlora.net/signup?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=tiktok-mobile-ruby-signup), open the [Crawlora console](https://crawlora.net/app?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=tiktok-mobile-ruby-console) to get an API key, and set `CRAWLORA_API_KEY` before running the client:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
require "json"
|
|
15
|
+
require "crawlora/tiktok-mobile"
|
|
16
|
+
|
|
17
|
+
client = Crawlora::TiktokMobile::Client.new
|
|
18
|
+
result = client.request("tiktok-mobile-search", JSON.parse("{\"keyword\": \"sample\"}"))
|
|
19
|
+
puts result
|
|
20
|
+
client.close
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Use an operation-specific method for normal calls. `request(operation_id, params = {}, response_type: :auto)` is available for every operation. `response_type: :text` returns raw response text. This gem includes 63 API operations.
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
client = Crawlora::TiktokMobile::Client.new(api_key: ENV.fetch("CRAWLORA_API_KEY"), timeout: 30)
|
|
27
|
+
# client.<operation_method>(<endpoint parameters>)
|
|
28
|
+
client.close
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Client options include `api_key`, `base_url`, and `timeout`. Ruby stdlib provides the HTTP and JSON support.
|
|
32
|
+
|
|
33
|
+
## Supported TikTok Mobile API endpoints
|
|
34
|
+
|
|
35
|
+
Browse every route included in this gem. The [endpoint reference](https://github.com/Crawlora-org/crawlora-tiktok-mobile/blob/main/docs/usage.md) includes parameter and response details.
|
|
36
|
+
|
|
37
|
+
| Method | Endpoint | API operation | Description |
|
|
38
|
+
| --- | --- | --- | --- |
|
|
39
|
+
| `GET` | `/tiktok/mobile/anchor/list` | `tiktok-mobile-anchor-list` | Anchor catalogue used by the video composer |
|
|
40
|
+
| `GET` | `/tiktok/mobile/collection/detail` | `tiktok-mobile-collection-detail` | Get TikTok collection detail on the mobile API |
|
|
41
|
+
| `GET` | `/tiktok/mobile/collection/list` | `tiktok-mobile-collection-list` | List TikTok collections on the mobile API |
|
|
42
|
+
| `GET` | `/tiktok/mobile/collection/videos` | `tiktok-mobile-collection-videos` | Get videos in a TikTok collection on the mobile API |
|
|
43
|
+
| `GET` | `/tiktok/mobile/comment/replies` | `tiktok-mobile-comment-replies` | Retrieve replies to a TikTok comment |
|
|
44
|
+
| `GET` | `/tiktok/mobile/comments` | `tiktok-mobile-comments` | Retrieve comments for a TikTok video |
|
|
45
|
+
| `GET` | `/tiktok/mobile/feed` | `tiktok-mobile-feed` | Retrieve the TikTok mobile For You feed |
|
|
46
|
+
| `GET` | `/tiktok/mobile/feed/offline` | `tiktok-mobile-feed-offline` | Fetch the app's cached-pull offline feed (protobuf) decoded without a schema |
|
|
47
|
+
| `GET` | `/tiktok/mobile/feed/post` | `tiktok-mobile-feed-post` | Fetch the app's primary feed call (POST, protobuf) decoded without a schema |
|
|
48
|
+
| `GET` | `/tiktok/mobile/hashtag` | `tiktok-mobile-hashtag` | Retrieve a TikTok hashtag |
|
|
49
|
+
| `GET` | `/tiktok/mobile/hashtag/posts` | `tiktok-mobile-hashtag-posts` | Retrieve videos for a TikTok hashtag |
|
|
50
|
+
| `GET` | `/tiktok/mobile/hashtags` | `tiktok-mobile-hashtags` | Search TikTok hashtags |
|
|
51
|
+
| `GET` | `/tiktok/mobile/live/effects` | `tiktok-mobile-live-effects` | Retrieve TikTok LIVE effects |
|
|
52
|
+
| `GET` | `/tiktok/mobile/live/gifts` | `tiktok-mobile-live-gifts` | Retrieve TikTok LIVE gifts |
|
|
53
|
+
| `GET` | `/tiktok/mobile/live/hashtags` | `tiktok-mobile-live-hashtags` | LIVE category/game reference catalogue (hashtags, live-studio/voice/third-party tags, game tags) |
|
|
54
|
+
| `GET` | `/tiktok/mobile/live/room` | `tiktok-mobile-live-room` | LIVE room detail: status, viewers, owner, stream URLs, cover |
|
|
55
|
+
| `GET` | `/tiktok/mobile/live/room-by-user` | `tiktok-mobile-live-room-by-user` | LIVE room by user: the room a user is currently streaming |
|
|
56
|
+
| `GET` | `/tiktok/mobile/live/search` | `tiktok-mobile-live-search` | Search TikTok LIVE streams |
|
|
57
|
+
| `GET` | `/tiktok/mobile/live/tabs` | `tiktok-mobile-live-tabs` | Fetch the LIVE tab catalogue |
|
|
58
|
+
| `GET` | `/tiktok/mobile/music/beats` | `tiktok-mobile-music-beats` | Retrieve TikTok music beats |
|
|
59
|
+
| `GET` | `/tiktok/mobile/music/chart` | `tiktok-mobile-music-chart` | List a regional TikTok sound chart |
|
|
60
|
+
| `GET` | `/tiktok/mobile/music/choices` | `tiktok-mobile-music-choices` | Recommended sounds for video creation |
|
|
61
|
+
| `GET` | `/tiktok/mobile/music/collection-list` | `tiktok-mobile-music-collection-list` | Music collection list (paged browse) |
|
|
62
|
+
| `GET` | `/tiktok/mobile/music/collections` | `tiktok-mobile-music-collections` | Retrieve TikTok music collections |
|
|
63
|
+
| `GET` | `/tiktok/mobile/music/detail` | `tiktok-mobile-music-detail` | Retrieve one TikTok sound |
|
|
64
|
+
| `GET` | `/tiktok/mobile/music/pick` | `tiktok-mobile-music-pick` | Retrieve TikTok sound chart picks |
|
|
65
|
+
| `GET` | `/tiktok/mobile/music/posts` | `tiktok-mobile-music-posts` | Retrieve videos for a TikTok sound |
|
|
66
|
+
| `GET` | `/tiktok/mobile/music/related` | `tiktok-mobile-music-related` | Retrieve sounds related to a TikTok video |
|
|
67
|
+
| `GET` | `/tiktok/mobile/music/trending` | `tiktok-mobile-music-trending` | Retrieve TikTok trending sounds |
|
|
68
|
+
| `GET` | `/tiktok/mobile/mv/videos` | `tiktok-mobile-mv-videos` | MV channel feed: the app's music-video tab, paged |
|
|
69
|
+
| `GET` | `/tiktok/mobile/place/detail` | `tiktok-mobile-place-detail` | Place (POI) detail from the mobile app's Places surface |
|
|
70
|
+
| `GET` | `/tiktok/mobile/place/info` | `tiktok-mobile-place-info` | Compact place (POI) record: address, coordinates, rating, hours, counters |
|
|
71
|
+
| `GET` | `/tiktok/mobile/place/media` | `tiktok-mobile-place-media` | List a TikTok place's mixed media |
|
|
72
|
+
| `GET` | `/tiktok/mobile/place/photos` | `tiktok-mobile-place-photos` | Place photo album with per-photo attribution |
|
|
73
|
+
| `GET` | `/tiktok/mobile/place/reviews` | `tiktok-mobile-place-reviews` | List a TikTok place's reviews |
|
|
74
|
+
| `GET` | `/tiktok/mobile/place/videos` | `tiktok-mobile-place-videos` | Videos posted at one place (paged) |
|
|
75
|
+
| `GET` | `/tiktok/mobile/playlist/detail` | `tiktok-mobile-playlist-detail` | Retrieve one TikTok playlist |
|
|
76
|
+
| `GET` | `/tiktok/mobile/playlist/list` | `tiktok-mobile-playlist-list` | List a creator's TikTok playlists |
|
|
77
|
+
| `GET` | `/tiktok/mobile/playlist/videos` | `tiktok-mobile-playlist-videos` | List a playlist's videos |
|
|
78
|
+
| `GET` | `/tiktok/mobile/search` | `tiktok-mobile-search` | Search TikTok videos on the mobile API |
|
|
79
|
+
| `GET` | `/tiktok/mobile/search/effects` | `tiktok-mobile-search-effects` | Search TikTok effects/stickers by keyword |
|
|
80
|
+
| `GET` | `/tiktok/mobile/search/hashtags` | `tiktok-mobile-search-hashtags` | Hashtag suggestions for a keyword (hashtag typeahead) |
|
|
81
|
+
| `GET` | `/tiktok/mobile/search/music` | `tiktok-mobile-search-music` | Search TikTok sounds on the mobile API |
|
|
82
|
+
| `GET` | `/tiktok/mobile/search/photos` | `tiktok-mobile-search-photos` | Search the mobile app's photo-post tab |
|
|
83
|
+
| `GET` | `/tiktok/mobile/search/place` | `tiktok-mobile-search-place` | Search TikTok places |
|
|
84
|
+
| `GET` | `/tiktok/mobile/search/pre-info` | `tiktok-mobile-search-pre-info` | Pre-search info token for a search session |
|
|
85
|
+
| `GET` | `/tiktok/mobile/search/stream` | `tiktok-mobile-search-stream` | Run the app's primary (streaming) general search |
|
|
86
|
+
| `GET` | `/tiktok/mobile/search/sug` | `tiktok-mobile-search-sug` | Retrieve TikTok search suggestions |
|
|
87
|
+
| `GET` | `/tiktok/mobile/search/tabs` | `tiktok-mobile-search-tabs` | Search tabs available for a keyword |
|
|
88
|
+
| `GET` | `/tiktok/mobile/search/user` | `tiktok-mobile-search-user` | Search TikTok users on the mobile API |
|
|
89
|
+
| `GET` | `/tiktok/mobile/search/videos` | `tiktok-mobile-search-videos` | Search TikTok videos on the mobile API (video tab) |
|
|
90
|
+
| `GET` | `/tiktok/mobile/translation/description` | `tiktok-mobile-translation-description` | Translate one TikTok video caption |
|
|
91
|
+
| `GET` | `/tiktok/mobile/trending/categories` | `tiktok-mobile-trending-categories` | Retrieve TikTok trending categories |
|
|
92
|
+
| `GET` | `/tiktok/mobile/trending/effects` | `tiktok-mobile-trending-effects` | Retrieve TikTok trending effects |
|
|
93
|
+
| `GET` | `/tiktok/mobile/user/followers` | `tiktok-mobile-user-followers` | Retrieve the followers of a TikTok creator |
|
|
94
|
+
| `GET` | `/tiktok/mobile/user/following` | `tiktok-mobile-user-following` | Retrieve the accounts a TikTok creator follows |
|
|
95
|
+
| `GET` | `/tiktok/mobile/user/original-effects` | `tiktok-mobile-user-original-effects` | List a creator's original TikTok effects |
|
|
96
|
+
| `GET` | `/tiktok/mobile/user/original-music` | `tiktok-mobile-user-original-music` | List a creator's original TikTok sounds |
|
|
97
|
+
| `GET` | `/tiktok/mobile/user/posts` | `tiktok-mobile-user-posts` | Retrieve the videos posted by a TikTok creator |
|
|
98
|
+
| `GET` | `/tiktok/mobile/user/profile` | `tiktok-mobile-user-profile` | Retrieve a TikTok creator profile |
|
|
99
|
+
| `GET` | `/tiktok/mobile/user/recommendations` | `tiktok-mobile-user-recommendations` | Recommended creators on the mobile app's Following page |
|
|
100
|
+
| `GET` | `/tiktok/mobile/video/detail` | `tiktok-mobile-video-detail` | Retrieve one TikTok video |
|
|
101
|
+
| `GET` | `/tiktok/mobile/video/subtitles` | `tiktok-mobile-video-subtitles` | Video captions translated into a target language |
|
|
102
|
+
|
|
103
|
+
See [Crawlora](https://crawlora.net/?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=tiktok-mobile-ruby-homepage), the [API documentation](https://crawlora.net/docs?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=tiktok-mobile-ruby-api-docs), and [the package repository](https://github.com/Crawlora-org/crawlora-tiktok-mobile) for account setup, the API operation reference, and release history.
|
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "net/http"
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
module Crawlora
|
|
6
|
+
module TiktokMobile
|
|
7
|
+
module Errors
|
|
8
|
+
class Error < StandardError
|
|
9
|
+
attr_reader :status, :operation_id, :body
|
|
10
|
+
|
|
11
|
+
def initialize(message, status: nil, operation_id: nil, body: nil)
|
|
12
|
+
super(message)
|
|
13
|
+
@status, @operation_id, @body = status, operation_id, body
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
class ClientError < Error; end
|
|
17
|
+
class ServerError < Error; end
|
|
18
|
+
class NetworkError < Error; end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
OPERATIONS = JSON.parse(<<~'JSON').freeze
|
|
22
|
+
{"tiktok-mobile-anchor-list": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-anchor-list", "method": "GET", "params": [], "path": "/tiktok/mobile/anchor/list", "pathParams": [], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "tiktok-mobile-collection-detail": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-collection-detail", "method": "GET", "params": [{"description": "Collection id (`item_archive_id` from the collection list)", "in": "query", "name": "collection_id", "required": true, "type": "string", "x-example": "7394627756635573022"}], "path": "/tiktok/mobile/collection/detail", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "collection_id", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-collection-list": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-collection-list", "method": "GET", "params": [{"description": "Creator's numeric user id", "in": "query", "name": "user_id", "required": true, "type": "string", "x-example": "107955"}, {"default": 0, "description": "Pagination cursor (server token)", "in": "query", "name": "cursor", "type": "integer"}, {"default": 10, "description": "Collections per page (max 30)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/collection/list", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-collection-videos": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-collection-videos", "method": "GET", "params": [{"description": "Collection id (`item_archive_id` from the collection list)", "in": "query", "name": "collection_id", "required": true, "type": "string", "x-example": "7394627756635573022"}, {"default": 0, "description": "Paging cursor; use the previous page's max_cursor", "in": "query", "name": "cursor", "type": "integer"}, {"default": 10, "description": "Videos per page (max 30; the server serves about 30)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/collection/videos", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "collection_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-comment-replies": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-comment-replies", "method": "GET", "params": [{"description": "Video id the comment belongs to", "in": "query", "name": "aweme_id", "required": true, "type": "string", "x-example": "7680168962218511636"}, {"description": "Comment id to list replies for", "in": "query", "name": "comment_id", "required": true, "type": "string", "x-example": "7680523330713240340"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}, {"default": 20, "description": "Replies per page (max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/comment/replies", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "aweme_id", "required": true, "type": "string"}, {"in": "query", "name": "comment_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-comments": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-comments", "method": "GET", "params": [{"description": "Video id to list comments for", "in": "query", "name": "aweme_id", "required": true, "type": "string", "x-example": "7681236055831416084"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}, {"default": 20, "description": "Comments per page (max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/comments", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "aweme_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-feed": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-feed", "method": "GET", "params": [{"default": 0, "description": "Videos to request (0 = app default, max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/feed", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-feed-offline": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-feed-offline", "method": "GET", "params": [{"description": "Pull type forwarded to the app route (measured 0 and 11; default 11)", "in": "query", "name": "pull_type", "type": "integer", "x-example": 11}, {"description": "Page size, bounded at 50 (upstream default when omitted)", "in": "query", "name": "count", "type": "integer", "x-example": 10}], "path": "/tiktok/mobile/feed/offline", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "pull_type", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-feed-post": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-feed-post", "method": "GET", "params": [], "path": "/tiktok/mobile/feed/post", "pathParams": [], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "tiktok-mobile-hashtag": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-hashtag", "method": "GET", "params": [{"description": "Hashtag name without the leading #", "in": "query", "name": "name", "required": true, "type": "string", "x-example": "readingvlog"}], "path": "/tiktok/mobile/hashtag", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "name", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-hashtag-posts": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-hashtag-posts", "method": "GET", "params": [{"description": "Hashtag name without the leading #", "in": "query", "name": "name", "required": true, "type": "string", "x-example": "readingvlog"}, {"default": "fresh", "description": "Listing: fresh (newest) or popular (hot)", "enum": ["fresh", "popular"], "in": "query", "name": "sort", "type": "string"}, {"default": 20, "description": "Videos per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/hashtag/posts", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "name", "required": true, "type": "string"}, {"enum": ["fresh", "popular"], "in": "query", "name": "sort", "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-hashtags": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-hashtags", "method": "GET", "params": [{"description": "Keyword to search hashtags for", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "reading"}, {"default": 20, "description": "Results per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/hashtags", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-live-effects": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-live-effects", "method": "GET", "params": [], "path": "/tiktok/mobile/live/effects", "pathParams": [], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "tiktok-mobile-live-gifts": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-live-gifts", "method": "GET", "params": [], "path": "/tiktok/mobile/live/gifts", "pathParams": [], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "tiktok-mobile-live-hashtags": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-live-hashtags", "method": "GET", "params": [], "path": "/tiktok/mobile/live/hashtags", "pathParams": [], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "tiktok-mobile-live-room": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-live-room", "method": "GET", "params": [{"description": "Numeric LIVE room id from /api/v1/tiktok/mobile/live/search (data[].lives.author.room_id)", "in": "query", "name": "room_id", "required": true, "type": "string", "x-example": "7699174209096059678"}], "path": "/tiktok/mobile/live/room", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "room_id", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-live-room-by-user": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-live-room-by-user", "method": "GET", "params": [{"description": "Numeric TikTok user id (e.g. the owner id from a profile, video author or live search row)", "in": "query", "name": "user_id", "required": true, "type": "string", "x-example": "6707748031095751686"}], "path": "/tiktok/mobile/live/room-by-user", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-live-search": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-live-search", "method": "GET", "params": [{"description": "Search keyword", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}, {"default": 0, "description": "Results to request (0 = app default, max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/live/search", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-live-tabs": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-live-tabs", "method": "GET", "params": [], "path": "/tiktok/mobile/live/tabs", "pathParams": [], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-beats": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-beats", "method": "GET", "params": [{"default": 0, "description": "Accepted for compatibility; not measured to change the page (max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/music/beats", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-chart": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-chart", "method": "GET", "params": [{"description": "Region of the chart: AE, BR, CA, DE, EG, FR, JP, KR, LB, MX, NG, SA, TR, US or ZA; omit for the default chart (same as US)", "enum": ["AE", "BR", "CA", "DE", "EG", "FR", "JP", "KR", "LB", "MX", "NG", "SA", "TR", "US", "ZA"], "in": "query", "name": "chart_region", "type": "string", "x-example": "US"}, {"default": 0, "description": "Paging token from the previous response", "in": "query", "name": "cursor", "type": "integer"}, {"default": 10, "description": "Rows per page (max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/music/chart", "pathParams": [], "produces": ["application/json"], "queryParams": [{"enum": ["AE", "BR", "CA", "DE", "EG", "FR", "JP", "KR", "LB", "MX", "NG", "SA", "TR", "US", "ZA"], "in": "query", "name": "chart_region", "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-choices": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-choices", "method": "GET", "params": [], "path": "/tiktok/mobile/music/choices", "pathParams": [], "produces": ["application/json"], "queryParams": [], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-collection-list": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-collection-list", "method": "GET", "params": [{"description": "Page size: honoured (1-50 measured, capped by the collection set)", "in": "query", "name": "count", "type": "integer", "x-example": 5}, {"description": "Next-page token from the previous response (0 = first page)", "in": "query", "name": "cursor", "type": "integer", "x-example": 5}], "path": "/tiktok/mobile/music/collection-list", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-collections": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-collections", "method": "GET", "params": [{"default": 0, "description": "Collections to request (0 = app default, max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Accepted for shape compatibility only; not a verified page token", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/music/collections", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-detail": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-detail", "method": "GET", "params": [{"description": "Sound id", "in": "query", "name": "music_id", "required": true, "type": "string", "x-example": "7329791746055080709"}], "path": "/tiktok/mobile/music/detail", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "music_id", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-pick": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-pick", "method": "GET", "params": [{"default": 20, "description": "Sounds per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/music/pick", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-posts": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-posts", "method": "GET", "params": [{"description": "Sound (music) id", "in": "query", "name": "music_id", "required": true, "type": "string", "x-example": "6799585653702019073"}, {"default": "fresh", "description": "Listing: fresh (newest, default) or popular (app canonical music.posts list; requires the device-backed signer)", "enum": ["fresh", "popular"], "in": "query", "name": "sort", "type": "string"}, {"default": 0, "description": "Accepted for compatibility; the server ignores it (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/music/posts", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "music_id", "required": true, "type": "string"}, {"enum": ["fresh", "popular"], "in": "query", "name": "sort", "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-related": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-related", "method": "GET", "params": [{"description": "Video id to find related sounds for", "in": "query", "name": "aweme_id", "required": true, "type": "string", "x-example": "7681236055831416084"}, {"default": 20, "description": "Sounds per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/music/related", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "aweme_id", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-music-trending": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-music-trending", "method": "GET", "params": [{"default": 20, "description": "Sounds per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/music/trending", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-mv-videos": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-mv-videos", "method": "GET", "params": [{"description": "MV channel index (small integer; measured 0-10 populated, 100 empty)", "in": "query", "name": "mv_id", "required": true, "type": "string", "x-example": "1"}, {"description": "Next-page token from the previous response (0 = first page)", "in": "query", "name": "cursor", "type": "integer", "x-example": 0}], "path": "/tiktok/mobile/mv/videos", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "mv_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-place-detail": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-place-detail", "method": "GET", "params": [{"description": "Place id from the place search endpoint", "in": "query", "name": "poi_id", "required": true, "type": "string", "x-example": "42203861086875992"}, {"description": "Review preview size forwarded upstream (default 20, bounded at 50)", "in": "query", "name": "review_count", "type": "integer", "x-example": 20}, {"description": "Review cursor forwarded upstream (default 0)", "in": "query", "name": "review_cursor", "type": "integer", "x-example": 0}, {"description": "Posts page size forwarded upstream (default 9, bounded at 50)", "in": "query", "name": "merged_count", "type": "integer", "x-example": 9}, {"description": "Top-videos cursor forwarded upstream (default 0)", "in": "query", "name": "video_cursor", "type": "integer", "x-example": 0}, {"description": "Forwarded analytics origin (default search_places)", "in": "query", "name": "enter_from", "type": "string", "x-example": "search_places"}], "path": "/tiktok/mobile/place/detail", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "poi_id", "required": true, "type": "string"}, {"in": "query", "name": "review_count", "type": "integer"}, {"in": "query", "name": "review_cursor", "type": "integer"}, {"in": "query", "name": "merged_count", "type": "integer"}, {"in": "query", "name": "video_cursor", "type": "integer"}, {"in": "query", "name": "enter_from", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-place-info": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-place-info", "method": "GET", "params": [{"description": "Place id from the place search endpoint", "in": "query", "name": "poi_id", "required": true, "type": "string", "x-example": "42203861086875992"}], "path": "/tiktok/mobile/place/info", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "poi_id", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-place-media": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-place-media", "method": "GET", "params": [{"description": "Place id from the place search/detail endpoints", "in": "query", "name": "poi_id", "required": true, "type": "string", "x-example": "42203861086875992"}, {"description": "Opaque next_cursor token from the previous response", "in": "query", "name": "cursor", "type": "string"}, {"default": 6, "description": "Rows per page hint (max 30)", "in": "query", "name": "size", "type": "integer"}, {"description": "Paging data version carried with cursor (defaults to v1)", "in": "query", "name": "data_version", "type": "string", "x-example": "v1"}], "path": "/tiktok/mobile/place/media", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "poi_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "string"}, {"in": "query", "name": "size", "type": "integer"}, {"in": "query", "name": "data_version", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-place-photos": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-place-photos", "method": "GET", "params": [{"description": "Place id from the place search endpoint", "in": "query", "name": "poi_id", "required": true, "type": "string", "x-example": "42203861086875992"}], "path": "/tiktok/mobile/place/photos", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "poi_id", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-place-reviews": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-place-reviews", "method": "GET", "params": [{"description": "Place id from the place search/detail endpoints", "in": "query", "name": "poi_id", "required": true, "type": "string", "x-example": "42203861086875992"}, {"default": 0, "description": "Paging token from the previous response", "in": "query", "name": "cursor", "type": "integer"}, {"default": 20, "description": "Reviews per page (max 30)", "in": "query", "name": "count", "type": "integer"}, {"default": 1, "description": "Sort order: 1 = default, 2 = recent", "enum": [1, 2], "in": "query", "name": "sort_type", "type": "integer"}], "path": "/tiktok/mobile/place/reviews", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "poi_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}, {"enum": ["1", "2"], "in": "query", "name": "sort_type", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-place-videos": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-place-videos", "method": "GET", "params": [{"description": "Place id from the place search endpoint", "in": "query", "name": "poi_id", "required": true, "type": "string", "x-example": "42203861086875992"}, {"description": "Page-size hint: 1-50 measured, server caps larger pages (default is the app default)", "in": "query", "name": "count", "type": "integer", "x-example": 10}, {"description": "Next-page token from the previous response (0 = first page)", "in": "query", "name": "cursor", "type": "integer", "x-example": 0}], "path": "/tiktok/mobile/place/videos", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "poi_id", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-playlist-detail": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-playlist-detail", "method": "GET", "params": [{"description": "Playlist id (web playlist id namespace)", "in": "query", "name": "mix_id", "required": true, "type": "string", "x-example": "7681171537575824159"}, {"description": "Playlist owner's numeric user id (one of user_id / sec_user_id is required)", "in": "query", "name": "user_id", "type": "string", "x-example": "107955"}, {"description": "Playlist owner's sec_user_id (one of user_id / sec_user_id is required)", "in": "query", "name": "sec_user_id", "type": "string"}], "path": "/tiktok/mobile/playlist/detail", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "mix_id", "required": true, "type": "string"}, {"in": "query", "name": "user_id", "type": "string"}, {"in": "query", "name": "sec_user_id", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-playlist-list": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-playlist-list", "method": "GET", "params": [{"description": "Creator's numeric user id (one of user_id / sec_user_id is required)", "in": "query", "name": "user_id", "type": "string", "x-example": "107955"}, {"description": "Creator's sec_user_id (one of user_id / sec_user_id is required)", "in": "query", "name": "sec_user_id", "type": "string"}, {"default": 0, "description": "Paging token from the previous response", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/playlist/list", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "type": "string"}, {"in": "query", "name": "sec_user_id", "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-playlist-videos": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-playlist-videos", "method": "GET", "params": [{"description": "Playlist id (web playlist id namespace)", "in": "query", "name": "mix_id", "required": true, "type": "string", "x-example": "7681171537575824159"}, {"default": 0, "description": "Paging token from the previous response", "in": "query", "name": "cursor", "type": "integer"}, {"default": 10, "description": "Videos per page (max 30)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/playlist/videos", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "mix_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search", "method": "GET", "params": [{"description": "Search keyword", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}, {"default": 5, "description": "Results per page (max 20)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Result offset; pass search_id with a non-zero offset", "in": "query", "name": "offset", "type": "integer"}, {"description": "search_id returned by a previous page of the same search", "in": "query", "name": "search_id", "type": "string"}], "path": "/tiktok/mobile/search", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "offset", "type": "integer"}, {"in": "query", "name": "search_id", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-effects": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-effects", "method": "GET", "params": [{"description": "Effect name or text, e.g. green screen", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "green screen"}, {"description": "Accepted for shape parity; the server was measured to ignore it", "in": "query", "name": "count", "type": "integer", "x-example": 20}, {"description": "Next-page token from the previous response (0 = first page)", "in": "query", "name": "cursor", "type": "integer", "x-example": 100}, {"description": "Search-session token from the previous response; required together with cursor", "in": "query", "name": "search_id", "type": "string", "x-example": "20261021173221C8185FF1D4A1062AD97C"}], "path": "/tiktok/mobile/search/effects", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "search_id", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-hashtags": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-hashtags", "method": "GET", "params": [{"description": "Text typed so far; the server also accepts prefixes (e.g. lofi -> lofihiphop)", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}, {"description": "Accepted for shape parity; the server was measured to ignore it (always up to 10 rows)", "in": "query", "name": "count", "type": "integer", "x-example": 10}], "path": "/tiktok/mobile/search/hashtags", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-music": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-music", "method": "GET", "params": [{"description": "Search keyword", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}, {"default": 20, "description": "Sounds per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Result offset", "in": "query", "name": "offset", "type": "integer"}], "path": "/tiktok/mobile/search/music", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "offset", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-photos": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-photos", "method": "GET", "params": [{"description": "Search keyword", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}, {"description": "Rows per page, bounded at 50 (default 10)", "in": "query", "name": "count", "type": "integer", "x-example": 10}, {"description": "Row offset forwarded to the app route (first page only in guest mode)", "in": "query", "name": "offset", "type": "integer", "x-example": 0}, {"description": "Sort: 0 relevance, 1 like count, 3 date posted", "in": "query", "name": "sort_type", "type": "integer", "x-example": 0}, {"description": "Recency window in days: 0 all, 1, 7, 30, 90, 180", "in": "query", "name": "publish_time", "type": "integer", "x-example": 0}, {"description": "Forwarded search source (default tab_search)", "in": "query", "name": "search_source", "type": "string", "x-example": "tab_search"}], "path": "/tiktok/mobile/search/photos", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "offset", "type": "integer"}, {"in": "query", "name": "sort_type", "type": "integer"}, {"in": "query", "name": "publish_time", "type": "integer"}, {"in": "query", "name": "search_source", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-place": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-place", "method": "GET", "params": [{"description": "Place name or category to search for", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "pizza"}, {"default": 10, "description": "Rows to return, 1-30 measured (a larger count yields an empty upstream page and is rejected)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/search/place", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-pre-info": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-pre-info", "method": "GET", "params": [{"description": "Seed keyword (optional; the app also requests this with an empty keyword)", "in": "query", "name": "keyword", "type": "string", "x-example": "lofi"}, {"default": 20, "description": "Suggestion count (max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/search/pre-info", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "type": "string"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-stream": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-stream", "method": "GET", "params": [{"description": "Search keyword", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}, {"default": 10, "description": "Requested page size (sanity bound: 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Row offset", "in": "query", "name": "offset", "type": "integer"}, {"description": "Search source hint forwarded to upstream (measured: video)", "in": "query", "name": "search_source", "type": "string", "x-example": "video"}, {"default": 0, "description": "Sort order: 0 relevance, 1 like count (stable ordering measured), 3 date posted", "enum": [0, 1, 3], "in": "query", "name": "sort_type", "type": "integer"}, {"default": 0, "description": "Recency window in days (0 = unlimited; 7 measured)", "in": "query", "name": "publish_time", "type": "integer"}, {"description": "Raw app filter blob as JSON; takes precedence over sort_type/publish_time", "in": "query", "name": "filter_selected", "type": "string", "x-example": "{\"sort_type\":\"1\",\"publish_time\":\"7\"}"}], "path": "/tiktok/mobile/search/stream", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "offset", "type": "integer"}, {"in": "query", "name": "search_source", "type": "string"}, {"enum": ["0", "1", "3"], "in": "query", "name": "sort_type", "type": "integer"}, {"in": "query", "name": "publish_time", "type": "integer"}, {"in": "query", "name": "filter_selected", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-sug": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-sug", "method": "GET", "params": [{"description": "Seed keyword to expand", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}, {"default": 10, "description": "Suggestions to return (max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/search/sug", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-tabs": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-tabs", "method": "GET", "params": [{"description": "Search keyword", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}], "path": "/tiktok/mobile/search/tabs", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-user": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-user", "method": "GET", "params": [{"description": "Search keyword (a username or display name)", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "mrbeast"}, {"default": 20, "description": "Users per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/search/user", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-search-videos": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-search-videos", "method": "GET", "params": [{"description": "Search keyword", "in": "query", "name": "keyword", "required": true, "type": "string", "x-example": "lofi"}, {"default": 10, "description": "Rows per page (sanity bound: 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Row offset; pass search_id with a non-zero offset", "in": "query", "name": "offset", "type": "integer"}, {"default": 0, "description": "Sort order: 0 relevance, 1 like count, 3 date posted", "enum": [0, 1, 3], "in": "query", "name": "sort_type", "type": "integer"}, {"default": 0, "description": "Date-posted window in days (0 to 365; the app uses 0, 1, 7, 30, 90, 180)", "in": "query", "name": "publish_time", "type": "integer"}, {"default": "tab_search", "description": "Search source forwarded as the app sends it", "in": "query", "name": "search_source", "type": "string"}, {"description": "search_id returned by a previous page of the same search", "in": "query", "name": "search_id", "type": "string"}], "path": "/tiktok/mobile/search/videos", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "keyword", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "offset", "type": "integer"}, {"enum": ["0", "1", "3"], "in": "query", "name": "sort_type", "type": "integer"}, {"in": "query", "name": "publish_time", "type": "integer"}, {"in": "query", "name": "search_source", "type": "string"}, {"in": "query", "name": "search_id", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-translation-description": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-translation-description", "method": "GET", "params": [{"description": "Video id to translate", "in": "query", "name": "item_id", "required": true, "type": "string", "x-example": "7650052642089225493"}, {"description": "Target language code (default en)", "in": "query", "name": "target_lang", "type": "string", "x-example": "en"}], "path": "/tiktok/mobile/translation/description", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "item_id", "required": true, "type": "string"}, {"in": "query", "name": "target_lang", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-trending-categories": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-trending-categories", "method": "GET", "params": [{"default": 20, "description": "Categories per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/trending/categories", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-trending-effects": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-trending-effects", "method": "GET", "params": [{"default": 20, "description": "Videos per page (max 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Pagination cursor", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/trending/effects", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-user-followers": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-user-followers", "method": "GET", "params": [{"description": "Creator user id", "in": "query", "name": "user_id", "required": true, "type": "string", "x-example": "7141011492116005914"}, {"description": "Followers per page (sanity bound: 50; omitted = app default, not measured)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Page token: the max_time returned by the previous page (omitted at 0 = first page)", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/user/followers", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-user-following": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-user-following", "method": "GET", "params": [{"description": "Creator's numeric user id", "in": "query", "name": "user_id", "required": true, "type": "string", "x-example": "7343380121988285482"}, {"default": 20, "description": "Rows per page (sanity bound: 50)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Page token: max_time returned by the previous page (omitted at 0)", "in": "query", "name": "cursor", "type": "integer"}], "path": "/tiktok/mobile/user/following", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "required": true, "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-user-original-effects": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-user-original-effects", "method": "GET", "params": [{"description": "Creator's numeric uid", "in": "query", "name": "user_id", "required": true, "type": "string", "x-example": "107955"}, {"default": 0, "description": "Paging token from the previous response", "in": "query", "name": "cursor", "type": "integer"}, {"default": 10, "description": "Effects per page (max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/user/original-effects", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-user-original-music": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-user-original-music", "method": "GET", "params": [{"description": "Creator's numeric uid", "in": "query", "name": "user_id", "required": true, "type": "string", "x-example": "107955"}, {"default": 0, "description": "Paging token from the previous response", "in": "query", "name": "cursor", "type": "integer"}, {"default": 10, "description": "Groups per page (max 50)", "in": "query", "name": "count", "type": "integer"}], "path": "/tiktok/mobile/user/original-music", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "required": true, "type": "string"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-user-posts": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-user-posts", "method": "GET", "params": [{"description": "Creator user id; one of user_id or sec_user_id is required (user_id wins if both are sent)", "in": "query", "name": "user_id", "type": "string", "x-example": "7141011492116005914"}, {"description": "Creator sec_user_id; one of user_id or sec_user_id is required", "in": "query", "name": "sec_user_id", "type": "string"}, {"default": 9, "description": "Videos per page (sanity bound: 50; omitted = app default, measured 9)", "in": "query", "name": "count", "type": "integer"}, {"default": 0, "description": "Page token: the max_cursor returned by the previous page (omitted at 0 = first page)", "in": "query", "name": "cursor", "type": "integer"}, {"default": 0, "description": "Sort type (0 = the app default; always sent, non-zero values are forwarded as given)", "in": "query", "name": "sort_type", "type": "integer"}], "path": "/tiktok/mobile/user/posts", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "type": "string"}, {"in": "query", "name": "sec_user_id", "type": "string"}, {"in": "query", "name": "count", "type": "integer"}, {"in": "query", "name": "cursor", "type": "integer"}, {"in": "query", "name": "sort_type", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-user-profile": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-user-profile", "method": "GET", "params": [{"description": "Creator user id", "in": "query", "name": "user_id", "type": "string", "x-example": "7141011492116005914"}, {"description": "Creator sec_user_id (alternate identifier; use instead of user_id)", "in": "query", "name": "sec_user_id", "type": "string", "x-example": "MS4wLjABAAAAexample"}], "path": "/tiktok/mobile/user/profile", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "user_id", "type": "string"}, {"in": "query", "name": "sec_user_id", "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-user-recommendations": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-user-recommendations", "method": "GET", "params": [{"description": "Requested page size; forwarded when set, but the gateway was measured to ignore it", "in": "query", "name": "count", "type": "integer", "x-example": 10}], "path": "/tiktok/mobile/user/recommendations", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "count", "type": "integer"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-video-detail": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-video-detail", "method": "GET", "params": [{"description": "Video id", "in": "query", "name": "aweme_id", "required": true, "type": "string", "x-example": "7329791719535283462"}], "path": "/tiktok/mobile/video/detail", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "aweme_id", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}, "tiktok-mobile-video-subtitles": {"bodyParam": null, "bodyRequired": false, "id": "tiktok-mobile-video-subtitles", "method": "GET", "params": [{"description": "Numeric id of the video (aweme id)", "in": "query", "name": "video_id", "required": true, "type": "string", "x-example": "7696141388324293918"}, {"description": "Target language code: ar, de, en, es, fr, hi, id, it, ja, ko, pt, ru, th, tr, vi, zh", "in": "query", "name": "language", "required": true, "type": "string", "x-example": "en"}], "path": "/tiktok/mobile/video/subtitles", "pathParams": [], "produces": ["application/json"], "queryParams": [{"in": "query", "name": "video_id", "required": true, "type": "string"}, {"in": "query", "name": "language", "required": true, "type": "string"}], "security": ["ApiKeyAuth"]}}
|
|
23
|
+
JSON
|
|
24
|
+
OPERATION_IDS = JSON.parse(<<~'JSON').freeze
|
|
25
|
+
["tiktok-mobile-anchor-list", "tiktok-mobile-collection-detail", "tiktok-mobile-collection-list", "tiktok-mobile-collection-videos", "tiktok-mobile-comment-replies", "tiktok-mobile-comments", "tiktok-mobile-feed", "tiktok-mobile-feed-offline", "tiktok-mobile-feed-post", "tiktok-mobile-hashtag", "tiktok-mobile-hashtag-posts", "tiktok-mobile-hashtags", "tiktok-mobile-live-effects", "tiktok-mobile-live-gifts", "tiktok-mobile-live-hashtags", "tiktok-mobile-live-room", "tiktok-mobile-live-room-by-user", "tiktok-mobile-live-search", "tiktok-mobile-live-tabs", "tiktok-mobile-music-beats", "tiktok-mobile-music-chart", "tiktok-mobile-music-choices", "tiktok-mobile-music-collection-list", "tiktok-mobile-music-collections", "tiktok-mobile-music-detail", "tiktok-mobile-music-pick", "tiktok-mobile-music-posts", "tiktok-mobile-music-related", "tiktok-mobile-music-trending", "tiktok-mobile-mv-videos", "tiktok-mobile-place-detail", "tiktok-mobile-place-info", "tiktok-mobile-place-media", "tiktok-mobile-place-photos", "tiktok-mobile-place-reviews", "tiktok-mobile-place-videos", "tiktok-mobile-playlist-detail", "tiktok-mobile-playlist-list", "tiktok-mobile-playlist-videos", "tiktok-mobile-search", "tiktok-mobile-search-effects", "tiktok-mobile-search-hashtags", "tiktok-mobile-search-music", "tiktok-mobile-search-photos", "tiktok-mobile-search-place", "tiktok-mobile-search-pre-info", "tiktok-mobile-search-stream", "tiktok-mobile-search-sug", "tiktok-mobile-search-tabs", "tiktok-mobile-search-user", "tiktok-mobile-search-videos", "tiktok-mobile-translation-description", "tiktok-mobile-trending-categories", "tiktok-mobile-trending-effects", "tiktok-mobile-user-followers", "tiktok-mobile-user-following", "tiktok-mobile-user-original-effects", "tiktok-mobile-user-original-music", "tiktok-mobile-user-posts", "tiktok-mobile-user-profile", "tiktok-mobile-user-recommendations", "tiktok-mobile-video-detail", "tiktok-mobile-video-subtitles"]
|
|
26
|
+
JSON
|
|
27
|
+
OPERATION_COUNT = OPERATION_IDS.length
|
|
28
|
+
|
|
29
|
+
class Client
|
|
30
|
+
attr_reader :base_url
|
|
31
|
+
|
|
32
|
+
def initialize(api_key: ENV["CRAWLORA_API_KEY"], base_url: "https://api.crawlora.net/api/v1", timeout: 30, user_agent: "crawlora-tiktok-mobile-ruby/0.1.0", transport: nil)
|
|
33
|
+
@api_key = api_key
|
|
34
|
+
@base_url = base_url.to_s.sub(%r{/+$}, "")
|
|
35
|
+
@timeout = Float(timeout)
|
|
36
|
+
@user_agent = user_agent
|
|
37
|
+
@transport = transport
|
|
38
|
+
@closed = false
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def request(operation_id, params = {}, response_type: :auto)
|
|
42
|
+
raise Errors::ClientError, "client is closed" if @closed
|
|
43
|
+
operation_id = operation_id.to_s
|
|
44
|
+
operation = OPERATIONS[operation_id]
|
|
45
|
+
raise Errors::ClientError.new("unknown operation: #{operation_id}", operation_id: operation_id) unless operation
|
|
46
|
+
raise Errors::ClientError.new("Crawlora API key is required", operation_id: operation_id) if @api_key.nil? || @api_key.to_s.empty?
|
|
47
|
+
normalized = params.each_with_object({}) { |(key, value), out| out[key.to_s] = value }
|
|
48
|
+
payload = request_body(operation, normalized)
|
|
49
|
+
url = build_url(operation, normalized)
|
|
50
|
+
uri = URI.parse(url)
|
|
51
|
+
request = operation["method"] == "POST" ? Net::HTTP::Post.new(uri) : Net::HTTP::Get.new(uri)
|
|
52
|
+
unless payload.nil?
|
|
53
|
+
request.body = payload
|
|
54
|
+
request["Content-Type"] = "application/json"
|
|
55
|
+
end
|
|
56
|
+
request["x-api-key"] = @api_key
|
|
57
|
+
request["User-Agent"] = @user_agent
|
|
58
|
+
request["Accept"] = operation["produces"].include?("text/plain") ? "application/json, text/plain" : "application/json"
|
|
59
|
+
begin
|
|
60
|
+
if @transport
|
|
61
|
+
response = if operation["method"] == "POST"
|
|
62
|
+
@transport.call(url, request.to_hash, @timeout, "POST", payload)
|
|
63
|
+
else
|
|
64
|
+
@transport.call(url, request.to_hash, @timeout)
|
|
65
|
+
end
|
|
66
|
+
status = Integer(response.fetch(:status) { response.fetch("status") })
|
|
67
|
+
body = response.fetch(:body) { response.fetch("body", "") }
|
|
68
|
+
headers = response.fetch(:headers) { response.fetch("headers", {}) }
|
|
69
|
+
content_type = headers["content-type"] || headers["Content-Type"]
|
|
70
|
+
else
|
|
71
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
72
|
+
http.use_ssl = uri.scheme == "https"
|
|
73
|
+
http.open_timeout = @timeout
|
|
74
|
+
http.read_timeout = @timeout
|
|
75
|
+
response = http.start { |connection| connection.request(request) }
|
|
76
|
+
status = response.code.to_i
|
|
77
|
+
body = response.body
|
|
78
|
+
content_type = response["content-type"]
|
|
79
|
+
end
|
|
80
|
+
rescue Timeout::Error, SocketError, SystemCallError, IOError, EOFError, Net::HTTPBadResponse, Net::ProtocolError, OpenSSL::SSL::SSLError => error
|
|
81
|
+
raise Errors::NetworkError.new("Crawlora request failed: #{error.message}", operation_id: operation_id)
|
|
82
|
+
end
|
|
83
|
+
unless status >= 200 && status < 300
|
|
84
|
+
klass = status >= 500 ? Errors::ServerError : Errors::ClientError
|
|
85
|
+
raise klass.new("Crawlora returned HTTP #{status}", status: status, operation_id: operation_id, body: body)
|
|
86
|
+
end
|
|
87
|
+
parse_response(body, content_type, operation, normalized, response_type)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def close
|
|
91
|
+
@closed = true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def closed?
|
|
95
|
+
@closed
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def with
|
|
99
|
+
return self unless block_given?
|
|
100
|
+
yield self
|
|
101
|
+
ensure
|
|
102
|
+
close if block_given?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.operation_count
|
|
106
|
+
OPERATION_COUNT
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def self.operation_ids
|
|
110
|
+
OPERATION_IDS
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def self.operations
|
|
114
|
+
OPERATIONS
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
define_method('anchor_list') do |**params|
|
|
118
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
119
|
+
request('tiktok-mobile-anchor-list', params, response_type: response_type)
|
|
120
|
+
end
|
|
121
|
+
define_method('collection_detail') do |**params|
|
|
122
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
123
|
+
request('tiktok-mobile-collection-detail', params, response_type: response_type)
|
|
124
|
+
end
|
|
125
|
+
define_method('collection_list') do |**params|
|
|
126
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
127
|
+
request('tiktok-mobile-collection-list', params, response_type: response_type)
|
|
128
|
+
end
|
|
129
|
+
define_method('collection_videos') do |**params|
|
|
130
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
131
|
+
request('tiktok-mobile-collection-videos', params, response_type: response_type)
|
|
132
|
+
end
|
|
133
|
+
define_method('comment_replies') do |**params|
|
|
134
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
135
|
+
request('tiktok-mobile-comment-replies', params, response_type: response_type)
|
|
136
|
+
end
|
|
137
|
+
define_method('comments') do |**params|
|
|
138
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
139
|
+
request('tiktok-mobile-comments', params, response_type: response_type)
|
|
140
|
+
end
|
|
141
|
+
define_method('feed') do |**params|
|
|
142
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
143
|
+
request('tiktok-mobile-feed', params, response_type: response_type)
|
|
144
|
+
end
|
|
145
|
+
define_method('feed_offline') do |**params|
|
|
146
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
147
|
+
request('tiktok-mobile-feed-offline', params, response_type: response_type)
|
|
148
|
+
end
|
|
149
|
+
define_method('feed_post') do |**params|
|
|
150
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
151
|
+
request('tiktok-mobile-feed-post', params, response_type: response_type)
|
|
152
|
+
end
|
|
153
|
+
define_method('hashtag') do |**params|
|
|
154
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
155
|
+
request('tiktok-mobile-hashtag', params, response_type: response_type)
|
|
156
|
+
end
|
|
157
|
+
define_method('hashtag_posts') do |**params|
|
|
158
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
159
|
+
request('tiktok-mobile-hashtag-posts', params, response_type: response_type)
|
|
160
|
+
end
|
|
161
|
+
define_method('hashtags') do |**params|
|
|
162
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
163
|
+
request('tiktok-mobile-hashtags', params, response_type: response_type)
|
|
164
|
+
end
|
|
165
|
+
define_method('live_effects') do |**params|
|
|
166
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
167
|
+
request('tiktok-mobile-live-effects', params, response_type: response_type)
|
|
168
|
+
end
|
|
169
|
+
define_method('live_gifts') do |**params|
|
|
170
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
171
|
+
request('tiktok-mobile-live-gifts', params, response_type: response_type)
|
|
172
|
+
end
|
|
173
|
+
define_method('live_hashtags') do |**params|
|
|
174
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
175
|
+
request('tiktok-mobile-live-hashtags', params, response_type: response_type)
|
|
176
|
+
end
|
|
177
|
+
define_method('live_room') do |**params|
|
|
178
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
179
|
+
request('tiktok-mobile-live-room', params, response_type: response_type)
|
|
180
|
+
end
|
|
181
|
+
define_method('live_room_by_user') do |**params|
|
|
182
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
183
|
+
request('tiktok-mobile-live-room-by-user', params, response_type: response_type)
|
|
184
|
+
end
|
|
185
|
+
define_method('live_search') do |**params|
|
|
186
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
187
|
+
request('tiktok-mobile-live-search', params, response_type: response_type)
|
|
188
|
+
end
|
|
189
|
+
define_method('live_tabs') do |**params|
|
|
190
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
191
|
+
request('tiktok-mobile-live-tabs', params, response_type: response_type)
|
|
192
|
+
end
|
|
193
|
+
define_method('music_beats') do |**params|
|
|
194
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
195
|
+
request('tiktok-mobile-music-beats', params, response_type: response_type)
|
|
196
|
+
end
|
|
197
|
+
define_method('music_chart') do |**params|
|
|
198
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
199
|
+
request('tiktok-mobile-music-chart', params, response_type: response_type)
|
|
200
|
+
end
|
|
201
|
+
define_method('music_choices') do |**params|
|
|
202
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
203
|
+
request('tiktok-mobile-music-choices', params, response_type: response_type)
|
|
204
|
+
end
|
|
205
|
+
define_method('music_collection_list') do |**params|
|
|
206
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
207
|
+
request('tiktok-mobile-music-collection-list', params, response_type: response_type)
|
|
208
|
+
end
|
|
209
|
+
define_method('music_collections') do |**params|
|
|
210
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
211
|
+
request('tiktok-mobile-music-collections', params, response_type: response_type)
|
|
212
|
+
end
|
|
213
|
+
define_method('music_detail') do |**params|
|
|
214
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
215
|
+
request('tiktok-mobile-music-detail', params, response_type: response_type)
|
|
216
|
+
end
|
|
217
|
+
define_method('music_pick') do |**params|
|
|
218
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
219
|
+
request('tiktok-mobile-music-pick', params, response_type: response_type)
|
|
220
|
+
end
|
|
221
|
+
define_method('music_posts') do |**params|
|
|
222
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
223
|
+
request('tiktok-mobile-music-posts', params, response_type: response_type)
|
|
224
|
+
end
|
|
225
|
+
define_method('music_related') do |**params|
|
|
226
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
227
|
+
request('tiktok-mobile-music-related', params, response_type: response_type)
|
|
228
|
+
end
|
|
229
|
+
define_method('music_trending') do |**params|
|
|
230
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
231
|
+
request('tiktok-mobile-music-trending', params, response_type: response_type)
|
|
232
|
+
end
|
|
233
|
+
define_method('mv_videos') do |**params|
|
|
234
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
235
|
+
request('tiktok-mobile-mv-videos', params, response_type: response_type)
|
|
236
|
+
end
|
|
237
|
+
define_method('place_detail') do |**params|
|
|
238
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
239
|
+
request('tiktok-mobile-place-detail', params, response_type: response_type)
|
|
240
|
+
end
|
|
241
|
+
define_method('place_info') do |**params|
|
|
242
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
243
|
+
request('tiktok-mobile-place-info', params, response_type: response_type)
|
|
244
|
+
end
|
|
245
|
+
define_method('place_media') do |**params|
|
|
246
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
247
|
+
request('tiktok-mobile-place-media', params, response_type: response_type)
|
|
248
|
+
end
|
|
249
|
+
define_method('place_photos') do |**params|
|
|
250
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
251
|
+
request('tiktok-mobile-place-photos', params, response_type: response_type)
|
|
252
|
+
end
|
|
253
|
+
define_method('place_reviews') do |**params|
|
|
254
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
255
|
+
request('tiktok-mobile-place-reviews', params, response_type: response_type)
|
|
256
|
+
end
|
|
257
|
+
define_method('place_videos') do |**params|
|
|
258
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
259
|
+
request('tiktok-mobile-place-videos', params, response_type: response_type)
|
|
260
|
+
end
|
|
261
|
+
define_method('playlist_detail') do |**params|
|
|
262
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
263
|
+
request('tiktok-mobile-playlist-detail', params, response_type: response_type)
|
|
264
|
+
end
|
|
265
|
+
define_method('playlist_list') do |**params|
|
|
266
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
267
|
+
request('tiktok-mobile-playlist-list', params, response_type: response_type)
|
|
268
|
+
end
|
|
269
|
+
define_method('playlist_videos') do |**params|
|
|
270
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
271
|
+
request('tiktok-mobile-playlist-videos', params, response_type: response_type)
|
|
272
|
+
end
|
|
273
|
+
define_method('search') do |**params|
|
|
274
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
275
|
+
request('tiktok-mobile-search', params, response_type: response_type)
|
|
276
|
+
end
|
|
277
|
+
define_method('search_effects') do |**params|
|
|
278
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
279
|
+
request('tiktok-mobile-search-effects', params, response_type: response_type)
|
|
280
|
+
end
|
|
281
|
+
define_method('search_hashtags') do |**params|
|
|
282
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
283
|
+
request('tiktok-mobile-search-hashtags', params, response_type: response_type)
|
|
284
|
+
end
|
|
285
|
+
define_method('search_music') do |**params|
|
|
286
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
287
|
+
request('tiktok-mobile-search-music', params, response_type: response_type)
|
|
288
|
+
end
|
|
289
|
+
define_method('search_photos') do |**params|
|
|
290
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
291
|
+
request('tiktok-mobile-search-photos', params, response_type: response_type)
|
|
292
|
+
end
|
|
293
|
+
define_method('search_place') do |**params|
|
|
294
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
295
|
+
request('tiktok-mobile-search-place', params, response_type: response_type)
|
|
296
|
+
end
|
|
297
|
+
define_method('search_pre_info') do |**params|
|
|
298
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
299
|
+
request('tiktok-mobile-search-pre-info', params, response_type: response_type)
|
|
300
|
+
end
|
|
301
|
+
define_method('search_stream') do |**params|
|
|
302
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
303
|
+
request('tiktok-mobile-search-stream', params, response_type: response_type)
|
|
304
|
+
end
|
|
305
|
+
define_method('search_sug') do |**params|
|
|
306
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
307
|
+
request('tiktok-mobile-search-sug', params, response_type: response_type)
|
|
308
|
+
end
|
|
309
|
+
define_method('search_tabs') do |**params|
|
|
310
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
311
|
+
request('tiktok-mobile-search-tabs', params, response_type: response_type)
|
|
312
|
+
end
|
|
313
|
+
define_method('search_user') do |**params|
|
|
314
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
315
|
+
request('tiktok-mobile-search-user', params, response_type: response_type)
|
|
316
|
+
end
|
|
317
|
+
define_method('search_videos') do |**params|
|
|
318
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
319
|
+
request('tiktok-mobile-search-videos', params, response_type: response_type)
|
|
320
|
+
end
|
|
321
|
+
define_method('translation_description') do |**params|
|
|
322
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
323
|
+
request('tiktok-mobile-translation-description', params, response_type: response_type)
|
|
324
|
+
end
|
|
325
|
+
define_method('trending_categories') do |**params|
|
|
326
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
327
|
+
request('tiktok-mobile-trending-categories', params, response_type: response_type)
|
|
328
|
+
end
|
|
329
|
+
define_method('trending_effects') do |**params|
|
|
330
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
331
|
+
request('tiktok-mobile-trending-effects', params, response_type: response_type)
|
|
332
|
+
end
|
|
333
|
+
define_method('user_followers') do |**params|
|
|
334
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
335
|
+
request('tiktok-mobile-user-followers', params, response_type: response_type)
|
|
336
|
+
end
|
|
337
|
+
define_method('user_following') do |**params|
|
|
338
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
339
|
+
request('tiktok-mobile-user-following', params, response_type: response_type)
|
|
340
|
+
end
|
|
341
|
+
define_method('user_original_effects') do |**params|
|
|
342
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
343
|
+
request('tiktok-mobile-user-original-effects', params, response_type: response_type)
|
|
344
|
+
end
|
|
345
|
+
define_method('user_original_music') do |**params|
|
|
346
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
347
|
+
request('tiktok-mobile-user-original-music', params, response_type: response_type)
|
|
348
|
+
end
|
|
349
|
+
define_method('user_posts') do |**params|
|
|
350
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
351
|
+
request('tiktok-mobile-user-posts', params, response_type: response_type)
|
|
352
|
+
end
|
|
353
|
+
define_method('user_profile') do |**params|
|
|
354
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
355
|
+
request('tiktok-mobile-user-profile', params, response_type: response_type)
|
|
356
|
+
end
|
|
357
|
+
define_method('user_recommendations') do |**params|
|
|
358
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
359
|
+
request('tiktok-mobile-user-recommendations', params, response_type: response_type)
|
|
360
|
+
end
|
|
361
|
+
define_method('video_detail') do |**params|
|
|
362
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
363
|
+
request('tiktok-mobile-video-detail', params, response_type: response_type)
|
|
364
|
+
end
|
|
365
|
+
define_method('video_subtitles') do |**params|
|
|
366
|
+
response_type = params.delete(:response_type) || params.delete(:_response_type) || :auto
|
|
367
|
+
request('tiktok-mobile-video-subtitles', params, response_type: response_type)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
private
|
|
371
|
+
|
|
372
|
+
# POST operations take their JSON body under the body parameter name or "body".
|
|
373
|
+
def request_body(operation, params)
|
|
374
|
+
name = operation["bodyParam"]
|
|
375
|
+
return nil if name.nil?
|
|
376
|
+
value = params.key?(name) ? params[name] : params["body"]
|
|
377
|
+
if value.nil?
|
|
378
|
+
raise Errors::ClientError.new("missing request body: #{name}", operation_id: operation["id"]) if operation["bodyRequired"]
|
|
379
|
+
return nil
|
|
380
|
+
end
|
|
381
|
+
value.is_a?(String) ? value : JSON.generate(value)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def build_url(operation, params)
|
|
385
|
+
known = operation["params"].map { |param| param["name"] }
|
|
386
|
+
known << "body" if operation["bodyParam"]
|
|
387
|
+
unknown = params.keys - known
|
|
388
|
+
raise Errors::ClientError.new("unknown parameters: #{unknown.join(', ')}", operation_id: operation["id"]) unless unknown.empty?
|
|
389
|
+
path = operation["path"].dup
|
|
390
|
+
operation["params"].select { |param| param["in"] == "path" }.each do |param|
|
|
391
|
+
value = params[param["name"]]
|
|
392
|
+
raise Errors::ClientError.new("missing path parameter: #{param['name']}", operation_id: operation["id"]) if value.nil?
|
|
393
|
+
path.sub!("{" + param["name"] + "}", percent_encode(value.to_s))
|
|
394
|
+
end
|
|
395
|
+
pairs = []
|
|
396
|
+
operation["queryParams"].each do |param|
|
|
397
|
+
name = param["name"]
|
|
398
|
+
value = params.key?(name) ? params[name] : param["default"]
|
|
399
|
+
if value.nil?
|
|
400
|
+
raise Errors::ClientError.new("missing query parameter: #{name}", operation_id: operation["id"]) if param["required"]
|
|
401
|
+
next
|
|
402
|
+
end
|
|
403
|
+
enum_values = param["enum"] || (param["items"] && param["items"]["enum"])
|
|
404
|
+
if enum_values && !(value.is_a?(Array) ? value : [value]).all? { |item| enum_values.map(&:to_s).include?(item.to_s) }
|
|
405
|
+
raise Errors::ClientError.new("invalid value for #{name}", operation_id: operation["id"])
|
|
406
|
+
end
|
|
407
|
+
if value.is_a?(Array)
|
|
408
|
+
format = param["collectionFormat"] || "csv"
|
|
409
|
+
if format == "multi"
|
|
410
|
+
value.each { |item| pairs << [name, scalar(item)] }
|
|
411
|
+
else
|
|
412
|
+
separator = {"csv" => ",", "ssv" => " ", "tsv" => "\t", "pipes" => "|"}[format] || ","
|
|
413
|
+
pairs << [name, value.map { |item| scalar(item) }.join(separator)]
|
|
414
|
+
end
|
|
415
|
+
else
|
|
416
|
+
pairs << [name, scalar(value)]
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
query = pairs.map { |name, value| "#{percent_encode(name)}=#{percent_encode(value)}" }.join("&")
|
|
420
|
+
@base_url + path + (query.empty? ? "" : "?" + query)
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def scalar(value)
|
|
424
|
+
value == true ? "true" : (value == false ? "false" : value.to_s)
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
def percent_encode(value)
|
|
428
|
+
URI::DEFAULT_PARSER.escape(value.to_s, /[^A-Za-z0-9\-._~]/)
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def parse_response(body, content_type, operation, params, response_type)
|
|
432
|
+
type = response_type.to_s
|
|
433
|
+
raise Errors::ClientError.new("response_type must be auto, json, or text", operation_id: operation["id"]) unless %w[auto json text].include?(type)
|
|
434
|
+
format = operation["params"].find { |param| param["name"] == "format" }
|
|
435
|
+
text_formats = format && format["enum"] ? format["enum"].reject { |value| %w[json application/json].include?(value.to_s.downcase) } : []
|
|
436
|
+
raw_format = params["format"] && text_formats.include?(params["format"].to_s)
|
|
437
|
+
json_format = format && format["enum"] && format["enum"].any? { |value| %w[json application/json].include?(value.to_s.downcase) } && %w[json application/json].include?(params["format"].to_s.downcase)
|
|
438
|
+
is_json = json_format || content_type.to_s.downcase.include?("json") || operation["produces"] == ["application/json"]
|
|
439
|
+
return body if type == "text" || raw_format || (type == "auto" && !is_json)
|
|
440
|
+
JSON.parse(body)
|
|
441
|
+
rescue JSON::ParserError => error
|
|
442
|
+
raise Errors::Error.new("invalid JSON response from Crawlora: #{error.message}", operation_id: operation["id"], body: body)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
public
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: crawlora-tiktok-mobile
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Crawlora
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-10-29 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Ruby client for Crawlora's hosted TikTok Mobile API. Covers mobile feed,
|
|
14
|
+
search, video, creator, music, LIVE, and place endpoints across 63 endpoints.
|
|
15
|
+
email:
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- CHANGELOG.md
|
|
21
|
+
- LICENSE
|
|
22
|
+
- README.md
|
|
23
|
+
- lib/crawlora/tiktok-mobile.rb
|
|
24
|
+
- lib/crawlora/tiktok-mobile/client.rb
|
|
25
|
+
- lib/crawlora/tiktok-mobile/version.rb
|
|
26
|
+
homepage: https://crawlora.net/?utm_source=rubygems&utm_medium=referral&utm_campaign=platform-clients&utm_content=tiktok-mobile-ruby-homepage
|
|
27
|
+
licenses:
|
|
28
|
+
- MIT
|
|
29
|
+
metadata:
|
|
30
|
+
source_code_uri: https://github.com/Crawlora-org/crawlora-tiktok-mobile
|
|
31
|
+
documentation_uri: https://github.com/Crawlora-org/crawlora-tiktok-mobile/blob/main/ruby/README.md
|
|
32
|
+
rubygems_mfa_required: 'true'
|
|
33
|
+
post_install_message:
|
|
34
|
+
rdoc_options: []
|
|
35
|
+
require_paths:
|
|
36
|
+
- lib
|
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '2.6'
|
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
requirements: []
|
|
48
|
+
rubygems_version: 3.4.19
|
|
49
|
+
signing_key:
|
|
50
|
+
specification_version: 4
|
|
51
|
+
summary: TikTok Mobile API client for Crawlora (63 endpoints)
|
|
52
|
+
test_files: []
|