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 +135 -0
- getsnap-1.4.0/README.md +120 -0
- getsnap-1.4.0/getsnap.egg-info/PKG-INFO +135 -0
- getsnap-1.4.0/getsnap.egg-info/SOURCES.txt +7 -0
- getsnap-1.4.0/getsnap.egg-info/dependency_links.txt +1 -0
- getsnap-1.4.0/getsnap.egg-info/top_level.txt +1 -0
- getsnap-1.4.0/getsnap.py +328 -0
- getsnap-1.4.0/pyproject.toml +25 -0
- getsnap-1.4.0/setup.cfg +4 -0
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
|
getsnap-1.4.0/README.md
ADDED
|
@@ -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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
getsnap
|
getsnap-1.4.0/getsnap.py
ADDED
|
@@ -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"]
|
getsnap-1.4.0/setup.cfg
ADDED