dataxapi 0.2.0__tar.gz → 0.3.0__tar.gz

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.
@@ -0,0 +1,58 @@
1
+ # Changelog
2
+
3
+ All notable changes to the `dataxapi` Python package are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the package uses [Semantic Versioning](https://semver.org/) (while below 1.0.0, a minor version may change defaults or behavior).
4
+
5
+ ## [0.3.0]
6
+
7
+ This release adopts the DataXAPI brand names, changes the default timeout and the automatic retry rules, and adds types for test keys and partial results. No API operation was added, removed or renamed since 0.2.1 (151 operations). See [Upgrading from 0.2.x](#upgrading-from-02x).
8
+
9
+ ### Added
10
+
11
+ - `DataXClient`, `DataXError`, `DataXConnectionError` and `DataXPaginationError` are now the primary exports of `dataxapi`.
12
+ - Constants `DEFAULT_TIMEOUT` (60.0), `IN_PROGRESS_WAIT_LIMIT` (90.0) and `NON_RETRYABLE_CODES` in `dataxapi.client`.
13
+ - Test keys: an `sk_test_...` key works with the same client. Calls are validated like live calls, return the API's documented example and are never charged; responses carry `meta["mode"] == "test"`. See [Test keys](https://dataxapi.com/en/docs/authentication/#test-keys).
14
+ - Response `TypedDict`s: optional `meta["mode"]` (`"test"`) on every operation, and `meta["delivered_count"]` / `meta["filtered_count"]` on operations that can return `partial` results (items returned vs. items removed for rights reasons).
15
+ - `Usage`: `mode` (`"live" | "test"`); `status` is now `Literal["reserved", "complete", "empty", "partial", "failed"]` instead of `str`.
16
+ - `Problem`: `limit_scope` (`"workspace" | "api_key" | "listing"`) on `rate_limited`; `request_id` may be `None`.
17
+ - The API can now answer with these problem codes, raised as `DataXError`: `no_results` (404; since 2026-10-08 live calls no longer return it, see Changed), `not_found` (404: the data source states that the requested object does not exist), `route_not_found` (404), `method_not_allowed` (405), `account_suspended` (403) and `invalid_test_scenario` (400, test keys only). See [Errors](https://dataxapi.com/en/docs/errors/).
18
+ - `client.call()` with an unknown operation id raises a `ValueError` that names the id, before any request is sent (0.2.x raised a bare `KeyError`).
19
+ - Package metadata: the "Documentation" link now points to the docs center; the API catalog has its own link.
20
+
21
+ ### Changed
22
+
23
+ - **Empty results are charged on every API (2026-10-08).** When the data source answers normally but has no data, the API returns 200 with `meta.result_status: "empty"` and charges the unit price, because the data source bills that lookup; data-source failures stay free. The 142 APIs that used to answer a free 404 `no_results` now do this, so `no_results` is no longer returned by live calls. The code stays in the types and in `NON_RETRYABLE_CODES` so existing error handling keeps working.
24
+ - **Default `timeout` per network attempt is 60 seconds (was 10 seconds)**, above the slowest data source's own timeout, so slow sources no longer end in `DataXConnectionError` by default.
25
+ - **`409 request_in_progress` is polled with the same idempotency key** for up to about 90 seconds, waiting `retry_after_ms` between polls, and no longer uses up `max_retries`. The original call may still be charged; polling returns its result instead of starting a new call. `max_retries=0` turns this off.
26
+ - **Deterministic error codes are never retried automatically**: `upstream_failed`, `upstream_schema_changed`, `settlement_failed`, `previous_execution_failed`, `no_results`, `not_found`, `route_not_found`, `method_not_allowed`, `listing_paused` and `account_suspended`. In 0.2.x every 5xx except `listing_paused` was retried with the same key, which could only repeat the same answer.
27
+ - Network errors and timeouts are retried with exponential backoff and jitter (about 0.25 s, 0.5 s, 1 s …, at most 8 s and never longer than the timeout) instead of immediately.
28
+ - Default error messages say "DataXAPI" instead of "Exact API".
29
+
30
+ ### Deprecated
31
+
32
+ - `ExactApiClient`, `ExactApiError`, `ExactApiConnectionError` and `ExactApiPaginationError` remain as aliases of the new classes (the same class objects, so `except ExactApiError` keeps working) and will be removed in a future major version.
33
+
34
+ ### Upgrading from 0.2.x
35
+
36
+ 1. `pip install -U "dataxapi>=0.3,<0.4"`. Existing code keeps working: the old names are aliases.
37
+ 2. Rename imports when convenient: `ExactApiClient` → `DataXClient`, `ExactApiError` → `DataXError`, `ExactApiConnectionError` → `DataXConnectionError`, `ExactApiPaginationError` → `DataXPaginationError`.
38
+ 3. If you relied on the old 10-second timeout to fail fast, pass `timeout=10`. With the defaults, a call that keeps hitting network timeouts can take about three minutes (3 attempts × 60 s) before `DataXConnectionError`.
39
+ 4. `upstream_failed`, `upstream_schema_changed`, `settlement_failed` and `previous_execution_failed` are no longer retried for you. They are not charged; retry later yourself with a **new** `idempotency_key` if you still need the data.
40
+ 5. A call that meets `request_in_progress` can now wait up to about 90 seconds inside the SDK. Use `max_retries=0` if your code polls on its own.
41
+ 6. If you caught `KeyError` for a mistyped operation id, catch `ValueError` instead.
42
+ 7. Type checkers: `Usage["status"]` is now a `Literal` union and `Problem["request_id"]` can be `None`.
43
+
44
+ ## 0.2.1 and earlier
45
+
46
+ 0.1.0 (2026-09-17), 0.2.0 (2026-09-19) and 0.2.1 (2026-09-20) were published before this changelog existed.
47
+
48
+ ## 中文摘要
49
+
50
+ 0.3.0 主要变化:
51
+
52
+ - 新名称 `DataXClient`、`DataXError`、`DataXConnectionError`、`DataXPaginationError`;旧的 `ExactApi*` 名称继续可用,作为兼容别名保留。
53
+ - 每次网络尝试的默认超时从 10 秒改为 60 秒。
54
+ - 遇到 `request_in_progress` 时,用同一个幂等 Key 继续等待最多约 90 秒,拿到首次调用的结果,不占用 `max_retries`。
55
+ - `upstream_failed`、`no_results`、`not_found` 等重试也不会变的错误不再自动重试;网络错误改为逐步加长、带随机抖动的等待后重试。
56
+ - 支持测试 Key(`sk_test_`):返回示例数据、不扣费,响应里 `meta["mode"]` 为 `"test"`;新增 `delivered_count` / `filtered_count` 等类型和 5 个新错误码。
57
+ - 写错接口名时抛出 `ValueError`(原来是 `KeyError`)。
58
+ - 接口本身没有增删或改名。升级方法见上面的 "Upgrading from 0.2.x"。
@@ -0,0 +1 @@
1
+ include CHANGELOG.md
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.4
2
+ Name: dataxapi
3
+ Version: 0.3.0
4
+ Summary: DataXAPI Python SDK: exact public social data endpoints with one CNY balance
5
+ Author-email: "DataXAPI (Aurevant LLC)" <support@dataxapi.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://dataxapi.com
8
+ Project-URL: Documentation, https://dataxapi.com/en/docs/
9
+ Project-URL: API catalog, https://dataxapi.com/en/apis/
10
+ Project-URL: Changelog, https://dataxapi.com/en/docs/sdks/#upgrade
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+
18
+ # DataXAPI Python SDK
19
+
20
+ Typed client for [DataXAPI](https://dataxapi.com): fixed, per-call-priced public social data APIs (X, Reddit, Xiaohongshu, Douyin, TikTok, Instagram, YouTube, Bilibili, Weibo, Kuaishou, Zhihu) paid from one CNY balance.
21
+
22
+ The input, output and error `TypedDict`s are generated from the current API catalog / [OpenAPI document](https://dataxapi.com/openapi.json). The runtime only handles authentication, timeouts, safe retries with one idempotency key, pagination and Usage lookups. It has no third-party dependencies.
23
+
24
+ ## Install
25
+
26
+ ```sh
27
+ pip install dataxapi
28
+ ```
29
+
30
+ Python 3.11 or later.
31
+
32
+ ## Authenticate
33
+
34
+ Create an API key in the [Console](https://dataxapi.com/en/console/) and keep it in an environment variable:
35
+
36
+ ```sh
37
+ export DATAXAPI_API_KEY="sk_live_..."
38
+ ```
39
+
40
+ For development and tests, use a test key (`sk_test_...`, created with the Console's "Test key" switch) with the same client: calls are validated like live calls but return each API's documented example with `meta["mode"] == "test"` and are never charged. See [Test keys](https://dataxapi.com/en/docs/authentication/#test-keys).
41
+
42
+ ## Call an API
43
+
44
+ ```python
45
+ import os
46
+ import uuid
47
+
48
+ from dataxapi import DataXClient
49
+
50
+ client = DataXClient("https://api.dataxapi.com", os.environ["DATAXAPI_API_KEY"])
51
+
52
+ # Every API is available by its operationId (the OpenAPI operationId).
53
+ result = client.call(
54
+ "x_posts_search_v1",
55
+ {"query": "OpenAI min_faves:100"},
56
+ # Optional. Store the key with your job and reuse it when you retry the same
57
+ # logical call: the retry returns the original result and is not charged again.
58
+ idempotency_key=str(uuid.uuid4()),
59
+ )
60
+
61
+ print(len(result["data"]["items"]), result["meta"]["charged_amount"], result["meta"]["balance_after"])
62
+ ```
63
+
64
+ When you do not pass `idempotency_key`, the SDK generates one per call and reuses it for its own automatic retries. Unknown operation ids raise a `ValueError` that names the id.
65
+
66
+ ## Errors and retries
67
+
68
+ - `DataXError`: the API answered with a non-2xx problem. Attributes: `status`, `code`, `detail`, `request_id`, `charge_status`, `charged_amount`, `retry_after_ms`, `retry_after`, `idempotency_key`.
69
+ - `DataXConnectionError`: the outcome is unknown (network error or timeout). Retry later with `error.idempotency_key` so the call cannot run or charge twice.
70
+
71
+ Built-in behavior (defaults: `timeout=60` seconds per attempt, `max_retries=2`):
72
+
73
+ - `429 rate_limited` and retryable `5xx` errors are retried with the same key after the server's `retry_after_ms` / `Retry-After`.
74
+ - `409 request_in_progress` means the original call with that key is still running (`charge_status` is `unknown` until it settles). The SDK waits `retry_after_ms` and polls with the same key for up to about 90 seconds; this does not use up `max_retries`. Set `max_retries=0` to disable all automatic retries.
75
+ - Network errors are retried with exponential backoff and jitter, with the same key.
76
+ - Codes that repeat deterministically are never retried: `upstream_failed`, `upstream_schema_changed`, `settlement_failed`, `previous_execution_failed`, `no_results`, `not_found`, `route_not_found`, `method_not_allowed`, `listing_paused`, `account_suspended`, and other 4xx errors. (`no_results` is no longer returned since 2026-10-08: an empty result is a charged 200 with `meta.result_status` `"empty"`.)
77
+
78
+ ```python
79
+ from dataxapi import DataXConnectionError, DataXError
80
+
81
+ try:
82
+ client.call("x_posts_search_v1", {"query": "OpenAI"})
83
+ except DataXError as error:
84
+ print(error.status, error.code, error.request_id, error.charge_status)
85
+ except DataXConnectionError as error:
86
+ print("retry later with", error.idempotency_key)
87
+ ```
88
+
89
+ `DataXPaginationError` is raised when a page iterator sees a repeated cursor. `ExactApiClient`, `ExactApiError`, `ExactApiConnectionError` and `ExactApiPaginationError` remain as deprecated aliases of these classes.
90
+
91
+ ## Upgrading from 0.2.x
92
+
93
+ 0.3.0 renames the classes (old names still work), raises the default timeout from 10 to 60 seconds, polls `request_in_progress` with the same key, and stops retrying errors that a retry would only repeat. See `CHANGELOG.md` in the source distribution and the [upgrade notes](https://dataxapi.com/en/docs/sdks/#upgrade).
94
+
95
+ ## Links
96
+
97
+ - Docs: <https://dataxapi.com/en/docs/> (中文:<https://dataxapi.com/zh/docs/>)
98
+ - SDK guide: <https://dataxapi.com/en/docs/sdks/>
99
+ - Upgrade notes: <https://dataxapi.com/en/docs/sdks/#upgrade>
100
+ - Errors: <https://dataxapi.com/en/docs/errors/>
101
+ - API catalog and prices: <https://dataxapi.com/en/apis/>
102
+ - OpenAPI: <https://dataxapi.com/openapi.json>
103
+ - MCP server for AI agents: <https://dataxapi.com/en/docs/mcp/>
104
+
105
+ ## 中文说明
106
+
107
+ `dataxapi` 是 DataXAPI 的 Python SDK:用一个 API Key、一个人民币余额按次调用各平台的公开数据接口。
108
+
109
+ - 安装:`pip install dataxapi`(Python 3.11+,无第三方依赖)。
110
+ - 用法:`DataXClient("https://api.dataxapi.com", api_key)`,再用 `client.call("<operationId>", 参数, idempotency_key=...)` 调用任意接口。
111
+ - 重试:同一个逻辑调用复用同一个 `idempotency_key`,重试不会重复执行或扣费;SDK 自动重试 429 和可重试的 5xx,遇到 `request_in_progress` 会用同一个 Key 等待最多约 90 秒;`upstream_failed`、`no_results`、`not_found` 等确定性错误不自动重试。
112
+ - 默认每次尝试超时 60 秒,`max_retries` 默认 2。
113
+ - 文档:<https://dataxapi.com/zh/docs/>,SDK 说明:<https://dataxapi.com/zh/docs/sdks/>。
114
+ - 从 0.2.x 升级:旧名称仍可用,默认超时由 10 秒改为 60 秒,重试规则有调整,见源码包中的 `CHANGELOG.md` 和 <https://dataxapi.com/zh/docs/sdks/#upgrade>。
115
+
116
+ ## License
117
+
118
+ MIT
@@ -0,0 +1,101 @@
1
+ # DataXAPI Python SDK
2
+
3
+ Typed client for [DataXAPI](https://dataxapi.com): fixed, per-call-priced public social data APIs (X, Reddit, Xiaohongshu, Douyin, TikTok, Instagram, YouTube, Bilibili, Weibo, Kuaishou, Zhihu) paid from one CNY balance.
4
+
5
+ The input, output and error `TypedDict`s are generated from the current API catalog / [OpenAPI document](https://dataxapi.com/openapi.json). The runtime only handles authentication, timeouts, safe retries with one idempotency key, pagination and Usage lookups. It has no third-party dependencies.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pip install dataxapi
11
+ ```
12
+
13
+ Python 3.11 or later.
14
+
15
+ ## Authenticate
16
+
17
+ Create an API key in the [Console](https://dataxapi.com/en/console/) and keep it in an environment variable:
18
+
19
+ ```sh
20
+ export DATAXAPI_API_KEY="sk_live_..."
21
+ ```
22
+
23
+ For development and tests, use a test key (`sk_test_...`, created with the Console's "Test key" switch) with the same client: calls are validated like live calls but return each API's documented example with `meta["mode"] == "test"` and are never charged. See [Test keys](https://dataxapi.com/en/docs/authentication/#test-keys).
24
+
25
+ ## Call an API
26
+
27
+ ```python
28
+ import os
29
+ import uuid
30
+
31
+ from dataxapi import DataXClient
32
+
33
+ client = DataXClient("https://api.dataxapi.com", os.environ["DATAXAPI_API_KEY"])
34
+
35
+ # Every API is available by its operationId (the OpenAPI operationId).
36
+ result = client.call(
37
+ "x_posts_search_v1",
38
+ {"query": "OpenAI min_faves:100"},
39
+ # Optional. Store the key with your job and reuse it when you retry the same
40
+ # logical call: the retry returns the original result and is not charged again.
41
+ idempotency_key=str(uuid.uuid4()),
42
+ )
43
+
44
+ print(len(result["data"]["items"]), result["meta"]["charged_amount"], result["meta"]["balance_after"])
45
+ ```
46
+
47
+ When you do not pass `idempotency_key`, the SDK generates one per call and reuses it for its own automatic retries. Unknown operation ids raise a `ValueError` that names the id.
48
+
49
+ ## Errors and retries
50
+
51
+ - `DataXError`: the API answered with a non-2xx problem. Attributes: `status`, `code`, `detail`, `request_id`, `charge_status`, `charged_amount`, `retry_after_ms`, `retry_after`, `idempotency_key`.
52
+ - `DataXConnectionError`: the outcome is unknown (network error or timeout). Retry later with `error.idempotency_key` so the call cannot run or charge twice.
53
+
54
+ Built-in behavior (defaults: `timeout=60` seconds per attempt, `max_retries=2`):
55
+
56
+ - `429 rate_limited` and retryable `5xx` errors are retried with the same key after the server's `retry_after_ms` / `Retry-After`.
57
+ - `409 request_in_progress` means the original call with that key is still running (`charge_status` is `unknown` until it settles). The SDK waits `retry_after_ms` and polls with the same key for up to about 90 seconds; this does not use up `max_retries`. Set `max_retries=0` to disable all automatic retries.
58
+ - Network errors are retried with exponential backoff and jitter, with the same key.
59
+ - Codes that repeat deterministically are never retried: `upstream_failed`, `upstream_schema_changed`, `settlement_failed`, `previous_execution_failed`, `no_results`, `not_found`, `route_not_found`, `method_not_allowed`, `listing_paused`, `account_suspended`, and other 4xx errors. (`no_results` is no longer returned since 2026-10-08: an empty result is a charged 200 with `meta.result_status` `"empty"`.)
60
+
61
+ ```python
62
+ from dataxapi import DataXConnectionError, DataXError
63
+
64
+ try:
65
+ client.call("x_posts_search_v1", {"query": "OpenAI"})
66
+ except DataXError as error:
67
+ print(error.status, error.code, error.request_id, error.charge_status)
68
+ except DataXConnectionError as error:
69
+ print("retry later with", error.idempotency_key)
70
+ ```
71
+
72
+ `DataXPaginationError` is raised when a page iterator sees a repeated cursor. `ExactApiClient`, `ExactApiError`, `ExactApiConnectionError` and `ExactApiPaginationError` remain as deprecated aliases of these classes.
73
+
74
+ ## Upgrading from 0.2.x
75
+
76
+ 0.3.0 renames the classes (old names still work), raises the default timeout from 10 to 60 seconds, polls `request_in_progress` with the same key, and stops retrying errors that a retry would only repeat. See `CHANGELOG.md` in the source distribution and the [upgrade notes](https://dataxapi.com/en/docs/sdks/#upgrade).
77
+
78
+ ## Links
79
+
80
+ - Docs: <https://dataxapi.com/en/docs/> (中文:<https://dataxapi.com/zh/docs/>)
81
+ - SDK guide: <https://dataxapi.com/en/docs/sdks/>
82
+ - Upgrade notes: <https://dataxapi.com/en/docs/sdks/#upgrade>
83
+ - Errors: <https://dataxapi.com/en/docs/errors/>
84
+ - API catalog and prices: <https://dataxapi.com/en/apis/>
85
+ - OpenAPI: <https://dataxapi.com/openapi.json>
86
+ - MCP server for AI agents: <https://dataxapi.com/en/docs/mcp/>
87
+
88
+ ## 中文说明
89
+
90
+ `dataxapi` 是 DataXAPI 的 Python SDK:用一个 API Key、一个人民币余额按次调用各平台的公开数据接口。
91
+
92
+ - 安装:`pip install dataxapi`(Python 3.11+,无第三方依赖)。
93
+ - 用法:`DataXClient("https://api.dataxapi.com", api_key)`,再用 `client.call("<operationId>", 参数, idempotency_key=...)` 调用任意接口。
94
+ - 重试:同一个逻辑调用复用同一个 `idempotency_key`,重试不会重复执行或扣费;SDK 自动重试 429 和可重试的 5xx,遇到 `request_in_progress` 会用同一个 Key 等待最多约 90 秒;`upstream_failed`、`no_results`、`not_found` 等确定性错误不自动重试。
95
+ - 默认每次尝试超时 60 秒,`max_retries` 默认 2。
96
+ - 文档:<https://dataxapi.com/zh/docs/>,SDK 说明:<https://dataxapi.com/zh/docs/sdks/>。
97
+ - 从 0.2.x 升级:旧名称仍可用,默认超时由 10 秒改为 60 秒,重试规则有调整,见源码包中的 `CHANGELOG.md` 和 <https://dataxapi.com/zh/docs/sdks/#upgrade>。
98
+
99
+ ## License
100
+
101
+ MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dataxapi"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  requires-python = ">=3.11"
5
5
  description = "DataXAPI Python SDK: exact public social data endpoints with one CNY balance"
6
6
  readme = "README.md"
@@ -15,7 +15,9 @@ classifiers = [
15
15
 
16
16
  [project.urls]
17
17
  Homepage = "https://dataxapi.com"
18
- Documentation = "https://dataxapi.com/en/apis/"
18
+ Documentation = "https://dataxapi.com/en/docs/"
19
+ "API catalog" = "https://dataxapi.com/en/apis/"
20
+ Changelog = "https://dataxapi.com/en/docs/sdks/#upgrade"
19
21
 
20
22
  [build-system]
21
23
  requires = ["setuptools>=77"]
@@ -0,0 +1,4 @@
1
+ # Generated by scripts/generate-exact-api-sdks.mjs. Do not edit.
2
+ from .client import DataXClient, DataXConnectionError, DataXError, DataXPaginationError, ExactApiClient, ExactApiConnectionError, ExactApiError, ExactApiPaginationError
3
+ from .generated import Problem, Usage, OperationId, OperationInput, OperationResponse, OPERATIONS, USAGE_OPERATION, EXAMPLES, XPostsSearchV1Input, XPostsSearchV1Response, XPostsDetailV1Input, XPostsDetailV1Response, XUsersProfileV1Input, XUsersProfileV1Response, XUsersPostsV1Input, XUsersPostsV1Response, RedditPostsDetailV1Input, RedditPostsDetailV1Response, RedditPostsSearchV1Input, RedditPostsSearchV1Response, RedditPostsCommentsV1Input, RedditPostsCommentsV1Response, XiaohongshuPgyNotesDetailV1Input, XiaohongshuPgyNotesDetailV1Response, XiaohongshuPgyBloggersDetailV1Input, XiaohongshuPgyBloggersDetailV1Response, XiaohongshuPgyBloggersNotesV1Input, XiaohongshuPgyBloggersNotesV1Response, XiaohongshuPgyBloggersCoredataV1Input, XiaohongshuPgyBloggersCoredataV1Response, XiaohongshuPgyBloggersDatasummaryV1Input, XiaohongshuPgyBloggersDatasummaryV1Response, XiaohongshuNotesDetailV2Input, XiaohongshuNotesDetailV2Response, XiaohongshuNotesSearchV1Input, XiaohongshuNotesSearchV1Response, DouyinVideosDetailV2Input, DouyinVideosDetailV2Response, DouyinVideosSearchV1Input, DouyinVideosSearchV1Response, XPostsRepliesV1Input, XPostsRepliesV1Response, XPostsQuotesV1Input, XPostsQuotesV1Response, XUsersSearchV1Input, XUsersSearchV1Response, XUsersFollowersV1Input, XUsersFollowersV1Response, XUsersFollowingV1Input, XUsersFollowingV1Response, XiaohongshuNotesCommentsV1Input, XiaohongshuNotesCommentsV1Response, XiaohongshuNotesCommentsRepliesV1Input, XiaohongshuNotesCommentsRepliesV1Response, XiaohongshuUsersProfileV1Input, XiaohongshuUsersProfileV1Response, XiaohongshuUsersNotesV1Input, XiaohongshuUsersNotesV1Response, XiaohongshuUsersSearchV1Input, XiaohongshuUsersSearchV1Response, XiaohongshuPgyBloggersPerformanceV1Input, XiaohongshuPgyBloggersPerformanceV1Response, XiaohongshuPgyBloggersFansSummaryV1Input, XiaohongshuPgyBloggersFansSummaryV1Response, XiaohongshuPgyBloggersFansProfileV1Input, XiaohongshuPgyBloggersFansProfileV1Response, XiaohongshuPgyBloggersFansHistoryV1Input, XiaohongshuPgyBloggersFansHistoryV1Response, XiaohongshuPgyBloggersSearchV1Input, XiaohongshuPgyBloggersSearchV1Response, DouyinVideosCommentsV1Input, DouyinVideosCommentsV1Response, DouyinVideosCommentsRepliesV1Input, DouyinVideosCommentsRepliesV1Response, DouyinUsersProfileV1Input, DouyinUsersProfileV1Response, DouyinUsersVideosV2Input, DouyinUsersVideosV2Response, DouyinUsersSearchV1Input, DouyinUsersSearchV1Response, DouyinTrendsHotsearchV1Input, DouyinTrendsHotsearchV1Response, DouyinTrendsRisingV1Input, DouyinTrendsRisingV1Response, DouyinRankingsLowfanV1Input, DouyinRankingsLowfanV1Response, DouyinRankingsHighcompletionV1Input, DouyinRankingsHighcompletionV1Response, DouyinMixesDetailV1Input, DouyinMixesDetailV1Response, DouyinMixesVideosV1Input, DouyinMixesVideosV1Response, XiaohongshuTopicsDetailV1Input, XiaohongshuTopicsDetailV1Response, XiaohongshuTopicsNotesV1Input, XiaohongshuTopicsNotesV1Response, XiaohongshuProductsSearchV1Input, XiaohongshuProductsSearchV1Response, XiaohongshuProductsDetailV1Input, XiaohongshuProductsDetailV1Response, XPostsRetweetersV1Input, XPostsRetweetersV1Response, XTrendsListV1Input, XTrendsListV1Response, XUsersRepliesV1Input, XUsersRepliesV1Response, XUsersMediaV1Input, XUsersMediaV1Response, RedditPostsBatchV1Input, RedditPostsBatchV1Response, RedditSubredditsProfileV1Input, RedditSubredditsProfileV1Response, RedditSubredditsPostsV1Input, RedditSubredditsPostsV1Response, RedditUsersProfileV1Input, RedditUsersProfileV1Response, RedditSearchTrendingV1Input, RedditSearchTrendingV1Response, XPostsThreadContextV1Input, XPostsThreadContextV1Response, XArticlesDetailV1Input, XArticlesDetailV1Response, XUsersMentionsV1Input, XUsersMentionsV1Response, XUsersBatchDetailV1Input, XUsersBatchDetailV1Response, XUsersVerifiedFollowersV1Input, XUsersVerifiedFollowersV1Response, RedditUsersPostsV1Input, RedditUsersPostsV1Response, XiaohongshuProductsReviewsV1Input, XiaohongshuProductsReviewsV1Response, XiaohongshuCreatorInspirationRecommendedV1Input, XiaohongshuCreatorInspirationRecommendedV1Response, XiaohongshuCreatorInspirationHotV1Input, XiaohongshuCreatorInspirationHotV1Response, DouyinVideosBatchDetailV1Input, DouyinVideosBatchDetailV1Response, DouyinVideosRelatedV1Input, DouyinVideosRelatedV1Response, DouyinSearchGeneralV1Input, DouyinSearchGeneralV1Response, DouyinMusicDetailV1Input, DouyinMusicDetailV1Response, DouyinMusicVideosV1Input, DouyinMusicVideosV1Response, DouyinRankingsHighlikeV1Input, DouyinRankingsHighlikeV1Response, DouyinRankingsHighfanV1Input, DouyinRankingsHighfanV1Response, XListsTweetsV1Input, XListsTweetsV1Response, XUsersFollowerIdsV1Input, XUsersFollowerIdsV1Response, XCommunitiesMembersV1Input, XCommunitiesMembersV1Response, XCommunitiesDetailV1Input, XCommunitiesDetailV1Response, XCommunitiesTweetsV1Input, XCommunitiesTweetsV1Response, RedditSearchSuggestionsV1Input, RedditSearchSuggestionsV1Response, RedditSubredditsHighlightsV1Input, RedditSubredditsHighlightsV1Response, RedditSubredditsPostFlairsV1Input, RedditSubredditsPostFlairsV1Response, RedditUsersCommentsV1Input, RedditUsersCommentsV1Response, RedditUsersActiveSubredditsV1Input, RedditUsersActiveSubredditsV1Response, XiaohongshuUsersFavedNotesV1Input, XiaohongshuUsersFavedNotesV1Response, XiaohongshuImagesSearchV1Input, XiaohongshuImagesSearchV1Response, DouyinHashtagsDetailV1Input, DouyinHashtagsDetailV1Response, DouyinHashtagsVideosV1Input, DouyinHashtagsVideosV1Response, DouyinCreatorsSimilarV1Input, DouyinCreatorsSimilarV1Response, DouyinCreatorsTrendsCompareV1Input, DouyinCreatorsTrendsCompareV1Response, DouyinXingtuCreatorsSearchV1Input, DouyinXingtuCreatorsSearchV1Response, DouyinRankingsTopicsV1Input, DouyinRankingsTopicsV1Response, DouyinRankingsSearchesV1Input, DouyinRankingsSearchesV1Response, DouyinXingtuCreatorsBatchDetailV1Input, DouyinXingtuCreatorsBatchDetailV1Response, DouyinXingtuCreatorsAudienceV1Input, DouyinXingtuCreatorsAudienceV1Response, DouyinXingtuCreatorsVideosV1Input, DouyinXingtuCreatorsVideosV1Response, TiktokUsersProfileV1Input, TiktokUsersProfileV1Response, TiktokUsersVideosV1Input, TiktokUsersVideosV1Response, TiktokVideosDetailV1Input, TiktokVideosDetailV1Response, TiktokVideosCommentsV1Input, TiktokVideosCommentsV1Response, TiktokVideosCommentsRepliesV1Input, TiktokVideosCommentsRepliesV1Response, TiktokVideosSearchV1Input, TiktokVideosSearchV1Response, TiktokUsersSearchV1Input, TiktokUsersSearchV1Response, TiktokHashtagsDetailV1Input, TiktokHashtagsDetailV1Response, TiktokHashtagsVideosV1Input, TiktokHashtagsVideosV1Response, TiktokShopProductsSearchV1Input, TiktokShopProductsSearchV1Response, TiktokShopProductsReviewsV1Input, TiktokShopProductsReviewsV1Response, TiktokShopSellersProductsV1Input, TiktokShopSellersProductsV1Response, TiktokAdsSearchV1Input, TiktokAdsSearchV1Response, TiktokAdsDetailV1Input, TiktokAdsDetailV1Response, TiktokAdsSpotlightV1Input, TiktokAdsSpotlightV1Response, TiktokAdsPercentileV1Input, TiktokAdsPercentileV1Response, TiktokTrendsHashtagsV1Input, TiktokTrendsHashtagsV1Response, InstagramUsersProfileV1Input, InstagramUsersProfileV1Response, InstagramUsersPostsV1Input, InstagramUsersPostsV1Response, InstagramUsersReelsV1Input, InstagramUsersReelsV1Response, InstagramPostsDetailV1Input, InstagramPostsDetailV1Response, InstagramPostsCommentsV1Input, InstagramPostsCommentsV1Response, InstagramPostsCommentsRepliesV1Input, InstagramPostsCommentsRepliesV1Response, InstagramUsersSearchV1Input, InstagramUsersSearchV1Response, InstagramHashtagsPostsV1Input, InstagramHashtagsPostsV1Response, YoutubeVideosDetailV1Input, YoutubeVideosDetailV1Response, YoutubeVideosCommentsV1Input, YoutubeVideosCommentsV1Response, YoutubeChannelsProfileV1Input, YoutubeChannelsProfileV1Response, YoutubeChannelsVideosV1Input, YoutubeChannelsVideosV1Response, YoutubeChannelsShortsV1Input, YoutubeChannelsShortsV1Response, YoutubeVideosSearchV1Input, YoutubeVideosSearchV1Response, YoutubeChannelsSearchV1Input, YoutubeChannelsSearchV1Response, YoutubeVideosRelatedV1Input, YoutubeVideosRelatedV1Response, YoutubeVideosCaptionsV1Input, YoutubeVideosCaptionsV1Response, BilibiliVideosDetailV1Input, BilibiliVideosDetailV1Response, BilibiliVideosCommentsV1Input, BilibiliVideosCommentsV1Response, BilibiliVideosCommentsRepliesV1Input, BilibiliVideosCommentsRepliesV1Response, BilibiliUsersProfileV1Input, BilibiliUsersProfileV1Response, BilibiliUsersStatsV1Input, BilibiliUsersStatsV1Response, BilibiliUsersVideosV1Input, BilibiliUsersVideosV1Response, BilibiliVideosSearchV1Input, BilibiliVideosSearchV1Response, BilibiliVideosPopularV1Input, BilibiliVideosPopularV1Response, BilibiliVideosSubtitlesV1Input, BilibiliVideosSubtitlesV1Response, BilibiliHuahuoCreatorsSearchV1Input, BilibiliHuahuoCreatorsSearchV1Response, BilibiliHuahuoCreatorsDetailV1Input, BilibiliHuahuoCreatorsDetailV1Response, BilibiliHuahuoCreatorsFollowersTrendV1Input, BilibiliHuahuoCreatorsFollowersTrendV1Response, BilibiliHuahuoCreatorsVideosTrendV1Input, BilibiliHuahuoCreatorsVideosTrendV1Response, BilibiliHuahuoCreatorsVideosPerformanceV1Input, BilibiliHuahuoCreatorsVideosPerformanceV1Response, BilibiliHuahuoCreatorsSimilarV1Input, BilibiliHuahuoCreatorsSimilarV1Response, BilibiliHuahuoCreatorsShowcaseV1Input, BilibiliHuahuoCreatorsShowcaseV1Response, BilibiliHuahuoRankingsCreatorsV1Input, BilibiliHuahuoRankingsCreatorsV1Response, BilibiliHuahuoRankingsVideosV1Input, BilibiliHuahuoRankingsVideosV1Response, BilibiliHuahuoRankingsSeedingV1Input, BilibiliHuahuoRankingsSeedingV1Response, BilibiliHuahuoRankingsGoodsV1Input, BilibiliHuahuoRankingsGoodsV1Response, BilibiliHuahuoGoodsCreatorsV1Input, BilibiliHuahuoGoodsCreatorsV1Response, WeiboPostsDetailV1Input, WeiboPostsDetailV1Response, WeiboUsersProfileV1Input, WeiboUsersProfileV1Response, WeiboUsersPostsV1Input, WeiboUsersPostsV1Response, WeiboPostsCommentsV1Input, WeiboPostsCommentsV1Response, WeiboCommentsRepliesV1Input, WeiboCommentsRepliesV1Response, WeiboPostsSearchV1Input, WeiboPostsSearchV1Response, WeiboPostsHotV1Input, WeiboPostsHotV1Response, WeiboTrendsHotsearchV1Input, WeiboTrendsHotsearchV1Response, KuaishouVideosDetailV1Input, KuaishouVideosDetailV1Response, KuaishouVideosCommentsV1Input, KuaishouVideosCommentsV1Response, KuaishouVideosCommentsRepliesV1Input, KuaishouVideosCommentsRepliesV1Response, KuaishouUsersProfileV1Input, KuaishouUsersProfileV1Response, KuaishouUsersVideosV1Input, KuaishouUsersVideosV1Response, KuaishouVideosSearchV2Input, KuaishouVideosSearchV2Response, KuaishouUsersSearchV1Input, KuaishouUsersSearchV1Response, KuaishouTrendsHotV1Input, KuaishouTrendsHotV1Response, ZhihuTrendsHotV1Input, ZhihuTrendsHotV1Response, ZhihuSearchContentV1Input, ZhihuSearchContentV1Response, ZhihuUsersSearchV1Input, ZhihuUsersSearchV1Response, ZhihuQuestionsDetailV1Input, ZhihuQuestionsDetailV1Response, ZhihuQuestionsAnswersV1Input, ZhihuQuestionsAnswersV1Response, ZhihuAnswersDetailV1Input, ZhihuAnswersDetailV1Response, ZhihuAnswersCommentsV1Input, ZhihuAnswersCommentsV1Response, ZhihuCommentsRepliesV1Input, ZhihuCommentsRepliesV1Response, ZhihuUsersProfileV1Input, ZhihuUsersProfileV1Response, DouyinIndexHottopicsV1Input, DouyinIndexHottopicsV1Response, DouyinIndexVideosSuggestV1Input, DouyinIndexVideosSuggestV1Response, DouyinIndexCreatorsMetricsV1Input, DouyinIndexCreatorsMetricsV1Response, DouyinIndexCreatorsAudienceV1Input, DouyinIndexCreatorsAudienceV1Response, DouyinIndexCreatorsTopvideosV1Input, DouyinIndexCreatorsTopvideosV1Response, DouyinIndexKeywordsAudienceV1Input, DouyinIndexKeywordsAudienceV1Response, DouyinIndexKeywordsTrendsV1Input, DouyinIndexKeywordsTrendsV1Response, DouyinIndexKeywordsInterpretationV1Input, DouyinIndexKeywordsInterpretationV1Response, DouyinXingtuCreatorsDetailV1Input, DouyinXingtuCreatorsDetailV1Response, DouyinXingtuCreatorsPricesV1Input, DouyinXingtuCreatorsPricesV1Response, DouyinXingtuCreatorsContentkeywordsV1Input, DouyinXingtuCreatorsContentkeywordsV1Response, DouyinXingtuCreatorsSpreadV1Input, DouyinXingtuCreatorsSpreadV1Response, DouyinXingtuCreatorsConversionV1Input, DouyinXingtuCreatorsConversionV1Response, DouyinXingtuCreatorsLinkindexV1Input, DouyinXingtuCreatorsLinkindexV1Response, DouyinXingtuCreatorsContentmixV1Input, DouyinXingtuCreatorsContentmixV1Response, DouyinXingtuRankingsCatalogV1Input, DouyinXingtuRankingsCatalogV1Response, DouyinXingtuRankingsCreatorsV1Input, DouyinXingtuRankingsCreatorsV1Response, DouyinXingtuVideosStatsV1Input, DouyinXingtuVideosStatsV1Response, DouyinXingtuCampaignsV1Input, DouyinXingtuCampaignsV1Response, WechatMpAccountsProfileV1Input, WechatMpAccountsProfileV1Response, WechatMpAccountsArticlesV1Input, WechatMpAccountsArticlesV1Response, WechatMpArticlesDetailV1Input, WechatMpArticlesDetailV1Response, WechatMpArticlesStatsV1Input, WechatMpArticlesStatsV1Response, WechatChannelsUsersProfileV1Input, WechatChannelsUsersProfileV1Response, WechatChannelsUsersVideosV1Input, WechatChannelsUsersVideosV1Response, WechatChannelsVideosDetailV1Input, WechatChannelsVideosDetailV1Response, WechatChannelsVideosCommentsV1Input, WechatChannelsVideosCommentsV1Response, WechatChannelsUsersCollectionsV1Input, WechatChannelsUsersCollectionsV1Response, WechatChannelsCollectionsVideosV1Input, WechatChannelsCollectionsVideosV1Response, WechatChannelsUsersSearchV1Input, WechatChannelsUsersSearchV1Response, WechatSearchGeneralV1Input, WechatSearchGeneralV1Response, WechatSearchVideosV1Input, WechatSearchVideosV1Response, ToutiaoArticlesDetailV1Input, ToutiaoArticlesDetailV1Response, ToutiaoVideosDetailV1Input, ToutiaoVideosDetailV1Response, ToutiaoPostsCommentsV1Input, ToutiaoPostsCommentsV1Response, XiguaVideosSearchV1Input, XiguaVideosSearchV1Response, XiguaVideosCommentsV1Input, XiguaVideosCommentsV1Response, XiguaUsersProfileV1Input, XiguaUsersProfileV1Response, XiguaUsersVideosV1Input, XiguaUsersVideosV1Response, ThreadsUsersProfileV1Input, ThreadsUsersProfileV1Response, ThreadsUsersPostsV1Input, ThreadsUsersPostsV1Response, ThreadsPostsDetailV1Input, ThreadsPostsDetailV1Response, ThreadsPostsSearchV1Input, ThreadsPostsSearchV1Response, LinkedinCompaniesProfileV1Input, LinkedinCompaniesProfileV1Response, LinkedinCompaniesPostsV1Input, LinkedinCompaniesPostsV1Response, LinkedinPostsDetailV1Input, LinkedinPostsDetailV1Response, LinkedinPostsCommentsV1Input, LinkedinPostsCommentsV1Response, LinkedinJobsSearchV1Input, LinkedinJobsSearchV1Response, LinkedinJobsDetailV1Input, LinkedinJobsDetailV1Response, Lemon8UsersProfileV1Input, Lemon8UsersProfileV1Response, Lemon8PostsDetailV1Input, Lemon8PostsDetailV1Response, Lemon8PostsCommentsV1Input, Lemon8PostsCommentsV1Response, Lemon8TrendsHotV1Input, Lemon8TrendsHotV1Response, Lemon8TopicsDetailV1Input, Lemon8TopicsDetailV1Response, Lemon8TopicsPostsV1Input, Lemon8TopicsPostsV1Response, TelegramChannelsProfileV1Input, TelegramChannelsProfileV1Response, TelegramChannelsBatchV1Input, TelegramChannelsBatchV1Response, TelegramChannelsMessagesV1Input, TelegramChannelsMessagesV1Response, TelegramMessagesDetailV1Input, TelegramMessagesDetailV1Response, TelegramChannelsMessagesSearchV1Input, TelegramChannelsMessagesSearchV1Response
4
+ __all__ = ["DataXClient", "DataXConnectionError", "DataXError", "DataXPaginationError", "ExactApiClient", "ExactApiConnectionError", "ExactApiError", "ExactApiPaginationError", "Problem", "Usage", "OperationId", "OperationInput", "OperationResponse", "OPERATIONS", "USAGE_OPERATION", "EXAMPLES", "XPostsSearchV1Input", "XPostsSearchV1Response", "XPostsDetailV1Input", "XPostsDetailV1Response", "XUsersProfileV1Input", "XUsersProfileV1Response", "XUsersPostsV1Input", "XUsersPostsV1Response", "RedditPostsDetailV1Input", "RedditPostsDetailV1Response", "RedditPostsSearchV1Input", "RedditPostsSearchV1Response", "RedditPostsCommentsV1Input", "RedditPostsCommentsV1Response", "XiaohongshuPgyNotesDetailV1Input", "XiaohongshuPgyNotesDetailV1Response", "XiaohongshuPgyBloggersDetailV1Input", "XiaohongshuPgyBloggersDetailV1Response", "XiaohongshuPgyBloggersNotesV1Input", "XiaohongshuPgyBloggersNotesV1Response", "XiaohongshuPgyBloggersCoredataV1Input", "XiaohongshuPgyBloggersCoredataV1Response", "XiaohongshuPgyBloggersDatasummaryV1Input", "XiaohongshuPgyBloggersDatasummaryV1Response", "XiaohongshuNotesDetailV2Input", "XiaohongshuNotesDetailV2Response", "XiaohongshuNotesSearchV1Input", "XiaohongshuNotesSearchV1Response", "DouyinVideosDetailV2Input", "DouyinVideosDetailV2Response", "DouyinVideosSearchV1Input", "DouyinVideosSearchV1Response", "XPostsRepliesV1Input", "XPostsRepliesV1Response", "XPostsQuotesV1Input", "XPostsQuotesV1Response", "XUsersSearchV1Input", "XUsersSearchV1Response", "XUsersFollowersV1Input", "XUsersFollowersV1Response", "XUsersFollowingV1Input", "XUsersFollowingV1Response", "XiaohongshuNotesCommentsV1Input", "XiaohongshuNotesCommentsV1Response", "XiaohongshuNotesCommentsRepliesV1Input", "XiaohongshuNotesCommentsRepliesV1Response", "XiaohongshuUsersProfileV1Input", "XiaohongshuUsersProfileV1Response", "XiaohongshuUsersNotesV1Input", "XiaohongshuUsersNotesV1Response", "XiaohongshuUsersSearchV1Input", "XiaohongshuUsersSearchV1Response", "XiaohongshuPgyBloggersPerformanceV1Input", "XiaohongshuPgyBloggersPerformanceV1Response", "XiaohongshuPgyBloggersFansSummaryV1Input", "XiaohongshuPgyBloggersFansSummaryV1Response", "XiaohongshuPgyBloggersFansProfileV1Input", "XiaohongshuPgyBloggersFansProfileV1Response", "XiaohongshuPgyBloggersFansHistoryV1Input", "XiaohongshuPgyBloggersFansHistoryV1Response", "XiaohongshuPgyBloggersSearchV1Input", "XiaohongshuPgyBloggersSearchV1Response", "DouyinVideosCommentsV1Input", "DouyinVideosCommentsV1Response", "DouyinVideosCommentsRepliesV1Input", "DouyinVideosCommentsRepliesV1Response", "DouyinUsersProfileV1Input", "DouyinUsersProfileV1Response", "DouyinUsersVideosV2Input", "DouyinUsersVideosV2Response", "DouyinUsersSearchV1Input", "DouyinUsersSearchV1Response", "DouyinTrendsHotsearchV1Input", "DouyinTrendsHotsearchV1Response", "DouyinTrendsRisingV1Input", "DouyinTrendsRisingV1Response", "DouyinRankingsLowfanV1Input", "DouyinRankingsLowfanV1Response", "DouyinRankingsHighcompletionV1Input", "DouyinRankingsHighcompletionV1Response", "DouyinMixesDetailV1Input", "DouyinMixesDetailV1Response", "DouyinMixesVideosV1Input", "DouyinMixesVideosV1Response", "XiaohongshuTopicsDetailV1Input", "XiaohongshuTopicsDetailV1Response", "XiaohongshuTopicsNotesV1Input", "XiaohongshuTopicsNotesV1Response", "XiaohongshuProductsSearchV1Input", "XiaohongshuProductsSearchV1Response", "XiaohongshuProductsDetailV1Input", "XiaohongshuProductsDetailV1Response", "XPostsRetweetersV1Input", "XPostsRetweetersV1Response", "XTrendsListV1Input", "XTrendsListV1Response", "XUsersRepliesV1Input", "XUsersRepliesV1Response", "XUsersMediaV1Input", "XUsersMediaV1Response", "RedditPostsBatchV1Input", "RedditPostsBatchV1Response", "RedditSubredditsProfileV1Input", "RedditSubredditsProfileV1Response", "RedditSubredditsPostsV1Input", "RedditSubredditsPostsV1Response", "RedditUsersProfileV1Input", "RedditUsersProfileV1Response", "RedditSearchTrendingV1Input", "RedditSearchTrendingV1Response", "XPostsThreadContextV1Input", "XPostsThreadContextV1Response", "XArticlesDetailV1Input", "XArticlesDetailV1Response", "XUsersMentionsV1Input", "XUsersMentionsV1Response", "XUsersBatchDetailV1Input", "XUsersBatchDetailV1Response", "XUsersVerifiedFollowersV1Input", "XUsersVerifiedFollowersV1Response", "RedditUsersPostsV1Input", "RedditUsersPostsV1Response", "XiaohongshuProductsReviewsV1Input", "XiaohongshuProductsReviewsV1Response", "XiaohongshuCreatorInspirationRecommendedV1Input", "XiaohongshuCreatorInspirationRecommendedV1Response", "XiaohongshuCreatorInspirationHotV1Input", "XiaohongshuCreatorInspirationHotV1Response", "DouyinVideosBatchDetailV1Input", "DouyinVideosBatchDetailV1Response", "DouyinVideosRelatedV1Input", "DouyinVideosRelatedV1Response", "DouyinSearchGeneralV1Input", "DouyinSearchGeneralV1Response", "DouyinMusicDetailV1Input", "DouyinMusicDetailV1Response", "DouyinMusicVideosV1Input", "DouyinMusicVideosV1Response", "DouyinRankingsHighlikeV1Input", "DouyinRankingsHighlikeV1Response", "DouyinRankingsHighfanV1Input", "DouyinRankingsHighfanV1Response", "XListsTweetsV1Input", "XListsTweetsV1Response", "XUsersFollowerIdsV1Input", "XUsersFollowerIdsV1Response", "XCommunitiesMembersV1Input", "XCommunitiesMembersV1Response", "XCommunitiesDetailV1Input", "XCommunitiesDetailV1Response", "XCommunitiesTweetsV1Input", "XCommunitiesTweetsV1Response", "RedditSearchSuggestionsV1Input", "RedditSearchSuggestionsV1Response", "RedditSubredditsHighlightsV1Input", "RedditSubredditsHighlightsV1Response", "RedditSubredditsPostFlairsV1Input", "RedditSubredditsPostFlairsV1Response", "RedditUsersCommentsV1Input", "RedditUsersCommentsV1Response", "RedditUsersActiveSubredditsV1Input", "RedditUsersActiveSubredditsV1Response", "XiaohongshuUsersFavedNotesV1Input", "XiaohongshuUsersFavedNotesV1Response", "XiaohongshuImagesSearchV1Input", "XiaohongshuImagesSearchV1Response", "DouyinHashtagsDetailV1Input", "DouyinHashtagsDetailV1Response", "DouyinHashtagsVideosV1Input", "DouyinHashtagsVideosV1Response", "DouyinCreatorsSimilarV1Input", "DouyinCreatorsSimilarV1Response", "DouyinCreatorsTrendsCompareV1Input", "DouyinCreatorsTrendsCompareV1Response", "DouyinXingtuCreatorsSearchV1Input", "DouyinXingtuCreatorsSearchV1Response", "DouyinRankingsTopicsV1Input", "DouyinRankingsTopicsV1Response", "DouyinRankingsSearchesV1Input", "DouyinRankingsSearchesV1Response", "DouyinXingtuCreatorsBatchDetailV1Input", "DouyinXingtuCreatorsBatchDetailV1Response", "DouyinXingtuCreatorsAudienceV1Input", "DouyinXingtuCreatorsAudienceV1Response", "DouyinXingtuCreatorsVideosV1Input", "DouyinXingtuCreatorsVideosV1Response", "TiktokUsersProfileV1Input", "TiktokUsersProfileV1Response", "TiktokUsersVideosV1Input", "TiktokUsersVideosV1Response", "TiktokVideosDetailV1Input", "TiktokVideosDetailV1Response", "TiktokVideosCommentsV1Input", "TiktokVideosCommentsV1Response", "TiktokVideosCommentsRepliesV1Input", "TiktokVideosCommentsRepliesV1Response", "TiktokVideosSearchV1Input", "TiktokVideosSearchV1Response", "TiktokUsersSearchV1Input", "TiktokUsersSearchV1Response", "TiktokHashtagsDetailV1Input", "TiktokHashtagsDetailV1Response", "TiktokHashtagsVideosV1Input", "TiktokHashtagsVideosV1Response", "TiktokShopProductsSearchV1Input", "TiktokShopProductsSearchV1Response", "TiktokShopProductsReviewsV1Input", "TiktokShopProductsReviewsV1Response", "TiktokShopSellersProductsV1Input", "TiktokShopSellersProductsV1Response", "TiktokAdsSearchV1Input", "TiktokAdsSearchV1Response", "TiktokAdsDetailV1Input", "TiktokAdsDetailV1Response", "TiktokAdsSpotlightV1Input", "TiktokAdsSpotlightV1Response", "TiktokAdsPercentileV1Input", "TiktokAdsPercentileV1Response", "TiktokTrendsHashtagsV1Input", "TiktokTrendsHashtagsV1Response", "InstagramUsersProfileV1Input", "InstagramUsersProfileV1Response", "InstagramUsersPostsV1Input", "InstagramUsersPostsV1Response", "InstagramUsersReelsV1Input", "InstagramUsersReelsV1Response", "InstagramPostsDetailV1Input", "InstagramPostsDetailV1Response", "InstagramPostsCommentsV1Input", "InstagramPostsCommentsV1Response", "InstagramPostsCommentsRepliesV1Input", "InstagramPostsCommentsRepliesV1Response", "InstagramUsersSearchV1Input", "InstagramUsersSearchV1Response", "InstagramHashtagsPostsV1Input", "InstagramHashtagsPostsV1Response", "YoutubeVideosDetailV1Input", "YoutubeVideosDetailV1Response", "YoutubeVideosCommentsV1Input", "YoutubeVideosCommentsV1Response", "YoutubeChannelsProfileV1Input", "YoutubeChannelsProfileV1Response", "YoutubeChannelsVideosV1Input", "YoutubeChannelsVideosV1Response", "YoutubeChannelsShortsV1Input", "YoutubeChannelsShortsV1Response", "YoutubeVideosSearchV1Input", "YoutubeVideosSearchV1Response", "YoutubeChannelsSearchV1Input", "YoutubeChannelsSearchV1Response", "YoutubeVideosRelatedV1Input", "YoutubeVideosRelatedV1Response", "YoutubeVideosCaptionsV1Input", "YoutubeVideosCaptionsV1Response", "BilibiliVideosDetailV1Input", "BilibiliVideosDetailV1Response", "BilibiliVideosCommentsV1Input", "BilibiliVideosCommentsV1Response", "BilibiliVideosCommentsRepliesV1Input", "BilibiliVideosCommentsRepliesV1Response", "BilibiliUsersProfileV1Input", "BilibiliUsersProfileV1Response", "BilibiliUsersStatsV1Input", "BilibiliUsersStatsV1Response", "BilibiliUsersVideosV1Input", "BilibiliUsersVideosV1Response", "BilibiliVideosSearchV1Input", "BilibiliVideosSearchV1Response", "BilibiliVideosPopularV1Input", "BilibiliVideosPopularV1Response", "BilibiliVideosSubtitlesV1Input", "BilibiliVideosSubtitlesV1Response", "BilibiliHuahuoCreatorsSearchV1Input", "BilibiliHuahuoCreatorsSearchV1Response", "BilibiliHuahuoCreatorsDetailV1Input", "BilibiliHuahuoCreatorsDetailV1Response", "BilibiliHuahuoCreatorsFollowersTrendV1Input", "BilibiliHuahuoCreatorsFollowersTrendV1Response", "BilibiliHuahuoCreatorsVideosTrendV1Input", "BilibiliHuahuoCreatorsVideosTrendV1Response", "BilibiliHuahuoCreatorsVideosPerformanceV1Input", "BilibiliHuahuoCreatorsVideosPerformanceV1Response", "BilibiliHuahuoCreatorsSimilarV1Input", "BilibiliHuahuoCreatorsSimilarV1Response", "BilibiliHuahuoCreatorsShowcaseV1Input", "BilibiliHuahuoCreatorsShowcaseV1Response", "BilibiliHuahuoRankingsCreatorsV1Input", "BilibiliHuahuoRankingsCreatorsV1Response", "BilibiliHuahuoRankingsVideosV1Input", "BilibiliHuahuoRankingsVideosV1Response", "BilibiliHuahuoRankingsSeedingV1Input", "BilibiliHuahuoRankingsSeedingV1Response", "BilibiliHuahuoRankingsGoodsV1Input", "BilibiliHuahuoRankingsGoodsV1Response", "BilibiliHuahuoGoodsCreatorsV1Input", "BilibiliHuahuoGoodsCreatorsV1Response", "WeiboPostsDetailV1Input", "WeiboPostsDetailV1Response", "WeiboUsersProfileV1Input", "WeiboUsersProfileV1Response", "WeiboUsersPostsV1Input", "WeiboUsersPostsV1Response", "WeiboPostsCommentsV1Input", "WeiboPostsCommentsV1Response", "WeiboCommentsRepliesV1Input", "WeiboCommentsRepliesV1Response", "WeiboPostsSearchV1Input", "WeiboPostsSearchV1Response", "WeiboPostsHotV1Input", "WeiboPostsHotV1Response", "WeiboTrendsHotsearchV1Input", "WeiboTrendsHotsearchV1Response", "KuaishouVideosDetailV1Input", "KuaishouVideosDetailV1Response", "KuaishouVideosCommentsV1Input", "KuaishouVideosCommentsV1Response", "KuaishouVideosCommentsRepliesV1Input", "KuaishouVideosCommentsRepliesV1Response", "KuaishouUsersProfileV1Input", "KuaishouUsersProfileV1Response", "KuaishouUsersVideosV1Input", "KuaishouUsersVideosV1Response", "KuaishouVideosSearchV2Input", "KuaishouVideosSearchV2Response", "KuaishouUsersSearchV1Input", "KuaishouUsersSearchV1Response", "KuaishouTrendsHotV1Input", "KuaishouTrendsHotV1Response", "ZhihuTrendsHotV1Input", "ZhihuTrendsHotV1Response", "ZhihuSearchContentV1Input", "ZhihuSearchContentV1Response", "ZhihuUsersSearchV1Input", "ZhihuUsersSearchV1Response", "ZhihuQuestionsDetailV1Input", "ZhihuQuestionsDetailV1Response", "ZhihuQuestionsAnswersV1Input", "ZhihuQuestionsAnswersV1Response", "ZhihuAnswersDetailV1Input", "ZhihuAnswersDetailV1Response", "ZhihuAnswersCommentsV1Input", "ZhihuAnswersCommentsV1Response", "ZhihuCommentsRepliesV1Input", "ZhihuCommentsRepliesV1Response", "ZhihuUsersProfileV1Input", "ZhihuUsersProfileV1Response", "DouyinIndexHottopicsV1Input", "DouyinIndexHottopicsV1Response", "DouyinIndexVideosSuggestV1Input", "DouyinIndexVideosSuggestV1Response", "DouyinIndexCreatorsMetricsV1Input", "DouyinIndexCreatorsMetricsV1Response", "DouyinIndexCreatorsAudienceV1Input", "DouyinIndexCreatorsAudienceV1Response", "DouyinIndexCreatorsTopvideosV1Input", "DouyinIndexCreatorsTopvideosV1Response", "DouyinIndexKeywordsAudienceV1Input", "DouyinIndexKeywordsAudienceV1Response", "DouyinIndexKeywordsTrendsV1Input", "DouyinIndexKeywordsTrendsV1Response", "DouyinIndexKeywordsInterpretationV1Input", "DouyinIndexKeywordsInterpretationV1Response", "DouyinXingtuCreatorsDetailV1Input", "DouyinXingtuCreatorsDetailV1Response", "DouyinXingtuCreatorsPricesV1Input", "DouyinXingtuCreatorsPricesV1Response", "DouyinXingtuCreatorsContentkeywordsV1Input", "DouyinXingtuCreatorsContentkeywordsV1Response", "DouyinXingtuCreatorsSpreadV1Input", "DouyinXingtuCreatorsSpreadV1Response", "DouyinXingtuCreatorsConversionV1Input", "DouyinXingtuCreatorsConversionV1Response", "DouyinXingtuCreatorsLinkindexV1Input", "DouyinXingtuCreatorsLinkindexV1Response", "DouyinXingtuCreatorsContentmixV1Input", "DouyinXingtuCreatorsContentmixV1Response", "DouyinXingtuRankingsCatalogV1Input", "DouyinXingtuRankingsCatalogV1Response", "DouyinXingtuRankingsCreatorsV1Input", "DouyinXingtuRankingsCreatorsV1Response", "DouyinXingtuVideosStatsV1Input", "DouyinXingtuVideosStatsV1Response", "DouyinXingtuCampaignsV1Input", "DouyinXingtuCampaignsV1Response", "WechatMpAccountsProfileV1Input", "WechatMpAccountsProfileV1Response", "WechatMpAccountsArticlesV1Input", "WechatMpAccountsArticlesV1Response", "WechatMpArticlesDetailV1Input", "WechatMpArticlesDetailV1Response", "WechatMpArticlesStatsV1Input", "WechatMpArticlesStatsV1Response", "WechatChannelsUsersProfileV1Input", "WechatChannelsUsersProfileV1Response", "WechatChannelsUsersVideosV1Input", "WechatChannelsUsersVideosV1Response", "WechatChannelsVideosDetailV1Input", "WechatChannelsVideosDetailV1Response", "WechatChannelsVideosCommentsV1Input", "WechatChannelsVideosCommentsV1Response", "WechatChannelsUsersCollectionsV1Input", "WechatChannelsUsersCollectionsV1Response", "WechatChannelsCollectionsVideosV1Input", "WechatChannelsCollectionsVideosV1Response", "WechatChannelsUsersSearchV1Input", "WechatChannelsUsersSearchV1Response", "WechatSearchGeneralV1Input", "WechatSearchGeneralV1Response", "WechatSearchVideosV1Input", "WechatSearchVideosV1Response", "ToutiaoArticlesDetailV1Input", "ToutiaoArticlesDetailV1Response", "ToutiaoVideosDetailV1Input", "ToutiaoVideosDetailV1Response", "ToutiaoPostsCommentsV1Input", "ToutiaoPostsCommentsV1Response", "XiguaVideosSearchV1Input", "XiguaVideosSearchV1Response", "XiguaVideosCommentsV1Input", "XiguaVideosCommentsV1Response", "XiguaUsersProfileV1Input", "XiguaUsersProfileV1Response", "XiguaUsersVideosV1Input", "XiguaUsersVideosV1Response", "ThreadsUsersProfileV1Input", "ThreadsUsersProfileV1Response", "ThreadsUsersPostsV1Input", "ThreadsUsersPostsV1Response", "ThreadsPostsDetailV1Input", "ThreadsPostsDetailV1Response", "ThreadsPostsSearchV1Input", "ThreadsPostsSearchV1Response", "LinkedinCompaniesProfileV1Input", "LinkedinCompaniesProfileV1Response", "LinkedinCompaniesPostsV1Input", "LinkedinCompaniesPostsV1Response", "LinkedinPostsDetailV1Input", "LinkedinPostsDetailV1Response", "LinkedinPostsCommentsV1Input", "LinkedinPostsCommentsV1Response", "LinkedinJobsSearchV1Input", "LinkedinJobsSearchV1Response", "LinkedinJobsDetailV1Input", "LinkedinJobsDetailV1Response", "Lemon8UsersProfileV1Input", "Lemon8UsersProfileV1Response", "Lemon8PostsDetailV1Input", "Lemon8PostsDetailV1Response", "Lemon8PostsCommentsV1Input", "Lemon8PostsCommentsV1Response", "Lemon8TrendsHotV1Input", "Lemon8TrendsHotV1Response", "Lemon8TopicsDetailV1Input", "Lemon8TopicsDetailV1Response", "Lemon8TopicsPostsV1Input", "Lemon8TopicsPostsV1Response", "TelegramChannelsProfileV1Input", "TelegramChannelsProfileV1Response", "TelegramChannelsBatchV1Input", "TelegramChannelsBatchV1Response", "TelegramChannelsMessagesV1Input", "TelegramChannelsMessagesV1Response", "TelegramMessagesDetailV1Input", "TelegramMessagesDetailV1Response", "TelegramChannelsMessagesSearchV1Input", "TelegramChannelsMessagesSearchV1Response"]
@@ -2,6 +2,7 @@ import json
2
2
  import io
3
3
  import http.client
4
4
  import math
5
+ import random
5
6
  import re
6
7
  import socket
7
8
  import time
@@ -13,9 +14,30 @@ from urllib.parse import quote, urlsplit
13
14
  from .generated import OPERATIONS, USAGE_OPERATION, GeneratedOperationsClient, OperationId, OperationInput, OperationResponse, RedditPostsDetailV1Input, RedditPostsDetailV1Response, Usage, XiaohongshuPgyBloggersCoredataV1Input, XiaohongshuPgyBloggersCoredataV1Response, XiaohongshuPgyBloggersDatasummaryV1Input, XiaohongshuPgyBloggersDatasummaryV1Response, XiaohongshuPgyBloggersDetailV1Input, XiaohongshuPgyBloggersDetailV1Response, XiaohongshuPgyBloggersNotesV1Input, XiaohongshuPgyBloggersNotesV1Response, XiaohongshuPgyNotesDetailV1Input, XiaohongshuPgyNotesDetailV1Response, XPostsDetailV1Input, XPostsDetailV1Response, XPostsSearchV1Input, XPostsSearchV1Response
14
15
 
15
16
 
16
- class ExactApiError(Exception):
17
+ DEFAULT_TIMEOUT = 60.0
18
+ """Seconds per network attempt; above the slowest data-source timeout (30 s)."""
19
+ IN_PROGRESS_WAIT_LIMIT = 90.0
20
+ """Seconds to keep polling a `request_in_progress` call with the same key."""
21
+ NON_RETRYABLE_CODES = frozenset({
22
+ "account_suspended",
23
+ "listing_paused",
24
+ "upstream_failed",
25
+ "upstream_schema_changed",
26
+ "settlement_failed",
27
+ "previous_execution_failed",
28
+ # Not returned by live calls since 2026-10-08 (empty results are a charged
29
+ # 200); kept so older error handling keeps working.
30
+ "no_results",
31
+ "not_found",
32
+ "route_not_found",
33
+ "method_not_allowed",
34
+ })
35
+ """Codes that repeat deterministically, so an automatic retry cannot help."""
36
+
37
+
38
+ class DataXError(Exception):
17
39
  def __init__(self, status: int, problem: dict[str, Any], idempotency_key: str | None = None, retry_after: str | None = None):
18
- super().__init__(problem.get("detail", f"Exact API request failed with HTTP {status}"))
40
+ super().__init__(problem.get("detail", f"DataXAPI request failed with HTTP {status}"))
19
41
  self.status = status
20
42
  self.code = problem.get("code", "unknown_error")
21
43
  self.detail = problem.get("detail", str(self))
@@ -27,14 +49,14 @@ class ExactApiError(Exception):
27
49
  self.idempotency_key = idempotency_key
28
50
 
29
51
 
30
- class ExactApiConnectionError(Exception):
52
+ class DataXConnectionError(Exception):
31
53
  def __init__(self, message: str, idempotency_key: str | None):
32
54
  super().__init__(message)
33
55
  self.idempotency_key = idempotency_key
34
56
 
35
57
 
36
- class ExactApiClient(GeneratedOperationsClient):
37
- def __init__(self, base_url: str, api_key: str, *, timeout: float = 10, max_retries: int = 2):
58
+ class DataXClient(GeneratedOperationsClient):
59
+ def __init__(self, base_url: str, api_key: str, *, timeout: float = DEFAULT_TIMEOUT, max_retries: int = 2):
38
60
  if not base_url or not api_key:
39
61
  raise ValueError("base_url and api_key are required")
40
62
  parsed_base_url = urlsplit(base_url)
@@ -51,7 +73,12 @@ class ExactApiClient(GeneratedOperationsClient):
51
73
  self.usage = _Usage(self)
52
74
 
53
75
  def _call_operation(self, operation_id: OperationId, input: OperationInput, *, idempotency_key: str | None = None) -> OperationResponse:
54
- operation = OPERATIONS[operation_id]
76
+ operation = OPERATIONS.get(operation_id)
77
+ if operation is None:
78
+ raise ValueError(
79
+ f"unknown operation {operation_id!r}; valid ids are the keys of dataxapi.OPERATIONS "
80
+ "(the OpenAPI operationId, e.g. 'x_posts_search_v1'); see https://dataxapi.com/en/docs/sdks/"
81
+ )
55
82
  return cast(OperationResponse, self._execute(operation["method"], operation["path"], dict(input), idempotency_key))
56
83
 
57
84
  def _execute(self, method: str, path: str, body: dict[str, Any], idempotency_key: str | None = None) -> dict[str, Any]:
@@ -67,7 +94,9 @@ class ExactApiClient(GeneratedOperationsClient):
67
94
  if idempotency_key:
68
95
  headers["Idempotency-Key"] = idempotency_key
69
96
 
70
- for attempt in range(self._max_retries + 1):
97
+ attempt = 0
98
+ in_progress_since: float | None = None
99
+ while True:
71
100
  try:
72
101
  status, response_headers, payload = self._send(method, path, headers, data)
73
102
  try:
@@ -76,28 +105,40 @@ class ExactApiClient(GeneratedOperationsClient):
76
105
  decoded = None
77
106
  if 200 <= status < 300:
78
107
  if not isinstance(decoded, dict):
79
- raise _InvalidResponse("Exact API success response must be a JSON object")
108
+ raise _InvalidResponse("DataXAPI success response must be a JSON object")
80
109
  return cast(dict[str, Any], decoded)
81
110
  problem: dict[str, Any] = cast(dict[str, Any], decoded) if isinstance(decoded, dict) else {
82
111
  "code": "invalid_error_response",
83
- "detail": f"Exact API returned an invalid HTTP {status} error body.",
112
+ "detail": f"DataXAPI returned an invalid HTTP {status} error body.",
84
113
  }
85
- error = ExactApiError(status, problem, idempotency_key, response_headers.get("Retry-After"))
114
+ error = DataXError(status, problem, idempotency_key, response_headers.get("Retry-After"))
115
+ delay = _retry_delay(error.retry_after, problem)
116
+ if status == 409 and error.code == "request_in_progress" and idempotency_key and self._max_retries > 0:
117
+ # The original call is still running and may still charge; its
118
+ # outcome arrives by polling with the same key, not a new call.
119
+ now = time.monotonic()
120
+ in_progress_since = now if in_progress_since is None else in_progress_since
121
+ if delay is not None and delay <= self._timeout and now - in_progress_since + delay <= IN_PROGRESS_WAIT_LIMIT:
122
+ if delay > 0:
123
+ time.sleep(delay)
124
+ continue
125
+ raise error
86
126
  if attempt < self._max_retries and _retryable(status, error.code):
87
- delay = _retry_delay(error.retry_after, problem)
88
127
  if delay is not None and delay <= self._timeout:
128
+ attempt += 1
89
129
  if delay > 0:
90
130
  time.sleep(delay)
91
131
  continue
92
132
  raise error
93
133
  except (OSError, TimeoutError, socket.timeout, http.client.HTTPException, _InvalidResponse) as error:
94
134
  if attempt < self._max_retries:
135
+ time.sleep(_network_backoff(attempt, self._timeout))
136
+ attempt += 1
95
137
  continue
96
- raise ExactApiConnectionError(
138
+ raise DataXConnectionError(
97
139
  "The request outcome is unknown. Retry the same logical operation with this idempotency key.",
98
140
  idempotency_key,
99
141
  ) from error
100
- raise AssertionError("unreachable")
101
142
 
102
143
  def get_usage(self, request_id: str) -> Usage:
103
144
  path = USAGE_OPERATION["path"].replace("{requestId}", quote(request_id, safe=""))
@@ -158,12 +199,12 @@ class _DeadlineReader(io.RawIOBase):
158
199
 
159
200
 
160
201
  class _X:
161
- def __init__(self, client: ExactApiClient):
202
+ def __init__(self, client: DataXClient):
162
203
  self.posts = _XPosts(client)
163
204
 
164
205
 
165
206
  class _XPosts:
166
- def __init__(self, client: ExactApiClient):
207
+ def __init__(self, client: DataXClient):
167
208
  self._client = client
168
209
 
169
210
  def search(self, input: XPostsSearchV1Input, *, idempotency_key: str | None = None) -> XPostsSearchV1Response:
@@ -176,7 +217,7 @@ class _XPosts:
176
217
  page = self.search(cast(XPostsSearchV1Input, current))
177
218
  cursor = page["page"]["next_cursor"]
178
219
  if cursor and cursor in seen_cursors:
179
- raise ExactApiPaginationError(cursor)
220
+ raise DataXPaginationError(cursor)
180
221
  if cursor:
181
222
  seen_cursors.add(cursor)
182
223
  yield page
@@ -189,12 +230,12 @@ class _XPosts:
189
230
 
190
231
 
191
232
  class _Reddit:
192
- def __init__(self, client: ExactApiClient):
233
+ def __init__(self, client: DataXClient):
193
234
  self.posts = _RedditPosts(client)
194
235
 
195
236
 
196
237
  class _RedditPosts:
197
- def __init__(self, client: ExactApiClient):
238
+ def __init__(self, client: DataXClient):
198
239
  self._client = client
199
240
 
200
241
  def detail(self, input: RedditPostsDetailV1Input, *, idempotency_key: str | None = None) -> RedditPostsDetailV1Response:
@@ -202,18 +243,18 @@ class _RedditPosts:
202
243
 
203
244
 
204
245
  class _Xiaohongshu:
205
- def __init__(self, client: ExactApiClient):
246
+ def __init__(self, client: DataXClient):
206
247
  self.pgy = _XiaohongshuPgy(client)
207
248
 
208
249
 
209
250
  class _XiaohongshuPgy:
210
- def __init__(self, client: ExactApiClient):
251
+ def __init__(self, client: DataXClient):
211
252
  self.notes = _XiaohongshuPgyNotes(client)
212
253
  self.bloggers = _XiaohongshuPgyBloggers(client)
213
254
 
214
255
 
215
256
  class _XiaohongshuPgyNotes:
216
- def __init__(self, client: ExactApiClient):
257
+ def __init__(self, client: DataXClient):
217
258
  self._client = client
218
259
 
219
260
  def detail(self, input: XiaohongshuPgyNotesDetailV1Input, *, idempotency_key: str | None = None) -> XiaohongshuPgyNotesDetailV1Response:
@@ -221,7 +262,7 @@ class _XiaohongshuPgyNotes:
221
262
 
222
263
 
223
264
  class _XiaohongshuPgyBloggers:
224
- def __init__(self, client: ExactApiClient):
265
+ def __init__(self, client: DataXClient):
225
266
  self._client = client
226
267
 
227
268
  def detail(self, input: XiaohongshuPgyBloggersDetailV1Input, *, idempotency_key: str | None = None) -> XiaohongshuPgyBloggersDetailV1Response:
@@ -238,7 +279,7 @@ class _XiaohongshuPgyBloggers:
238
279
 
239
280
 
240
281
  class _Usage:
241
- def __init__(self, client: ExactApiClient):
282
+ def __init__(self, client: DataXClient):
242
283
  self._client = client
243
284
 
244
285
  def get(self, request_id: str) -> Usage:
@@ -249,7 +290,7 @@ class _InvalidResponse(Exception):
249
290
  pass
250
291
 
251
292
 
252
- class ExactApiPaginationError(Exception):
293
+ class DataXPaginationError(Exception):
253
294
  def __init__(self, cursor: str):
254
295
  super().__init__("Pagination stopped because the API returned a repeated cursor.")
255
296
  self.cursor = cursor
@@ -281,7 +322,12 @@ def _set_socket_timeout(connection: http.client.HTTPConnection, deadline: float)
281
322
 
282
323
 
283
324
  def _retryable(status: int, code: str) -> bool:
284
- return status == 429 or (status >= 500 and code != "listing_paused") or (status == 409 and code == "request_in_progress")
325
+ return code not in NON_RETRYABLE_CODES and (status == 429 or status >= 500)
326
+
327
+
328
+ def _network_backoff(attempt: int, timeout: float) -> float:
329
+ """Exponential backoff with jitter, never longer than one attempt's timeout."""
330
+ return min(0.25 * 2 ** attempt, 8.0, timeout) * random.uniform(0.5, 1.0)
285
331
 
286
332
 
287
333
  def _retry_delay(retry_after: str | None, problem: dict[str, Any]) -> float | None:
@@ -300,3 +346,10 @@ def _retry_delay(retry_after: str | None, problem: dict[str, Any]) -> float | No
300
346
  else:
301
347
  seconds = 0.1
302
348
  return seconds if math.isfinite(seconds) and seconds >= 0 else None
349
+
350
+
351
+ # Deprecated aliases kept for code written against the preview names.
352
+ ExactApiClient = DataXClient
353
+ ExactApiError = DataXError
354
+ ExactApiConnectionError = DataXConnectionError
355
+ ExactApiPaginationError = DataXPaginationError