getsnap 1.4.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.
getsnap-1.4.0/PKG-INFO ADDED
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: getsnap
3
+ Version: 1.4.0
4
+ Summary: Official Python SDK for getSnap.dev - Screenshot & PDF API
5
+ Author-email: "getSnap.dev" <support@getsnap.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://getsnap.dev
8
+ Project-URL: Documentation, https://getsnap.dev/docs/
9
+ Keywords: screenshot,api,pdf,webpage,capture,getsnap
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+
16
+ # getsnap
17
+
18
+ Official Python SDK for [getSnap.dev](https://getsnap.dev) — Screenshot & PDF API.
19
+
20
+ Zero dependencies. Uses only Python's standard library (`urllib`).
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install getsnap
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ from getsnap import GetSnap
32
+
33
+ snap = GetSnap("sk_live_YOUR_KEY")
34
+
35
+ # Take a screenshot
36
+ result = snap.screenshot(url="https://github.com", format="png")
37
+ print(result["url"]) # CDN URL to your screenshot
38
+ ```
39
+
40
+ ## Features
41
+
42
+ - Zero external dependencies (uses `urllib`)
43
+ - Python 3.8+
44
+ - Screenshot capture (URL or HTML)
45
+ - Binary response (raw image bytes)
46
+ - Batch capture (up to 100 URLs)
47
+ - Usage tracking
48
+ - All API parameters supported: `lazy_load`, `wait_for_selector`, `hide_selectors`, `remove_selectors`, `extract_text`, `extract_html`, `click_selector`, `scroll_to_selector`, and more
49
+
50
+ ## API
51
+
52
+ ### `GetSnap(api_key, base_url=None)`
53
+
54
+ Create a client instance.
55
+
56
+ - `api_key` — Your getSnap.dev key (starts with `sk_live_` or `sk_test_`)
57
+ - `base_url` — Custom base URL (default: `https://api.getsnap.dev`)
58
+
59
+ ### `snap.screenshot(**kwargs)`
60
+
61
+ Take a screenshot. Returns a dict with `url`, `cached`, `request_id`, and optionally `extracted_text`/`extracted_html`.
62
+
63
+ ```python
64
+ result = snap.screenshot(
65
+ url="https://example.com",
66
+ format="png",
67
+ full_page=True,
68
+ remove_popups=True,
69
+ lazy_load=True,
70
+ extract_text=True,
71
+ )
72
+ print(result["url"])
73
+ print(result["extracted_text"])
74
+ ```
75
+
76
+ ### `snap.screenshot_binary(**kwargs)`
77
+
78
+ Get raw image/PDF bytes.
79
+
80
+ ```python
81
+ data = snap.screenshot_binary(url="https://example.com", format="webp", quality=90)
82
+ with open("screenshot.webp", "wb") as f:
83
+ f.write(data)
84
+ ```
85
+
86
+ ### `snap.batch(urls, **kwargs)`
87
+
88
+ Capture multiple URLs in one request.
89
+
90
+ ```python
91
+ result = snap.batch(
92
+ urls=["https://github.com", "https://stripe.com", "https://vercel.com"],
93
+ format="png",
94
+ remove_popups=True,
95
+ )
96
+
97
+ print(f"{result['succeeded']}/{result['count']} captured")
98
+ for r in result["results"]:
99
+ print(f"{r['source_url']} -> {r['url']}")
100
+ ```
101
+
102
+ ### `snap.usage()`
103
+
104
+ Check current usage and quota.
105
+
106
+ ```python
107
+ usage = snap.usage()
108
+ print(f"{usage['used']}/{usage['limit']} ({usage['plan']})")
109
+ ```
110
+
111
+ ### `snap.status(request_id)`
112
+
113
+ Check status of an async (webhook) request.
114
+
115
+ ```python
116
+ status = snap.status("req_abc123")
117
+ ```
118
+
119
+ ## Error Handling
120
+
121
+ ```python
122
+ from getsnap import GetSnap, GetSnapError
123
+
124
+ snap = GetSnap("sk_live_YOUR_KEY")
125
+
126
+ try:
127
+ snap.screenshot(url="https://example.com", format="png")
128
+ except GetSnapError as e:
129
+ print(e.status, e.error, str(e))
130
+ # 402, "quota_exceeded", "Monthly limit reached..."
131
+ ```
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,120 @@
1
+ # getsnap
2
+
3
+ Official Python SDK for [getSnap.dev](https://getsnap.dev) — Screenshot & PDF API.
4
+
5
+ Zero dependencies. Uses only Python's standard library (`urllib`).
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install getsnap
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```python
16
+ from getsnap import GetSnap
17
+
18
+ snap = GetSnap("sk_live_YOUR_KEY")
19
+
20
+ # Take a screenshot
21
+ result = snap.screenshot(url="https://github.com", format="png")
22
+ print(result["url"]) # CDN URL to your screenshot
23
+ ```
24
+
25
+ ## Features
26
+
27
+ - Zero external dependencies (uses `urllib`)
28
+ - Python 3.8+
29
+ - Screenshot capture (URL or HTML)
30
+ - Binary response (raw image bytes)
31
+ - Batch capture (up to 100 URLs)
32
+ - Usage tracking
33
+ - All API parameters supported: `lazy_load`, `wait_for_selector`, `hide_selectors`, `remove_selectors`, `extract_text`, `extract_html`, `click_selector`, `scroll_to_selector`, and more
34
+
35
+ ## API
36
+
37
+ ### `GetSnap(api_key, base_url=None)`
38
+
39
+ Create a client instance.
40
+
41
+ - `api_key` — Your getSnap.dev key (starts with `sk_live_` or `sk_test_`)
42
+ - `base_url` — Custom base URL (default: `https://api.getsnap.dev`)
43
+
44
+ ### `snap.screenshot(**kwargs)`
45
+
46
+ Take a screenshot. Returns a dict with `url`, `cached`, `request_id`, and optionally `extracted_text`/`extracted_html`.
47
+
48
+ ```python
49
+ result = snap.screenshot(
50
+ url="https://example.com",
51
+ format="png",
52
+ full_page=True,
53
+ remove_popups=True,
54
+ lazy_load=True,
55
+ extract_text=True,
56
+ )
57
+ print(result["url"])
58
+ print(result["extracted_text"])
59
+ ```
60
+
61
+ ### `snap.screenshot_binary(**kwargs)`
62
+
63
+ Get raw image/PDF bytes.
64
+
65
+ ```python
66
+ data = snap.screenshot_binary(url="https://example.com", format="webp", quality=90)
67
+ with open("screenshot.webp", "wb") as f:
68
+ f.write(data)
69
+ ```
70
+
71
+ ### `snap.batch(urls, **kwargs)`
72
+
73
+ Capture multiple URLs in one request.
74
+
75
+ ```python
76
+ result = snap.batch(
77
+ urls=["https://github.com", "https://stripe.com", "https://vercel.com"],
78
+ format="png",
79
+ remove_popups=True,
80
+ )
81
+
82
+ print(f"{result['succeeded']}/{result['count']} captured")
83
+ for r in result["results"]:
84
+ print(f"{r['source_url']} -> {r['url']}")
85
+ ```
86
+
87
+ ### `snap.usage()`
88
+
89
+ Check current usage and quota.
90
+
91
+ ```python
92
+ usage = snap.usage()
93
+ print(f"{usage['used']}/{usage['limit']} ({usage['plan']})")
94
+ ```
95
+
96
+ ### `snap.status(request_id)`
97
+
98
+ Check status of an async (webhook) request.
99
+
100
+ ```python
101
+ status = snap.status("req_abc123")
102
+ ```
103
+
104
+ ## Error Handling
105
+
106
+ ```python
107
+ from getsnap import GetSnap, GetSnapError
108
+
109
+ snap = GetSnap("sk_live_YOUR_KEY")
110
+
111
+ try:
112
+ snap.screenshot(url="https://example.com", format="png")
113
+ except GetSnapError as e:
114
+ print(e.status, e.error, str(e))
115
+ # 402, "quota_exceeded", "Monthly limit reached..."
116
+ ```
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: getsnap
3
+ Version: 1.4.0
4
+ Summary: Official Python SDK for getSnap.dev - Screenshot & PDF API
5
+ Author-email: "getSnap.dev" <support@getsnap.dev>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://getsnap.dev
8
+ Project-URL: Documentation, https://getsnap.dev/docs/
9
+ Keywords: screenshot,api,pdf,webpage,capture,getsnap
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Requires-Python: >=3.8
14
+ Description-Content-Type: text/markdown
15
+
16
+ # getsnap
17
+
18
+ Official Python SDK for [getSnap.dev](https://getsnap.dev) — Screenshot & PDF API.
19
+
20
+ Zero dependencies. Uses only Python's standard library (`urllib`).
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install getsnap
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ from getsnap import GetSnap
32
+
33
+ snap = GetSnap("sk_live_YOUR_KEY")
34
+
35
+ # Take a screenshot
36
+ result = snap.screenshot(url="https://github.com", format="png")
37
+ print(result["url"]) # CDN URL to your screenshot
38
+ ```
39
+
40
+ ## Features
41
+
42
+ - Zero external dependencies (uses `urllib`)
43
+ - Python 3.8+
44
+ - Screenshot capture (URL or HTML)
45
+ - Binary response (raw image bytes)
46
+ - Batch capture (up to 100 URLs)
47
+ - Usage tracking
48
+ - All API parameters supported: `lazy_load`, `wait_for_selector`, `hide_selectors`, `remove_selectors`, `extract_text`, `extract_html`, `click_selector`, `scroll_to_selector`, and more
49
+
50
+ ## API
51
+
52
+ ### `GetSnap(api_key, base_url=None)`
53
+
54
+ Create a client instance.
55
+
56
+ - `api_key` — Your getSnap.dev key (starts with `sk_live_` or `sk_test_`)
57
+ - `base_url` — Custom base URL (default: `https://api.getsnap.dev`)
58
+
59
+ ### `snap.screenshot(**kwargs)`
60
+
61
+ Take a screenshot. Returns a dict with `url`, `cached`, `request_id`, and optionally `extracted_text`/`extracted_html`.
62
+
63
+ ```python
64
+ result = snap.screenshot(
65
+ url="https://example.com",
66
+ format="png",
67
+ full_page=True,
68
+ remove_popups=True,
69
+ lazy_load=True,
70
+ extract_text=True,
71
+ )
72
+ print(result["url"])
73
+ print(result["extracted_text"])
74
+ ```
75
+
76
+ ### `snap.screenshot_binary(**kwargs)`
77
+
78
+ Get raw image/PDF bytes.
79
+
80
+ ```python
81
+ data = snap.screenshot_binary(url="https://example.com", format="webp", quality=90)
82
+ with open("screenshot.webp", "wb") as f:
83
+ f.write(data)
84
+ ```
85
+
86
+ ### `snap.batch(urls, **kwargs)`
87
+
88
+ Capture multiple URLs in one request.
89
+
90
+ ```python
91
+ result = snap.batch(
92
+ urls=["https://github.com", "https://stripe.com", "https://vercel.com"],
93
+ format="png",
94
+ remove_popups=True,
95
+ )
96
+
97
+ print(f"{result['succeeded']}/{result['count']} captured")
98
+ for r in result["results"]:
99
+ print(f"{r['source_url']} -> {r['url']}")
100
+ ```
101
+
102
+ ### `snap.usage()`
103
+
104
+ Check current usage and quota.
105
+
106
+ ```python
107
+ usage = snap.usage()
108
+ print(f"{usage['used']}/{usage['limit']} ({usage['plan']})")
109
+ ```
110
+
111
+ ### `snap.status(request_id)`
112
+
113
+ Check status of an async (webhook) request.
114
+
115
+ ```python
116
+ status = snap.status("req_abc123")
117
+ ```
118
+
119
+ ## Error Handling
120
+
121
+ ```python
122
+ from getsnap import GetSnap, GetSnapError
123
+
124
+ snap = GetSnap("sk_live_YOUR_KEY")
125
+
126
+ try:
127
+ snap.screenshot(url="https://example.com", format="png")
128
+ except GetSnapError as e:
129
+ print(e.status, e.error, str(e))
130
+ # 402, "quota_exceeded", "Monthly limit reached..."
131
+ ```
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,7 @@
1
+ README.md
2
+ getsnap.py
3
+ pyproject.toml
4
+ getsnap.egg-info/PKG-INFO
5
+ getsnap.egg-info/SOURCES.txt
6
+ getsnap.egg-info/dependency_links.txt
7
+ getsnap.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ getsnap
@@ -0,0 +1,328 @@
1
+ """
2
+ getsnap - Official Python SDK for the getSnap.dev Screenshot & PDF API
3
+ https://getsnap.dev
4
+ """
5
+
6
+ import json
7
+ from typing import Any, Optional
8
+ from urllib import request as urllib_request
9
+ from urllib.error import HTTPError
10
+
11
+
12
+ class GetSnapError(Exception):
13
+ """Error from the getSnap.dev service."""
14
+
15
+ def __init__(self, status: int, error: str, message: str, details: Optional[dict] = None):
16
+ super().__init__(message)
17
+ self.status = status
18
+ self.error = error
19
+ self.details = details
20
+
21
+
22
+ class GetSnap:
23
+ """Client for the getSnap.dev Screenshot & PDF API."""
24
+
25
+ def __init__(self, api_key: str, base_url: str = "https://api.getsnap.dev"):
26
+ self.api_key = api_key
27
+ self.base_url = base_url
28
+
29
+ def screenshot(self, **kwargs) -> dict:
30
+ """
31
+ Take a screenshot of a URL or HTML content.
32
+
33
+ Common options:
34
+ url: URL to capture
35
+ html: HTML content to render
36
+ format: png | jpg | jpeg | webp | avif | pdf
37
+ viewport_width: 320-3840
38
+ viewport_height: 200-2160
39
+ full_page: bool
40
+ full_page_max_height: hard cap in pixels (0 = unlimited)
41
+ full_page_slices: split tall pages for AI vision workflows
42
+ full_page_slice_height: max height per slice (200-16000)
43
+ full_page_slice_overlap_height: pixel overlap between slices
44
+ device: desktop | mobile | tablet
45
+ delay: extra ms after page load (0-10000)
46
+ css: string of CSS to inject
47
+ js: string of JavaScript to execute
48
+ dark_mode: bool
49
+ reduced_motion: emulate prefers-reduced-motion:reduce
50
+ media_type: "screen" | "print" emulation
51
+ quality: 1-100 for lossy formats
52
+ remove_popups: heuristic banner/popup killer
53
+ click_accept: best-effort cookie-consent Accept clicker
54
+ press_escape: send Escape key after load
55
+ block_ads: bool
56
+ block_images / block_fonts / block_scripts / block_frames: bool
57
+ block_urls: list of URL substrings / wildcards to block
58
+ bypass_csp: disable page CSP (needed for some inject scenarios)
59
+ selector: CSS selector to capture only that element
60
+ fail_if_selector_missing: 500 if selector isn't found
61
+ clip: dict {"x", "y", "width", "height"} - capture region
62
+ device_scale_factor: 0.5-3
63
+ headers: dict of custom HTTP headers
64
+ cookies: list of cookie dicts
65
+ locale: browser locale (e.g. "de-DE")
66
+ timezone: browser timezone (e.g. "Europe/Berlin")
67
+ user_agent: custom UA string
68
+ wait_for_selector: wait for CSS selector
69
+ wait_for_network_idle: bool
70
+ lazy_load: auto-scroll to trigger lazy content
71
+ hide_selectors / remove_selectors: lists of CSS selectors
72
+ omit_background: transparent PNG
73
+ extract_text: return page innerText
74
+ extract_html: return page outerHTML
75
+ extract_metadata: return {title, favicon, open_graph,
76
+ fonts, http_status, http_status_text, http_headers}
77
+ click_selector: click element before capture
78
+ scroll_to_selector: scroll to element before capture
79
+ response_type: "url" (default) or "binary"
80
+ attachment_name: sets Content-Disposition on binary
81
+ responses / user S3 filename
82
+ webhook_url: async webhook URL
83
+ s3_upload: dict with your own S3 credentials
84
+
85
+ PDF-specific:
86
+ pdf_page_size: A0-A6, Letter, Legal, Tabloid, Ledger
87
+ pdf_orientation: portrait | landscape
88
+ pdf_margin_top / _right / _bottom / _left: CSS unit strings
89
+ pdf_background: print CSS backgrounds
90
+ pdf_scale: 0.1-2.0
91
+ pdf_show_header / pdf_header: Chromium templated HTML
92
+ pdf_show_footer / pdf_footer: Chromium templated HTML
93
+ pdf_page_range: e.g. "1-5,8,11-13"
94
+ pdf_title / pdf_subject / pdf_author / pdf_keywords /
95
+ pdf_creator: document metadata
96
+
97
+ Returns:
98
+ Dict with url, cached, request_id, response_time_ms,
99
+ and optional extracted_text / extracted_html /
100
+ extracted_metadata / slices / s3_key / s3_bucket.
101
+ """
102
+ return self._request("POST", "/v1/screenshot", kwargs)
103
+
104
+ def screenshot_binary(self, **kwargs) -> bytes:
105
+ """Take a screenshot and return raw binary image data."""
106
+ kwargs["response_type"] = "binary"
107
+ return self._request_binary("POST", "/v1/screenshot", kwargs)
108
+
109
+ def batch(self, urls: list, **kwargs) -> dict:
110
+ """
111
+ Capture screenshots of multiple URLs.
112
+
113
+ Args:
114
+ urls: List of URLs to capture (1-100)
115
+ **kwargs: Same options as screenshot() except url/html/selector
116
+
117
+ Returns:
118
+ Dict with batch_id, count, succeeded, failed, results
119
+ """
120
+ kwargs["urls"] = urls
121
+ return self._request("POST", "/v1/batch", kwargs)
122
+
123
+ def usage(self) -> dict:
124
+ """Check current usage and quota."""
125
+ return self._request("GET", "/v1/usage")
126
+
127
+ def status(self, request_id: str) -> dict:
128
+ """Check status of an async webhook request."""
129
+ return self._request("GET", f"/v1/screenshot/{request_id}")
130
+
131
+ # ------------------------------------------------------------------
132
+ # Scheduled captures (Phase 10)
133
+ #
134
+ # Example:
135
+ # gs = GetSnap("sk_live_...")
136
+ # s = gs.create_schedule(
137
+ # name="morning traffic dashboard",
138
+ # cron="0 9 * * MON-FRI",
139
+ # timezone="Europe/Berlin",
140
+ # options={"url": "https://dash.example.com", "full_page": True},
141
+ # webhook_url="https://my-app.com/webhooks/getsnap",
142
+ # webhook_secret="a-strong-shared-secret",
143
+ # )
144
+ # gs.run_schedule_now(s["id"])
145
+ # history = gs.list_schedule_runs(s["id"])
146
+ # ------------------------------------------------------------------
147
+ def create_schedule(
148
+ self,
149
+ name: str,
150
+ cron: str,
151
+ options: dict,
152
+ timezone: str = "UTC",
153
+ webhook_url: Optional[str] = None,
154
+ webhook_secret: Optional[str] = None,
155
+ is_active: bool = True,
156
+ ) -> dict:
157
+ """
158
+ Create a cron-driven scheduled capture.
159
+
160
+ Args:
161
+ name: Human-friendly name.
162
+ cron: Standard 5- or 6-field UNIX cron expression.
163
+ timezone: IANA timezone (default 'UTC').
164
+ options: ScreenshotOptions dict (url or html required).
165
+ webhook_url: Optional URL to POST each result to.
166
+ webhook_secret: If set, deliveries include
167
+ X-Webhook-Signature: sha256=hex(hmac(secret, ts + "." + body))
168
+ and X-Webhook-Timestamp headers.
169
+ is_active: Start immediately if True (default), paused otherwise.
170
+ """
171
+ body: dict = {
172
+ "name": name,
173
+ "cron": cron,
174
+ "timezone": timezone,
175
+ "options": options,
176
+ "is_active": is_active,
177
+ }
178
+ if webhook_url is not None:
179
+ body["webhook_url"] = webhook_url
180
+ if webhook_secret is not None:
181
+ body["webhook_secret"] = webhook_secret
182
+ return self._request("POST", "/v1/schedules", body)
183
+
184
+ def list_schedules(self) -> dict:
185
+ """List all your scheduled captures."""
186
+ return self._request("GET", "/v1/schedules")
187
+
188
+ def get_schedule(self, schedule_id: str) -> dict:
189
+ """Fetch one scheduled capture."""
190
+ return self._request("GET", f"/v1/schedules/{schedule_id}")
191
+
192
+ def update_schedule(self, schedule_id: str, **fields) -> dict:
193
+ """
194
+ Update fields on a schedule.
195
+
196
+ Accepted kwargs: name, cron, timezone, options,
197
+ webhook_url, webhook_secret, is_active.
198
+ Pass empty strings to clear webhook_url / webhook_secret.
199
+ """
200
+ return self._request("PATCH", f"/v1/schedules/{schedule_id}", fields)
201
+
202
+ def delete_schedule(self, schedule_id: str) -> dict:
203
+ """Delete a schedule (also removes it from the queue)."""
204
+ return self._request("DELETE", f"/v1/schedules/{schedule_id}")
205
+
206
+ def run_schedule_now(self, schedule_id: str) -> dict:
207
+ """Fire a scheduled capture immediately (ad-hoc, does not touch the cron)."""
208
+ return self._request("POST", f"/v1/schedules/{schedule_id}/run")
209
+
210
+ def list_schedule_runs(self, schedule_id: str) -> dict:
211
+ """Recent execution history (latest 100 runs) for a schedule."""
212
+ return self._request("GET", f"/v1/schedules/{schedule_id}/runs")
213
+
214
+ # ------------------------------------------------------------------
215
+ # Scrolling video (Phase 11)
216
+ #
217
+ # Example:
218
+ # result = gs.video(
219
+ # url="https://en.wikipedia.org/wiki/Screenshot",
220
+ # format="mp4",
221
+ # duration_ms=5000,
222
+ # fps=15,
223
+ # viewport_width=800,
224
+ # viewport_height=500,
225
+ # )
226
+ # print(result["url"], result["frame_count"], result["bytes"])
227
+ #
228
+ # Costs 1 credit per second of video (rounded up, min 1). Sync mode
229
+ # returns the CDN URL; pass webhook_url for async delivery.
230
+ # ------------------------------------------------------------------
231
+ def video(self, **kwargs) -> dict:
232
+ """
233
+ Capture a scrolling video (MP4 or GIF) of a URL.
234
+
235
+ Common kwargs:
236
+ url: required, must be a valid HTTP/HTTPS URL
237
+ format: "mp4" (default) or "gif"
238
+ viewport_width: 320-1920 (default 1280)
239
+ viewport_height: 200-1080 (default 720)
240
+ device: desktop | mobile | tablet
241
+ device_scale_factor: 0.5-2 (default 1)
242
+ fps: 10-30 (default 15)
243
+ duration_ms: 2000-30000 (default 5000)
244
+ delay: extra ms after page load (0-10000)
245
+ quality: 1-100 (MP4 CRF / GIF palette)
246
+ remove_popups: bool (default True)
247
+ block_ads: bool (default True)
248
+ dark_mode: bool
249
+ css / js: string injections
250
+ headers: dict of HTTP headers
251
+ cookies: list of cookie dicts
252
+ locale, timezone, user_agent, wait_for_selector,
253
+ wait_for_network_idle: same semantics as screenshot()
254
+ response_type: "url" (default) or "binary"
255
+ attachment_name: filename hint on binary / S3
256
+ webhook_url: async webhook delivery URL
257
+ s3_upload: dict with your own S3 credentials
258
+
259
+ Returns dict with url, request_id, format, duration_ms,
260
+ frame_count, fps, bytes, credits_charged, response_time_ms.
261
+ In binary mode returns raw bytes (use video_binary()).
262
+ In webhook mode returns 202 status envelope with credits_reserved.
263
+ """
264
+ return self._request("POST", "/v1/video", kwargs)
265
+
266
+ def video_binary(self, **kwargs) -> bytes:
267
+ """Capture a scrolling video and return the raw MP4/GIF bytes."""
268
+ kwargs["response_type"] = "binary"
269
+ return self._request_binary("POST", "/v1/video", kwargs)
270
+
271
+ def _request(self, method: str, path: str, body: Optional[dict] = None) -> dict:
272
+ url = f"{self.base_url}{path}"
273
+ data = json.dumps(body).encode("utf-8") if body else None
274
+
275
+ req = urllib_request.Request(url, data=data, method=method)
276
+ req.add_header("Content-Type", "application/json")
277
+ req.add_header("x-api-key", self.api_key)
278
+
279
+ try:
280
+ with urllib_request.urlopen(req) as resp:
281
+ return json.loads(resp.read().decode("utf-8"))
282
+ except HTTPError as e:
283
+ try:
284
+ error_body = json.loads(e.read().decode("utf-8"))
285
+ raise GetSnapError(
286
+ status=e.code,
287
+ error=error_body.get("error", "unknown"),
288
+ message=error_body.get("message", str(e)),
289
+ details=error_body.get("details"),
290
+ ) from e
291
+ except (json.JSONDecodeError, UnicodeDecodeError):
292
+ raise GetSnapError(
293
+ status=e.code,
294
+ error="http_error",
295
+ message=str(e),
296
+ ) from e
297
+
298
+ def _request_binary(self, method: str, path: str, body: Optional[dict] = None) -> bytes:
299
+ url = f"{self.base_url}{path}"
300
+ data = json.dumps(body).encode("utf-8") if body else None
301
+
302
+ req = urllib_request.Request(url, data=data, method=method)
303
+ req.add_header("Content-Type", "application/json")
304
+ req.add_header("x-api-key", self.api_key)
305
+
306
+ try:
307
+ with urllib_request.urlopen(req) as resp:
308
+ return resp.read()
309
+ except HTTPError as e:
310
+ try:
311
+ error_body = json.loads(e.read().decode("utf-8"))
312
+ raise GetSnapError(
313
+ status=e.code,
314
+ error=error_body.get("error", "unknown"),
315
+ message=error_body.get("message", str(e)),
316
+ details=error_body.get("details"),
317
+ ) from e
318
+ except (json.JSONDecodeError, UnicodeDecodeError):
319
+ raise GetSnapError(
320
+ status=e.code,
321
+ error="http_error",
322
+ message=str(e),
323
+ ) from e
324
+
325
+
326
+ # Backwards-compat aliases (in case anything imported the old names locally)
327
+ SnapAPI = GetSnap
328
+ SnapAPIError = GetSnapError
@@ -0,0 +1,25 @@
1
+ [project]
2
+ name = "getsnap"
3
+ version = "1.4.0"
4
+ description = "Official Python SDK for getSnap.dev - Screenshot & PDF API"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.8"
8
+ authors = [{ name = "getSnap.dev", email = "support@getsnap.dev" }]
9
+ keywords = ["screenshot", "api", "pdf", "webpage", "capture", "getsnap"]
10
+ classifiers = [
11
+ "Development Status :: 5 - Production/Stable",
12
+ "Intended Audience :: Developers",
13
+ "Programming Language :: Python :: 3",
14
+ ]
15
+
16
+ [project.urls]
17
+ Homepage = "https://getsnap.dev"
18
+ Documentation = "https://getsnap.dev/docs/"
19
+
20
+ [build-system]
21
+ requires = ["setuptools>=61.0"]
22
+ build-backend = "setuptools.build_meta"
23
+
24
+ [tool.setuptools]
25
+ py-modules = ["getsnap"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+