vermin-sdk 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
+ .venv/
2
+ .pytest_cache/
3
+ .mypy_cache/
4
+ dist/
5
+ __pycache__/
6
+ .ruff_cache/
@@ -0,0 +1,234 @@
1
+ Metadata-Version: 2.5
2
+ Name: vermin-sdk
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the Vermin web-data API: scrape, crawl, map and brand.
5
+ Project-URL: Homepage, https://vermin.dev
6
+ Project-URL: Documentation, https://vermin.dev/docs
7
+ Keywords: ai,crawl,llm,scrape,sdk,vermin,web-data
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.9
19
+ Requires-Dist: httpx<1,>=0.25
20
+ Requires-Dist: pydantic<3,>=2.5
21
+ Requires-Dist: typing-extensions>=4.7
22
+ Provides-Extra: dev
23
+ Requires-Dist: jsonschema[format-nongpl]>=4.18; extra == 'dev'
24
+ Requires-Dist: mypy>=1.10; extra == 'dev'
25
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
26
+ Requires-Dist: pytest>=8; extra == 'dev'
27
+ Requires-Dist: pyyaml>=6; extra == 'dev'
28
+ Requires-Dist: respx>=0.21; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # vermin
32
+
33
+ Official Python SDK for [Vermin](https://vermin.dev), the web-data API for AI: **scrape, crawl, map, search, extract and brand**.
34
+
35
+ - Sync `Vermin` and async `AsyncVermin` clients with the same methods, built on [httpx](https://www.python-httpx.org/).
36
+ - Typed [pydantic v2](https://docs.pydantic.dev/) response models that follow the [OpenAPI contract](../../packages/spec/openapi.yaml).
37
+ - Automatic retries with jittered backoff on 429, 5xx and network errors. They honour `retry_after_ms` / `Retry-After`.
38
+ - Auto-generated `Idempotency-Key` on `scrape` and `crawl.start`, reused across retries.
39
+ - `credits_used` and `request_id` on every result, plus a typed `VerminError`.
40
+
41
+ Requires Python 3.9+.
42
+
43
+ ```sh
44
+ pip install vermin-sdk
45
+ ```
46
+
47
+ ## Quick start
48
+
49
+ ```python
50
+ from vermin import Vermin
51
+
52
+ vermin = Vermin() # reads VERMIN_API_KEY (and optionally VERMIN_API_URL)
53
+
54
+ page = vermin.scrape("https://example.com", formats=["markdown", "metadata"])
55
+ print(page.data.markdown, page.data.metadata.title)
56
+ print(f"cost: {page.credits_used} credits (request {page.request_id})")
57
+ ```
58
+
59
+ Async:
60
+
61
+ ```python
62
+ import asyncio
63
+ from vermin import AsyncVermin
64
+
65
+ async def main():
66
+ async with AsyncVermin() as vermin:
67
+ page = await vermin.scrape("https://example.com")
68
+ print(page.data.markdown)
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ### Options
74
+
75
+ ```python
76
+ Vermin(
77
+ api_key="vmn_live_…", # default: VERMIN_API_KEY
78
+ base_url="https://api.vermin.dev", # default: VERMIN_API_URL, then https://api.vermin.dev
79
+ timeout=60.0, # per-attempt HTTP timeout, seconds
80
+ max_retries=2, # retries on 429 / 5xx / network errors
81
+ headers={"X-Team": "growth"}, # extra headers on every request
82
+ http_client=httpx.Client(proxy=…), # bring your own httpx client
83
+ )
84
+ ```
85
+
86
+ Use the client as a context manager (`with Vermin() as vermin:`), or call `close()` / `await aclose()` when you're done.
87
+
88
+ Keyword options use snake_case (`only_main_content=True`). The SDK sends them as the API's camelCase. Nested `scrape_options` accept a dict with snake_case or camelCase keys, or a `ScrapeOptions` model.
89
+
90
+ ## API
91
+
92
+ ### `scrape(url, **options)`
93
+
94
+ ```python
95
+ res = vermin.scrape(
96
+ "https://news.ycombinator.com",
97
+ formats=["markdown", "links", "chunks"],
98
+ only_main_content=True,
99
+ tier="auto", # fetch → render → premium (if allow_premium)
100
+ chunk_size=1200,
101
+ actions=[{"type": "click", "selector": "#more"}, {"type": "wait", "ms": 1000}],
102
+ )
103
+ for chunk in res.data.chunks or []:
104
+ print(chunk.heading_path, chunk.tokens)
105
+ res.data.quality.issues # e.g. ["js_required"]
106
+ ```
107
+
108
+ ### `crawl`
109
+
110
+ ```python
111
+ job = vermin.crawl.start("https://docs.example.com", limit=200, max_depth=3,
112
+ scrape_options={"formats": ["markdown"]})
113
+
114
+ # Poll until finished and collect every page (follows `next` cursors):
115
+ result = vermin.crawl.wait(job.id, poll_interval=2.0, timeout=600,
116
+ on_status=lambda s: print(s.completed, "/", s.total))
117
+ print(result.status, len(result.data), result.credits_used)
118
+
119
+ # …or stream pages as they arrive (server-sent events):
120
+ for event in vermin.crawl.stream(job.id):
121
+ if event.type == "page":
122
+ print(event.data.url)
123
+ elif event.type == "done":
124
+ print("finished:", event.data.status)
125
+
126
+ # Lower level:
127
+ status = vermin.crawl.get(job.id, limit=50)
128
+ page2 = vermin.crawl.get(job.id, cursor=status.next) if status.next else None
129
+ for doc in vermin.crawl.documents(job.id):
130
+ print(doc.url)
131
+ vermin.crawl.cancel(job.id)
132
+ ```
133
+
134
+ With `AsyncVermin`, use `await vermin.crawl.wait(...)`, `async for event in vermin.crawl.stream(...)` and `async for doc in vermin.crawl.documents(...)`.
135
+
136
+ `crawl.wait` returns the final status even if the crawl `failed` or was `cancelled`, so check `result.status`. It raises `VerminError(code="timeout")` if `timeout` seconds pass first. A failed crawl carries `result.error` (`code`, `message`). `crawl.stream` reconnects with `Last-Event-ID` if the connection drops before `done` (waiting the server's `retry:` delay, else backoff), up to `max_reconnects` consecutive times (default `max_retries`; the budget resets after each page/status event), then raises `VerminError(code="connection_error")`. Resume with `crawl.stream(id, last_event_id="41")`.
137
+
138
+ ### `map(url, **options)`
139
+
140
+ ```python
141
+ res = vermin.map("https://example.com", search="pricing", limit=500)
142
+ urls = [link.url for link in res.links]
143
+ vermin.map("https://example.com", include_paths=["/blog/*"], exclude_paths=["/blog/tag/*"]) # path globs
144
+ ```
145
+
146
+ ### `search(query, **options)`
147
+
148
+ ```python
149
+ res = vermin.search("best vector databases 2026", limit=5, time_range="month",
150
+ scrape_options={"formats": ["markdown"]}) # optional: scrape each result
151
+ for hit in res.results:
152
+ print(hit.position, hit.title, hit.document and len(hit.document.markdown or ""))
153
+ # hit.date and hit.source are set when the provider reports them (e.g. news results)
154
+ ```
155
+
156
+ ### `extract(urls, schema=..., prompt=...)`
157
+
158
+ Pass a pydantic model class or a JSON Schema dict. A model is sent as its JSON Schema. The returned `data` is then parsed into an instance of the model.
159
+
160
+ ```python
161
+ from pydantic import BaseModel
162
+
163
+ class Product(BaseModel):
164
+ name: str
165
+ price: float
166
+ in_stock: bool
167
+
168
+ res = vermin.extract("https://shop.example.com/p/123", schema=Product, prompt="Extract the product")
169
+ res.data.price # float, and res.data is a Product
170
+
171
+ raw = vermin.extract(["https://example.com"], schema={"type": "object", "properties": {"title": {"type": "string"}}})
172
+ raw.data["title"]
173
+ ```
174
+
175
+ If the data doesn't match the model, `extract` raises `VerminError(code="invalid_response")`. Pass `validate=False` to get the raw dict instead.
176
+
177
+ ### `brand(domain, max_age=None)`
178
+
179
+ ```python
180
+ res = vermin.brand("stripe.com") # max_age=0 forces a fresh extraction
181
+ brand = res.data
182
+ brand.logo.url # Vermin-hosted copy; brand.logo.source_url is where it was found
183
+ next(c.hex for c in brand.colors if c.role == "primary") # each colour has text_color + WCAG contrast
184
+ # Every image found, tagged with type ("logo" | "icon" | "wordmark") and background ("light" | "dark" | "any")
185
+ for_dark_ui = [l for l in brand.logos or [] if l.background == "dark" and l.type != "icon"]
186
+ res.cached, res.stale # stale: cached result older than max_age, served because a fresh lookup failed
187
+ ```
188
+
189
+ ## Errors
190
+
191
+ Every failure raises `VerminError`:
192
+
193
+ ```python
194
+ from vermin import VerminError
195
+
196
+ try:
197
+ vermin.scrape("https://example.com")
198
+ except VerminError as err:
199
+ err.code # "rate_limited", "insufficient_credits", "blocked_by_site", … or "connection_error"
200
+ err.status # HTTP status (0 if no response)
201
+ err.request_id # quote this to support
202
+ err.retry_after_ms # server hint, when present
203
+ err.retryable # whether retrying could help
204
+ ```
205
+
206
+ Retries apply to 429, 5xx (including `capacity_exceeded`), timeouts and connection errors. By default the SDK makes up to 3 attempts, with jittered exponential backoff from 0.5 s up to 8 s. When the server sends a `retry_after_ms` hint (or a `Retry-After` header, seconds or HTTP date), the SDK waits exactly that long. It never retries `invalid_request`, `unauthorized`, `forbidden`, `not_found`, `insufficient_credits`, `spend_cap_reached`, `blocked_url`, `not_implemented`, `not_configured` or `extract_failed`, whatever the status. Failed requests cost 0 credits.
207
+
208
+ ## Webhooks
209
+
210
+ Crawl webhooks carry a `Vermin-Signature: t=<unix seconds>,v1=<hex>` header, where `v1` is the HMAC-SHA256 of `"<t>.<raw body>"` keyed with your signing secret (Dashboard → Webhooks). Verify it against the **raw** request body before trusting the payload. The helper compares in constant time, accepts any of several `v1=` values (secret rotation) and rejects timestamps more than 300 s from now (`tolerance`; `0` disables the check).
211
+
212
+ ```python
213
+ import os
214
+ from vermin import VerminError, verify_webhook
215
+
216
+ def handle(body: bytes, headers) -> int:
217
+ try:
218
+ event = verify_webhook(body, headers.get("Vermin-Signature"), os.environ["VERMIN_WEBHOOK_SECRET"])
219
+ except VerminError: # code == "invalid_signature"
220
+ return 400
221
+ if event["type"] == "crawl.page":
222
+ print(event["data"]["url"])
223
+ return 204
224
+ ```
225
+
226
+ `is_valid_webhook()` returns a bool instead, and `sign_webhook()` builds a header for your tests.
227
+
228
+ ## Development
229
+
230
+ ```sh
231
+ python3 -m venv .venv
232
+ .venv/bin/pip install -e ".[dev]"
233
+ .venv/bin/pytest # respx-mocked; every mock response is validated against the OpenAPI spec
234
+ ```
@@ -0,0 +1,204 @@
1
+ # vermin
2
+
3
+ Official Python SDK for [Vermin](https://vermin.dev), the web-data API for AI: **scrape, crawl, map, search, extract and brand**.
4
+
5
+ - Sync `Vermin` and async `AsyncVermin` clients with the same methods, built on [httpx](https://www.python-httpx.org/).
6
+ - Typed [pydantic v2](https://docs.pydantic.dev/) response models that follow the [OpenAPI contract](../../packages/spec/openapi.yaml).
7
+ - Automatic retries with jittered backoff on 429, 5xx and network errors. They honour `retry_after_ms` / `Retry-After`.
8
+ - Auto-generated `Idempotency-Key` on `scrape` and `crawl.start`, reused across retries.
9
+ - `credits_used` and `request_id` on every result, plus a typed `VerminError`.
10
+
11
+ Requires Python 3.9+.
12
+
13
+ ```sh
14
+ pip install vermin-sdk
15
+ ```
16
+
17
+ ## Quick start
18
+
19
+ ```python
20
+ from vermin import Vermin
21
+
22
+ vermin = Vermin() # reads VERMIN_API_KEY (and optionally VERMIN_API_URL)
23
+
24
+ page = vermin.scrape("https://example.com", formats=["markdown", "metadata"])
25
+ print(page.data.markdown, page.data.metadata.title)
26
+ print(f"cost: {page.credits_used} credits (request {page.request_id})")
27
+ ```
28
+
29
+ Async:
30
+
31
+ ```python
32
+ import asyncio
33
+ from vermin import AsyncVermin
34
+
35
+ async def main():
36
+ async with AsyncVermin() as vermin:
37
+ page = await vermin.scrape("https://example.com")
38
+ print(page.data.markdown)
39
+
40
+ asyncio.run(main())
41
+ ```
42
+
43
+ ### Options
44
+
45
+ ```python
46
+ Vermin(
47
+ api_key="vmn_live_…", # default: VERMIN_API_KEY
48
+ base_url="https://api.vermin.dev", # default: VERMIN_API_URL, then https://api.vermin.dev
49
+ timeout=60.0, # per-attempt HTTP timeout, seconds
50
+ max_retries=2, # retries on 429 / 5xx / network errors
51
+ headers={"X-Team": "growth"}, # extra headers on every request
52
+ http_client=httpx.Client(proxy=…), # bring your own httpx client
53
+ )
54
+ ```
55
+
56
+ Use the client as a context manager (`with Vermin() as vermin:`), or call `close()` / `await aclose()` when you're done.
57
+
58
+ Keyword options use snake_case (`only_main_content=True`). The SDK sends them as the API's camelCase. Nested `scrape_options` accept a dict with snake_case or camelCase keys, or a `ScrapeOptions` model.
59
+
60
+ ## API
61
+
62
+ ### `scrape(url, **options)`
63
+
64
+ ```python
65
+ res = vermin.scrape(
66
+ "https://news.ycombinator.com",
67
+ formats=["markdown", "links", "chunks"],
68
+ only_main_content=True,
69
+ tier="auto", # fetch → render → premium (if allow_premium)
70
+ chunk_size=1200,
71
+ actions=[{"type": "click", "selector": "#more"}, {"type": "wait", "ms": 1000}],
72
+ )
73
+ for chunk in res.data.chunks or []:
74
+ print(chunk.heading_path, chunk.tokens)
75
+ res.data.quality.issues # e.g. ["js_required"]
76
+ ```
77
+
78
+ ### `crawl`
79
+
80
+ ```python
81
+ job = vermin.crawl.start("https://docs.example.com", limit=200, max_depth=3,
82
+ scrape_options={"formats": ["markdown"]})
83
+
84
+ # Poll until finished and collect every page (follows `next` cursors):
85
+ result = vermin.crawl.wait(job.id, poll_interval=2.0, timeout=600,
86
+ on_status=lambda s: print(s.completed, "/", s.total))
87
+ print(result.status, len(result.data), result.credits_used)
88
+
89
+ # …or stream pages as they arrive (server-sent events):
90
+ for event in vermin.crawl.stream(job.id):
91
+ if event.type == "page":
92
+ print(event.data.url)
93
+ elif event.type == "done":
94
+ print("finished:", event.data.status)
95
+
96
+ # Lower level:
97
+ status = vermin.crawl.get(job.id, limit=50)
98
+ page2 = vermin.crawl.get(job.id, cursor=status.next) if status.next else None
99
+ for doc in vermin.crawl.documents(job.id):
100
+ print(doc.url)
101
+ vermin.crawl.cancel(job.id)
102
+ ```
103
+
104
+ With `AsyncVermin`, use `await vermin.crawl.wait(...)`, `async for event in vermin.crawl.stream(...)` and `async for doc in vermin.crawl.documents(...)`.
105
+
106
+ `crawl.wait` returns the final status even if the crawl `failed` or was `cancelled`, so check `result.status`. It raises `VerminError(code="timeout")` if `timeout` seconds pass first. A failed crawl carries `result.error` (`code`, `message`). `crawl.stream` reconnects with `Last-Event-ID` if the connection drops before `done` (waiting the server's `retry:` delay, else backoff), up to `max_reconnects` consecutive times (default `max_retries`; the budget resets after each page/status event), then raises `VerminError(code="connection_error")`. Resume with `crawl.stream(id, last_event_id="41")`.
107
+
108
+ ### `map(url, **options)`
109
+
110
+ ```python
111
+ res = vermin.map("https://example.com", search="pricing", limit=500)
112
+ urls = [link.url for link in res.links]
113
+ vermin.map("https://example.com", include_paths=["/blog/*"], exclude_paths=["/blog/tag/*"]) # path globs
114
+ ```
115
+
116
+ ### `search(query, **options)`
117
+
118
+ ```python
119
+ res = vermin.search("best vector databases 2026", limit=5, time_range="month",
120
+ scrape_options={"formats": ["markdown"]}) # optional: scrape each result
121
+ for hit in res.results:
122
+ print(hit.position, hit.title, hit.document and len(hit.document.markdown or ""))
123
+ # hit.date and hit.source are set when the provider reports them (e.g. news results)
124
+ ```
125
+
126
+ ### `extract(urls, schema=..., prompt=...)`
127
+
128
+ Pass a pydantic model class or a JSON Schema dict. A model is sent as its JSON Schema. The returned `data` is then parsed into an instance of the model.
129
+
130
+ ```python
131
+ from pydantic import BaseModel
132
+
133
+ class Product(BaseModel):
134
+ name: str
135
+ price: float
136
+ in_stock: bool
137
+
138
+ res = vermin.extract("https://shop.example.com/p/123", schema=Product, prompt="Extract the product")
139
+ res.data.price # float, and res.data is a Product
140
+
141
+ raw = vermin.extract(["https://example.com"], schema={"type": "object", "properties": {"title": {"type": "string"}}})
142
+ raw.data["title"]
143
+ ```
144
+
145
+ If the data doesn't match the model, `extract` raises `VerminError(code="invalid_response")`. Pass `validate=False` to get the raw dict instead.
146
+
147
+ ### `brand(domain, max_age=None)`
148
+
149
+ ```python
150
+ res = vermin.brand("stripe.com") # max_age=0 forces a fresh extraction
151
+ brand = res.data
152
+ brand.logo.url # Vermin-hosted copy; brand.logo.source_url is where it was found
153
+ next(c.hex for c in brand.colors if c.role == "primary") # each colour has text_color + WCAG contrast
154
+ # Every image found, tagged with type ("logo" | "icon" | "wordmark") and background ("light" | "dark" | "any")
155
+ for_dark_ui = [l for l in brand.logos or [] if l.background == "dark" and l.type != "icon"]
156
+ res.cached, res.stale # stale: cached result older than max_age, served because a fresh lookup failed
157
+ ```
158
+
159
+ ## Errors
160
+
161
+ Every failure raises `VerminError`:
162
+
163
+ ```python
164
+ from vermin import VerminError
165
+
166
+ try:
167
+ vermin.scrape("https://example.com")
168
+ except VerminError as err:
169
+ err.code # "rate_limited", "insufficient_credits", "blocked_by_site", … or "connection_error"
170
+ err.status # HTTP status (0 if no response)
171
+ err.request_id # quote this to support
172
+ err.retry_after_ms # server hint, when present
173
+ err.retryable # whether retrying could help
174
+ ```
175
+
176
+ Retries apply to 429, 5xx (including `capacity_exceeded`), timeouts and connection errors. By default the SDK makes up to 3 attempts, with jittered exponential backoff from 0.5 s up to 8 s. When the server sends a `retry_after_ms` hint (or a `Retry-After` header, seconds or HTTP date), the SDK waits exactly that long. It never retries `invalid_request`, `unauthorized`, `forbidden`, `not_found`, `insufficient_credits`, `spend_cap_reached`, `blocked_url`, `not_implemented`, `not_configured` or `extract_failed`, whatever the status. Failed requests cost 0 credits.
177
+
178
+ ## Webhooks
179
+
180
+ Crawl webhooks carry a `Vermin-Signature: t=<unix seconds>,v1=<hex>` header, where `v1` is the HMAC-SHA256 of `"<t>.<raw body>"` keyed with your signing secret (Dashboard → Webhooks). Verify it against the **raw** request body before trusting the payload. The helper compares in constant time, accepts any of several `v1=` values (secret rotation) and rejects timestamps more than 300 s from now (`tolerance`; `0` disables the check).
181
+
182
+ ```python
183
+ import os
184
+ from vermin import VerminError, verify_webhook
185
+
186
+ def handle(body: bytes, headers) -> int:
187
+ try:
188
+ event = verify_webhook(body, headers.get("Vermin-Signature"), os.environ["VERMIN_WEBHOOK_SECRET"])
189
+ except VerminError: # code == "invalid_signature"
190
+ return 400
191
+ if event["type"] == "crawl.page":
192
+ print(event["data"]["url"])
193
+ return 204
194
+ ```
195
+
196
+ `is_valid_webhook()` returns a bool instead, and `sign_webhook()` builds a header for your tests.
197
+
198
+ ## Development
199
+
200
+ ```sh
201
+ python3 -m venv .venv
202
+ .venv/bin/pip install -e ".[dev]"
203
+ .venv/bin/pytest # respx-mocked; every mock response is validated against the OpenAPI spec
204
+ ```
@@ -0,0 +1,56 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.21"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "vermin-sdk"
7
+ dynamic = ["version"]
8
+ description = "Official Python SDK for the Vermin web-data API: scrape, crawl, map and brand."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ keywords = ["vermin", "scrape", "crawl", "web-data", "ai", "llm", "sdk"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Programming Language :: Python :: 3.9",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = [
25
+ "httpx>=0.25,<1",
26
+ "pydantic>=2.5,<3",
27
+ "typing-extensions>=4.7",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ dev = [
32
+ "pytest>=8",
33
+ "pytest-asyncio>=0.23",
34
+ "respx>=0.21",
35
+ "jsonschema[format-nongpl]>=4.18",
36
+ "pyyaml>=6",
37
+ "mypy>=1.10",
38
+ ]
39
+
40
+ [project.urls]
41
+ Homepage = "https://vermin.dev"
42
+ Documentation = "https://vermin.dev/docs"
43
+
44
+ [tool.hatch.version]
45
+ path = "src/vermin/_version.py"
46
+
47
+ [tool.hatch.build.targets.wheel]
48
+ packages = ["src/vermin"]
49
+
50
+ [tool.hatch.build.targets.sdist]
51
+ include = ["src/vermin", "tests", "README.md", "pyproject.toml"]
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+ asyncio_mode = "auto"
56
+ filterwarnings = ["error"]
@@ -0,0 +1,90 @@
1
+ """Official Python SDK for Vermin (https://vermin.dev): web data for AI."""
2
+
3
+ from ._base import DEFAULT_BASE_URL, CrawlOptionsDict, MapOptionsDict, ScrapeOptionsDict, SearchOptionsDict
4
+ from ._client import AsyncCrawl, AsyncVermin, Crawl, Vermin
5
+ from ._errors import API_ERROR_CODES, CLIENT_ERROR_CODES, VerminError
6
+ from ._models import (
7
+ Action,
8
+ Brand,
9
+ BrandColor,
10
+ BrandFont,
11
+ BrandImage,
12
+ BrandResponse,
13
+ BrandSocial,
14
+ Chunk,
15
+ CrawlError,
16
+ CrawlEvent,
17
+ CrawlJob,
18
+ CrawlStatus,
19
+ Document,
20
+ ExtractResponse,
21
+ ExtractSource,
22
+ Location,
23
+ MapLink,
24
+ MapResponse,
25
+ Metadata,
26
+ Quality,
27
+ ScrapeOptions,
28
+ ScrapeResponse,
29
+ SearchHit,
30
+ SearchResponse,
31
+ VerminModel,
32
+ Webhook,
33
+ )
34
+ from ._version import __version__
35
+ from ._webhooks import (
36
+ WEBHOOK_SIGNATURE_HEADER,
37
+ WEBHOOK_TOLERANCE_SECONDS,
38
+ is_valid_webhook,
39
+ sign_webhook,
40
+ verify_webhook,
41
+ verify_webhook_signature,
42
+ )
43
+
44
+ __all__ = [
45
+ "API_ERROR_CODES",
46
+ "CLIENT_ERROR_CODES",
47
+ "DEFAULT_BASE_URL",
48
+ "Action",
49
+ "AsyncCrawl",
50
+ "AsyncVermin",
51
+ "Brand",
52
+ "BrandColor",
53
+ "BrandFont",
54
+ "BrandImage",
55
+ "BrandResponse",
56
+ "BrandSocial",
57
+ "Chunk",
58
+ "Crawl",
59
+ "CrawlError",
60
+ "CrawlEvent",
61
+ "CrawlJob",
62
+ "CrawlOptionsDict",
63
+ "CrawlStatus",
64
+ "Document",
65
+ "ExtractResponse",
66
+ "ExtractSource",
67
+ "Location",
68
+ "MapLink",
69
+ "MapOptionsDict",
70
+ "MapResponse",
71
+ "Metadata",
72
+ "Quality",
73
+ "ScrapeOptions",
74
+ "ScrapeOptionsDict",
75
+ "ScrapeResponse",
76
+ "SearchHit",
77
+ "SearchOptionsDict",
78
+ "SearchResponse",
79
+ "Vermin",
80
+ "VerminError",
81
+ "VerminModel",
82
+ "WEBHOOK_SIGNATURE_HEADER",
83
+ "WEBHOOK_TOLERANCE_SECONDS",
84
+ "Webhook",
85
+ "__version__",
86
+ "is_valid_webhook",
87
+ "sign_webhook",
88
+ "verify_webhook",
89
+ "verify_webhook_signature",
90
+ ]