scrapelyserp 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,61 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # next.js
17
+ /.next/
18
+ /out/
19
+
20
+ # production
21
+ /build
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # env files (can opt-in for committing if needed)
34
+ .env*
35
+
36
+ # vercel
37
+ .vercel
38
+
39
+ # typescript
40
+ *.tsbuildinfo
41
+ next-env.d.ts
42
+
43
+ .data/
44
+ .env*
45
+ !.env.example
46
+
47
+ # tls-client shared libraries: only the Linux build ships (native/tls-client-linux-amd64.so).
48
+ # Point TLS_CLIENT_LIB_PATH at a Windows/macOS build for local live testing.
49
+ native/*.dll
50
+ native/*.dylib
51
+ .live/
52
+
53
+ # ForgeDeck local artifacts (run prompts, captured transcripts)
54
+ .forgedeck/
55
+ .forgememory/transcripts/
56
+
57
+ # SDK / CLI / MCP package builds (packages/)
58
+ packages/*/dist/
59
+ packages/*/node_modules/
60
+ __pycache__/
61
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Scrapely
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.
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.5
2
+ Name: scrapelyserp
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the Scrapely API: product, price and stock data, structured search results, and watchlists that alert on change.
5
+ Project-URL: Homepage, https://scrapelyserp.com
6
+ Project-URL: Documentation, https://scrapelyserp.com/docs
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: api,price monitoring,product data,scrapely,scrapelyserp,sdk
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Typing :: Typed
13
+ Requires-Python: >=3.9
14
+ Requires-Dist: httpx<1,>=0.24
15
+ Description-Content-Type: text/markdown
16
+
17
+ # scrapelyserp
18
+
19
+ The official Python SDK for the [Scrapely API](https://scrapelyserp.com/docs): product, price and stock data from
20
+ the retailers you sell on, structured search results, and watchlists that alert you when something changes.
21
+
22
+ - Every API operation, generated from the API's OpenAPI document, grouped by resource.
23
+ - Sync (`Scrapely`) and asyncio (`AsyncScrapely`) clients over [httpx](https://www.python-httpx.org/), the only
24
+ dependency. Python 3.9+.
25
+ - Typed: request and response bodies are `TypedDict`s (`scrapelyserp.types`), methods have typed signatures.
26
+ - Retries with backoff on 429 and 5xx, honouring `Retry-After`; idempotency keys generated for you.
27
+ - Typed errors carrying the API's error `code` and `param`.
28
+
29
+ ```sh
30
+ pip install scrapelyserp
31
+ ```
32
+
33
+ ## Quick start
34
+
35
+ ```python
36
+ from scrapelyserp import Scrapely
37
+
38
+ client = Scrapely() # reads SCRAPELY_API_KEY
39
+
40
+ account = client.account.get()
41
+ print(account["balance"], "credits")
42
+
43
+ products = client.products.fetch(platform="amazon", ids=["B0009KNLV6"], tier="lite")
44
+ for item in products["results"]:
45
+ print(item["id"], item["status"], item.get("price"))
46
+
47
+ # Structured endpoints are grouped by platform.
48
+ results = client.structured.amazon.search(query="wireless earbuds")
49
+ posts = client.structured.reddit.posts(subreddit="deals")
50
+ ```
51
+
52
+ Async:
53
+
54
+ ```python
55
+ import asyncio
56
+ from scrapelyserp import AsyncScrapely
57
+
58
+ async def main() -> None:
59
+ async with AsyncScrapely() as client:
60
+ account = await client.account.get()
61
+ async for item in client.watchlists.list_items_iter("wl_…"):
62
+ print(item["id"])
63
+
64
+ asyncio.run(main())
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ ```python
70
+ client = Scrapely(
71
+ api_key="sk_live_…", # default: SCRAPELY_API_KEY
72
+ base_url="https://scrapelyserp.com", # default: SCRAPELY_BASE_URL, then this
73
+ max_retries=2, # 429, 5xx (except 501) and network errors
74
+ timeout=360.0, # seconds per attempt
75
+ headers={"x-my-header": "1"},
76
+ http_client=httpx.Client(...), # your own httpx client (proxies, transports, tests)
77
+ )
78
+ ```
79
+
80
+ Use it as a context manager (`with Scrapely() as client:`) or call `client.close()`.
81
+
82
+ ## Resources
83
+
84
+ `client.products`, `client.offers`, `client.platforms`, `client.structured` (one sub-resource per platform:
85
+ `client.structured.ebay.image_search(...)`), `client.watchlists`, `client.destinations`,
86
+ `client.webhook_endpoints`, `client.s3_export`, `client.account`, `client.meta`.
87
+
88
+ Path parameters are positional; query parameters are keyword arguments; a body is a dict, keyword arguments, or both
89
+ (keywords win):
90
+
91
+ ```python
92
+ wl = client.watchlists.create(name="Consoles", platform="target", ids=["83887639"], every="15m")
93
+ client.watchlists.update(wl["id"], status="paused")
94
+ page = client.watchlists.list_items(wl["id"], state="active", limit=100)
95
+ client.destinations.set_for_watchlist(wl["id"], destinations=[])
96
+
97
+ # Any operation by its OpenAPI operationId:
98
+ client.request("getWatchlist", path={"id": wl["id"]})
99
+ ```
100
+
101
+ Every method also takes `options={"headers": ..., "timeout": ..., "max_retries": ...}`.
102
+
103
+ ## Pagination
104
+
105
+ List operations with a `starting_after` cursor have an `..._iter` twin that walks every page:
106
+
107
+ ```python
108
+ for item in client.watchlists.list_items_iter("wl_…", changed_since="2026-11-01T00:00:00Z"):
109
+ print(item["id"], item.get("snapshot"))
110
+ ```
111
+
112
+ ## Errors
113
+
114
+ Every error is a `ScrapelyError`. API answers are an `APIError` subclass with `status`, `code` (stable: branch on
115
+ it), `message`, `param`, `docs` and `request_id`:
116
+
117
+ ```python
118
+ from scrapelyserp import APIError, InsufficientCreditsError
119
+
120
+ try:
121
+ client.products.fetch(platform="walmart", ids=["5689919121"])
122
+ except InsufficientCreditsError:
123
+ print("Top up first")
124
+ except APIError as e:
125
+ print(e.code, e.param, e.request_id)
126
+ ```
127
+
128
+ `BadRequestError` (400), `AuthenticationError` (401), `InsufficientCreditsError` (402), `PermissionDeniedError`
129
+ (403), `NotFoundError` (404), `ConflictError` (409), `RateLimitError` (429), `InternalServerError` (5xx),
130
+ `ComingSoonError` (501: announced, not live yet; nothing charged), `APIConnectionError` / `APITimeoutError`
131
+ (no answer).
132
+
133
+ ## Retries and idempotency
134
+
135
+ 429 and 5xx answers (except 501) and network errors are retried `max_retries` times, waiting for `Retry-After` when
136
+ the API sends it, else 0.5 s, 1 s, 2 s... Operations that accept an `Idempotency-Key` (creating a watchlist) get one
137
+ generated per call and reused across its retries; pass `idempotency_key=` to choose your own.
138
+
139
+ ## Development
140
+
141
+ `types.py`, `_routes.py` and `_resources.py` are generated from the API's OpenAPI document by
142
+ `scripts/sdk/generate.ts` in the Scrapely repository; do not edit them by hand.
143
+
144
+ ```sh
145
+ python -m unittest discover -s tests # offline, against httpx.MockTransport
146
+ python -m build # sdist + wheel
147
+ ```
148
+
149
+ ## License
150
+
151
+ MIT
@@ -0,0 +1,135 @@
1
+ # scrapelyserp
2
+
3
+ The official Python SDK for the [Scrapely API](https://scrapelyserp.com/docs): product, price and stock data from
4
+ the retailers you sell on, structured search results, and watchlists that alert you when something changes.
5
+
6
+ - Every API operation, generated from the API's OpenAPI document, grouped by resource.
7
+ - Sync (`Scrapely`) and asyncio (`AsyncScrapely`) clients over [httpx](https://www.python-httpx.org/), the only
8
+ dependency. Python 3.9+.
9
+ - Typed: request and response bodies are `TypedDict`s (`scrapelyserp.types`), methods have typed signatures.
10
+ - Retries with backoff on 429 and 5xx, honouring `Retry-After`; idempotency keys generated for you.
11
+ - Typed errors carrying the API's error `code` and `param`.
12
+
13
+ ```sh
14
+ pip install scrapelyserp
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ ```python
20
+ from scrapelyserp import Scrapely
21
+
22
+ client = Scrapely() # reads SCRAPELY_API_KEY
23
+
24
+ account = client.account.get()
25
+ print(account["balance"], "credits")
26
+
27
+ products = client.products.fetch(platform="amazon", ids=["B0009KNLV6"], tier="lite")
28
+ for item in products["results"]:
29
+ print(item["id"], item["status"], item.get("price"))
30
+
31
+ # Structured endpoints are grouped by platform.
32
+ results = client.structured.amazon.search(query="wireless earbuds")
33
+ posts = client.structured.reddit.posts(subreddit="deals")
34
+ ```
35
+
36
+ Async:
37
+
38
+ ```python
39
+ import asyncio
40
+ from scrapelyserp import AsyncScrapely
41
+
42
+ async def main() -> None:
43
+ async with AsyncScrapely() as client:
44
+ account = await client.account.get()
45
+ async for item in client.watchlists.list_items_iter("wl_…"):
46
+ print(item["id"])
47
+
48
+ asyncio.run(main())
49
+ ```
50
+
51
+ ## Configuration
52
+
53
+ ```python
54
+ client = Scrapely(
55
+ api_key="sk_live_…", # default: SCRAPELY_API_KEY
56
+ base_url="https://scrapelyserp.com", # default: SCRAPELY_BASE_URL, then this
57
+ max_retries=2, # 429, 5xx (except 501) and network errors
58
+ timeout=360.0, # seconds per attempt
59
+ headers={"x-my-header": "1"},
60
+ http_client=httpx.Client(...), # your own httpx client (proxies, transports, tests)
61
+ )
62
+ ```
63
+
64
+ Use it as a context manager (`with Scrapely() as client:`) or call `client.close()`.
65
+
66
+ ## Resources
67
+
68
+ `client.products`, `client.offers`, `client.platforms`, `client.structured` (one sub-resource per platform:
69
+ `client.structured.ebay.image_search(...)`), `client.watchlists`, `client.destinations`,
70
+ `client.webhook_endpoints`, `client.s3_export`, `client.account`, `client.meta`.
71
+
72
+ Path parameters are positional; query parameters are keyword arguments; a body is a dict, keyword arguments, or both
73
+ (keywords win):
74
+
75
+ ```python
76
+ wl = client.watchlists.create(name="Consoles", platform="target", ids=["83887639"], every="15m")
77
+ client.watchlists.update(wl["id"], status="paused")
78
+ page = client.watchlists.list_items(wl["id"], state="active", limit=100)
79
+ client.destinations.set_for_watchlist(wl["id"], destinations=[])
80
+
81
+ # Any operation by its OpenAPI operationId:
82
+ client.request("getWatchlist", path={"id": wl["id"]})
83
+ ```
84
+
85
+ Every method also takes `options={"headers": ..., "timeout": ..., "max_retries": ...}`.
86
+
87
+ ## Pagination
88
+
89
+ List operations with a `starting_after` cursor have an `..._iter` twin that walks every page:
90
+
91
+ ```python
92
+ for item in client.watchlists.list_items_iter("wl_…", changed_since="2026-11-01T00:00:00Z"):
93
+ print(item["id"], item.get("snapshot"))
94
+ ```
95
+
96
+ ## Errors
97
+
98
+ Every error is a `ScrapelyError`. API answers are an `APIError` subclass with `status`, `code` (stable: branch on
99
+ it), `message`, `param`, `docs` and `request_id`:
100
+
101
+ ```python
102
+ from scrapelyserp import APIError, InsufficientCreditsError
103
+
104
+ try:
105
+ client.products.fetch(platform="walmart", ids=["5689919121"])
106
+ except InsufficientCreditsError:
107
+ print("Top up first")
108
+ except APIError as e:
109
+ print(e.code, e.param, e.request_id)
110
+ ```
111
+
112
+ `BadRequestError` (400), `AuthenticationError` (401), `InsufficientCreditsError` (402), `PermissionDeniedError`
113
+ (403), `NotFoundError` (404), `ConflictError` (409), `RateLimitError` (429), `InternalServerError` (5xx),
114
+ `ComingSoonError` (501: announced, not live yet; nothing charged), `APIConnectionError` / `APITimeoutError`
115
+ (no answer).
116
+
117
+ ## Retries and idempotency
118
+
119
+ 429 and 5xx answers (except 501) and network errors are retried `max_retries` times, waiting for `Retry-After` when
120
+ the API sends it, else 0.5 s, 1 s, 2 s... Operations that accept an `Idempotency-Key` (creating a watchlist) get one
121
+ generated per call and reused across its retries; pass `idempotency_key=` to choose your own.
122
+
123
+ ## Development
124
+
125
+ `types.py`, `_routes.py` and `_resources.py` are generated from the API's OpenAPI document by
126
+ `scripts/sdk/generate.ts` in the Scrapely repository; do not edit them by hand.
127
+
128
+ ```sh
129
+ python -m unittest discover -s tests # offline, against httpx.MockTransport
130
+ python -m build # sdist + wheel
131
+ ```
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.21"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "scrapelyserp"
7
+ version = "0.1.0"
8
+ description = "Official Python SDK for the Scrapely API: product, price and stock data, structured search results, and watchlists that alert on change."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.9"
13
+ dependencies = ["httpx>=0.24,<1"]
14
+ keywords = ["scrapely", "scrapelyserp", "api", "sdk", "price monitoring", "product data"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Typing :: Typed",
18
+ "Intended Audience :: Developers",
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://scrapelyserp.com"
23
+ Documentation = "https://scrapelyserp.com/docs"
24
+
25
+ [tool.hatch.build.targets.wheel]
26
+ packages = ["src/scrapelyserp"]
@@ -0,0 +1,50 @@
1
+ """Official Python SDK for the Scrapely API (https://scrapelyserp.com).
2
+
3
+ from scrapelyserp import Scrapely
4
+
5
+ client = Scrapely() # reads SCRAPELY_API_KEY
6
+ print(client.account.get()["balance"])
7
+ """
8
+
9
+ from . import types
10
+ from ._base import DEFAULT_BASE_URL, RequestOptions, normalize_base_url
11
+ from ._errors import (
12
+ APIConnectionError,
13
+ APIError,
14
+ APITimeoutError,
15
+ AuthenticationError,
16
+ BadRequestError,
17
+ ComingSoonError,
18
+ ConflictError,
19
+ InsufficientCreditsError,
20
+ InternalServerError,
21
+ NotFoundError,
22
+ PermissionDeniedError,
23
+ RateLimitError,
24
+ ScrapelyError,
25
+ )
26
+ from ._resources import AsyncScrapely, Scrapely
27
+ from ._version import __version__
28
+
29
+ __all__ = [
30
+ "Scrapely",
31
+ "AsyncScrapely",
32
+ "RequestOptions",
33
+ "DEFAULT_BASE_URL",
34
+ "normalize_base_url",
35
+ "types",
36
+ "ScrapelyError",
37
+ "APIError",
38
+ "BadRequestError",
39
+ "AuthenticationError",
40
+ "InsufficientCreditsError",
41
+ "PermissionDeniedError",
42
+ "NotFoundError",
43
+ "ConflictError",
44
+ "RateLimitError",
45
+ "InternalServerError",
46
+ "ComingSoonError",
47
+ "APIConnectionError",
48
+ "APITimeoutError",
49
+ "__version__",
50
+ ]