repsapi 0.1.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,6 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ dist/
4
+ build/
5
+ *.egg-info/
6
+ .venv/
repsapi-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RepsApi.pl
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.
repsapi-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.5
2
+ Name: repsapi
3
+ Version: 0.1.0
4
+ Summary: Official RepsApi.pl client: parcel tracking, QC photos, product lookup, agent links and shipping estimates.
5
+ Project-URL: Homepage, https://repsapi.pl
6
+ Project-URL: Documentation, https://repsapi.pl/docs
7
+ Project-URL: Changelog, https://repsapi.pl/changelog
8
+ Project-URL: Status, https://repsapi.pl/status
9
+ Author: RepsApi.pl
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: 1688,agent,api-client,qc,reps,repsapi,shipping,taobao,tracking,weidian
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.9
21
+ Requires-Dist: httpx<1,>=0.23
22
+ Description-Content-Type: text/markdown
23
+
24
+ # repsapi
25
+
26
+ Official Python client for the [RepsApi.pl](https://repsapi.pl) API: parcel tracking, warehouse QC
27
+ photos, product lookup, agent links, shipping estimates and haul calculations.
28
+
29
+ - Sync (`RepsApi`) and async (`AsyncRepsApi`) clients, e.g. for discord.py bots.
30
+ - Typed responses (`TypedDict`), so your editor completes every field.
31
+ - Retries network errors, 5xx answers and short rate limits on its own.
32
+ - One dependency: [httpx](https://www.python-httpx.org/). Python 3.9+.
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ pip install repsapi
38
+ ```
39
+
40
+ ## Quick start
41
+
42
+ Create an API key in the [dashboard](https://repsapi.pl/dashboard) and put it in the `REPSAPI_KEY`
43
+ environment variable (or pass it as `api_key`).
44
+
45
+ ```python
46
+ from repsapi import RepsApi
47
+
48
+ with RepsApi() as api: # reads REPSAPI_KEY
49
+ parcel = api.tracking("YT2530998211074512")
50
+ if parcel["summary"]:
51
+ print(parcel["summary"]["status_en"])
52
+
53
+ item = api.lookup("https://weidian.com/item.html?itemID=7779617259", currency="PLN")
54
+ print(item["title_en"], item.get("price_local"))
55
+ ```
56
+
57
+ Async:
58
+
59
+ ```python
60
+ import asyncio
61
+ from repsapi import AsyncRepsApi
62
+
63
+ async def main():
64
+ async with AsyncRepsApi() as api:
65
+ qc = await api.qc(platform="weidian", id="7779617259")
66
+ print(qc["total_photos"])
67
+
68
+ asyncio.run(main())
69
+ ```
70
+
71
+ Responses are the API's JSON as plain dicts. Field-by-field descriptions are in the
72
+ [documentation](https://repsapi.pl/docs).
73
+
74
+ ## Methods
75
+
76
+ Every method also takes `timeout=` (seconds) and `max_retries=`.
77
+
78
+ | Method | API | Key |
79
+ | --- | --- | --- |
80
+ | `tracking(number)` | `GET /v1/tracking` | yes |
81
+ | `qc(url)` / `qc(platform=, id=)` | `GET /v1/qc` | yes |
82
+ | `lookup(url, currency=, agent=)` / `lookup(platform=, id=, ...)` | `GET /v1/lookup` | yes |
83
+ | `convert(url, agents=)` | `GET /v1/convert` | yes |
84
+ | `shipping_estimate(country, weight=, dimensions=, urls=, currency=, battery=, brands=, liquids=)` | `POST /v1/shipping/estimate` | yes |
85
+ | `haul(items, country, currency=, battery=, brands=, liquids=)` | `POST /v1/haul` | yes |
86
+ | `usage()` | `GET /v1/usage` | yes |
87
+ | `shipping_countries()` | `GET /v1/shipping/countries` | no |
88
+ | `rates(base="CNY")` | `GET /v1/rates` | no |
89
+ | `health(days=)` | `GET /v1/health` | no |
90
+ | `changelog(limit=, lang=)` | `GET /v1/changelog` | no |
91
+
92
+ ```python
93
+ # the same product on two agents
94
+ links = api.convert("https://detail.1688.com/offer/678091425228.html", agents=["kakobuy", "cnfans"])
95
+ print(links["agents"]["kakobuy"])
96
+
97
+ # shipping lines for a 1.2 kg parcel to Poland, priced in PLN
98
+ estimate = api.shipping_estimate("PL", weight=1200, currency="PLN")
99
+ print(estimate["cheapest"])
100
+
101
+ # a whole haul
102
+ haul = api.haul(
103
+ [
104
+ {"url": "https://weidian.com/item.html?itemID=7779617259", "quantity": 2},
105
+ {"url": "https://detail.1688.com/offer/678091425228.html"},
106
+ ],
107
+ "PL",
108
+ currency="PLN",
109
+ )
110
+ print(haul["totals"].get("local"))
111
+ ```
112
+
113
+ ## Errors
114
+
115
+ Every error extends `RepsApiError`. Errors from the API extend `APIError` and carry `status`, `type`
116
+ (the API's error type), `message` and `details` (extra fields such as `allowed`).
117
+
118
+ | Class | When |
119
+ | --- | --- |
120
+ | `BadRequestError` | 400 / 413 / 415: something in the request is wrong |
121
+ | `AuthenticationError` | 401: missing, invalid or revoked key |
122
+ | `PermissionDeniedError` | 403: the key lacks this API's scope, or the IP isn't whitelisted |
123
+ | `UnprocessableEntityError` | 422: no platform and product id in the link |
124
+ | `RateLimitError` | 429: has `retry_after` (seconds), `reset_at`, `limit`, `used`, `scope` |
125
+ | `InternalServerError` | 5xx |
126
+ | `APIConnectionError` / `APITimeoutError` | no answer at all |
127
+
128
+ ```python
129
+ from repsapi import RateLimitError, UnprocessableEntityError
130
+
131
+ try:
132
+ api.lookup(link)
133
+ except RateLimitError as err:
134
+ print("Quota used up, resets", err.reset_at)
135
+ except UnprocessableEntityError:
136
+ print("That isn't a product link.")
137
+ ```
138
+
139
+ ## Retries, timeouts and limits
140
+
141
+ - Network errors and 500/502/503/504 answers are retried twice with a growing pause (`max_retries`).
142
+ - A `429` is retried after its `Retry-After` when that is at most 60 s (`max_retry_wait`); a used-up
143
+ daily quota raises `RateLimitError` at once.
144
+ - Failed calls don't count toward your quota, so retries cost nothing.
145
+ - Timeouts: 60 s, hauls and link-based shipping estimates 180 s (`timeout`).
146
+ - `api.rate_limit` holds the per-minute limit from the last response that carried one.
147
+ - The client never follows redirects, so your key is never sent anywhere but `base_url`, and
148
+ `base_url` must be `https://`. `repr(api)` never shows the key.
149
+
150
+ ```python
151
+ api = RepsApi(api_key="rk_live_...", timeout=30, max_retries=3)
152
+ ```
153
+
154
+ ## Links
155
+
156
+ - Documentation: https://repsapi.pl/docs
157
+ - Status: https://repsapi.pl/status
158
+ - Discord: https://discord.gg/repsapi
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,139 @@
1
+ # repsapi
2
+
3
+ Official Python client for the [RepsApi.pl](https://repsapi.pl) API: parcel tracking, warehouse QC
4
+ photos, product lookup, agent links, shipping estimates and haul calculations.
5
+
6
+ - Sync (`RepsApi`) and async (`AsyncRepsApi`) clients, e.g. for discord.py bots.
7
+ - Typed responses (`TypedDict`), so your editor completes every field.
8
+ - Retries network errors, 5xx answers and short rate limits on its own.
9
+ - One dependency: [httpx](https://www.python-httpx.org/). Python 3.9+.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install repsapi
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ Create an API key in the [dashboard](https://repsapi.pl/dashboard) and put it in the `REPSAPI_KEY`
20
+ environment variable (or pass it as `api_key`).
21
+
22
+ ```python
23
+ from repsapi import RepsApi
24
+
25
+ with RepsApi() as api: # reads REPSAPI_KEY
26
+ parcel = api.tracking("YT2530998211074512")
27
+ if parcel["summary"]:
28
+ print(parcel["summary"]["status_en"])
29
+
30
+ item = api.lookup("https://weidian.com/item.html?itemID=7779617259", currency="PLN")
31
+ print(item["title_en"], item.get("price_local"))
32
+ ```
33
+
34
+ Async:
35
+
36
+ ```python
37
+ import asyncio
38
+ from repsapi import AsyncRepsApi
39
+
40
+ async def main():
41
+ async with AsyncRepsApi() as api:
42
+ qc = await api.qc(platform="weidian", id="7779617259")
43
+ print(qc["total_photos"])
44
+
45
+ asyncio.run(main())
46
+ ```
47
+
48
+ Responses are the API's JSON as plain dicts. Field-by-field descriptions are in the
49
+ [documentation](https://repsapi.pl/docs).
50
+
51
+ ## Methods
52
+
53
+ Every method also takes `timeout=` (seconds) and `max_retries=`.
54
+
55
+ | Method | API | Key |
56
+ | --- | --- | --- |
57
+ | `tracking(number)` | `GET /v1/tracking` | yes |
58
+ | `qc(url)` / `qc(platform=, id=)` | `GET /v1/qc` | yes |
59
+ | `lookup(url, currency=, agent=)` / `lookup(platform=, id=, ...)` | `GET /v1/lookup` | yes |
60
+ | `convert(url, agents=)` | `GET /v1/convert` | yes |
61
+ | `shipping_estimate(country, weight=, dimensions=, urls=, currency=, battery=, brands=, liquids=)` | `POST /v1/shipping/estimate` | yes |
62
+ | `haul(items, country, currency=, battery=, brands=, liquids=)` | `POST /v1/haul` | yes |
63
+ | `usage()` | `GET /v1/usage` | yes |
64
+ | `shipping_countries()` | `GET /v1/shipping/countries` | no |
65
+ | `rates(base="CNY")` | `GET /v1/rates` | no |
66
+ | `health(days=)` | `GET /v1/health` | no |
67
+ | `changelog(limit=, lang=)` | `GET /v1/changelog` | no |
68
+
69
+ ```python
70
+ # the same product on two agents
71
+ links = api.convert("https://detail.1688.com/offer/678091425228.html", agents=["kakobuy", "cnfans"])
72
+ print(links["agents"]["kakobuy"])
73
+
74
+ # shipping lines for a 1.2 kg parcel to Poland, priced in PLN
75
+ estimate = api.shipping_estimate("PL", weight=1200, currency="PLN")
76
+ print(estimate["cheapest"])
77
+
78
+ # a whole haul
79
+ haul = api.haul(
80
+ [
81
+ {"url": "https://weidian.com/item.html?itemID=7779617259", "quantity": 2},
82
+ {"url": "https://detail.1688.com/offer/678091425228.html"},
83
+ ],
84
+ "PL",
85
+ currency="PLN",
86
+ )
87
+ print(haul["totals"].get("local"))
88
+ ```
89
+
90
+ ## Errors
91
+
92
+ Every error extends `RepsApiError`. Errors from the API extend `APIError` and carry `status`, `type`
93
+ (the API's error type), `message` and `details` (extra fields such as `allowed`).
94
+
95
+ | Class | When |
96
+ | --- | --- |
97
+ | `BadRequestError` | 400 / 413 / 415: something in the request is wrong |
98
+ | `AuthenticationError` | 401: missing, invalid or revoked key |
99
+ | `PermissionDeniedError` | 403: the key lacks this API's scope, or the IP isn't whitelisted |
100
+ | `UnprocessableEntityError` | 422: no platform and product id in the link |
101
+ | `RateLimitError` | 429: has `retry_after` (seconds), `reset_at`, `limit`, `used`, `scope` |
102
+ | `InternalServerError` | 5xx |
103
+ | `APIConnectionError` / `APITimeoutError` | no answer at all |
104
+
105
+ ```python
106
+ from repsapi import RateLimitError, UnprocessableEntityError
107
+
108
+ try:
109
+ api.lookup(link)
110
+ except RateLimitError as err:
111
+ print("Quota used up, resets", err.reset_at)
112
+ except UnprocessableEntityError:
113
+ print("That isn't a product link.")
114
+ ```
115
+
116
+ ## Retries, timeouts and limits
117
+
118
+ - Network errors and 500/502/503/504 answers are retried twice with a growing pause (`max_retries`).
119
+ - A `429` is retried after its `Retry-After` when that is at most 60 s (`max_retry_wait`); a used-up
120
+ daily quota raises `RateLimitError` at once.
121
+ - Failed calls don't count toward your quota, so retries cost nothing.
122
+ - Timeouts: 60 s, hauls and link-based shipping estimates 180 s (`timeout`).
123
+ - `api.rate_limit` holds the per-minute limit from the last response that carried one.
124
+ - The client never follows redirects, so your key is never sent anywhere but `base_url`, and
125
+ `base_url` must be `https://`. `repr(api)` never shows the key.
126
+
127
+ ```python
128
+ api = RepsApi(api_key="rk_live_...", timeout=30, max_retries=3)
129
+ ```
130
+
131
+ ## Links
132
+
133
+ - Documentation: https://repsapi.pl/docs
134
+ - Status: https://repsapi.pl/status
135
+ - Discord: https://discord.gg/repsapi
136
+
137
+ ## License
138
+
139
+ MIT
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "repsapi"
7
+ dynamic = ["version"]
8
+ description = "Official RepsApi.pl client: parcel tracking, QC photos, product lookup, agent links and shipping estimates."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.9"
13
+ dependencies = ["httpx>=0.23,<1"]
14
+ authors = [{ name = "RepsApi.pl" }]
15
+ keywords = ["repsapi", "reps", "qc", "tracking", "weidian", "taobao", "1688", "agent", "shipping", "api-client"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Framework :: AsyncIO",
23
+ "Typing :: Typed",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://repsapi.pl"
28
+ Documentation = "https://repsapi.pl/docs"
29
+ Changelog = "https://repsapi.pl/changelog"
30
+ Status = "https://repsapi.pl/status"
31
+
32
+ [tool.hatch.version]
33
+ path = "src/repsapi/_version.py"
34
+
35
+ [tool.hatch.build.targets.sdist]
36
+ # hatchling also copies the nearest .gitignore; the local one keeps the site's out of the package
37
+ only-include = ["src/repsapi", "README.md", "LICENSE", "pyproject.toml"]
38
+
39
+ [tool.hatch.build.targets.wheel]
40
+ packages = ["src/repsapi"]
@@ -0,0 +1,39 @@
1
+ """Official client for the RepsApi.pl API - https://repsapi.pl/docs"""
2
+
3
+ from . import types
4
+ from ._client import AGENTS, DEFAULT_BASE_URL, AsyncRepsApi, RateLimitInfo, RepsApi
5
+ from ._errors import (
6
+ APIConnectionError,
7
+ APIError,
8
+ APITimeoutError,
9
+ AuthenticationError,
10
+ BadRequestError,
11
+ InternalServerError,
12
+ NotFoundError,
13
+ PermissionDeniedError,
14
+ RateLimitError,
15
+ RepsApiError,
16
+ UnprocessableEntityError,
17
+ )
18
+ from ._version import __version__
19
+
20
+ __all__ = [
21
+ "RepsApi",
22
+ "AsyncRepsApi",
23
+ "RateLimitInfo",
24
+ "AGENTS",
25
+ "DEFAULT_BASE_URL",
26
+ "RepsApiError",
27
+ "APIError",
28
+ "BadRequestError",
29
+ "AuthenticationError",
30
+ "PermissionDeniedError",
31
+ "NotFoundError",
32
+ "UnprocessableEntityError",
33
+ "RateLimitError",
34
+ "InternalServerError",
35
+ "APIConnectionError",
36
+ "APITimeoutError",
37
+ "types",
38
+ "__version__",
39
+ ]