snapapi-sdk 1.3.0
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.
- package/README.md +131 -0
- package/index.d.ts +177 -0
- package/index.js +300 -0
- package/package.json +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# snapapi-js
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for [SnapAPI](https://snapapi.tech) — capture any website as an image.
|
|
4
|
+
|
|
5
|
+
Zero dependencies. Uses native `fetch` (Node 18+).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install snapapi-js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
const SnapAPI = require('snapapi-js');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
|
|
19
|
+
const snap = new SnapAPI('snap_your_key_here');
|
|
20
|
+
|
|
21
|
+
// Basic screenshot
|
|
22
|
+
const image = await snap.screenshot('example.com');
|
|
23
|
+
fs.writeFileSync('screenshot.png', image);
|
|
24
|
+
|
|
25
|
+
// With options
|
|
26
|
+
const webp = await snap.screenshot('example.com', {
|
|
27
|
+
format: 'webp',
|
|
28
|
+
darkMode: true,
|
|
29
|
+
fullPage: true,
|
|
30
|
+
});
|
|
31
|
+
fs.writeFileSync('screenshot.webp', webp);
|
|
32
|
+
|
|
33
|
+
// Mobile screenshot (iPhone 14)
|
|
34
|
+
const mobile = await snap.screenshot('example.com', { device: 'iphone14' });
|
|
35
|
+
|
|
36
|
+
// Capture a specific element
|
|
37
|
+
const header = await snap.screenshot('example.com', { selector: 'header' });
|
|
38
|
+
|
|
39
|
+
// Get structured page data (great for AI agents)
|
|
40
|
+
const meta = await snap.screenshot('example.com', { meta: true });
|
|
41
|
+
console.log(meta.title, meta.description);
|
|
42
|
+
console.log(meta.headings); // [{level: 1, text: "..."}, ...]
|
|
43
|
+
console.log(meta.links); // [{text: "...", href: "..."}, ...]
|
|
44
|
+
console.log(meta.text); // Extracted visible text (first 2000 chars)
|
|
45
|
+
|
|
46
|
+
// Check your usage
|
|
47
|
+
const usage = await snap.usage();
|
|
48
|
+
console.log(`${usage.usage.count} screenshots this month`);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Screenshot Options
|
|
52
|
+
|
|
53
|
+
| Option | Type | Description |
|
|
54
|
+
|------------|---------|--------------------------------------|
|
|
55
|
+
| `width` | number | Viewport width in pixels |
|
|
56
|
+
| `height` | number | Viewport height in pixels |
|
|
57
|
+
| `format` | string | `"png"`, `"jpeg"`, or `"webp"` |
|
|
58
|
+
| `quality` | number | Image quality 1-100 (jpeg/webp only) |
|
|
59
|
+
| `fullPage` | boolean | Capture the full scrollable page |
|
|
60
|
+
| `delay` | number | Wait ms before capturing |
|
|
61
|
+
| `darkMode` | boolean | Emulate dark color scheme |
|
|
62
|
+
| `blockAds` | boolean | Block common ad networks |
|
|
63
|
+
| `cache` | boolean | Use cached result if available |
|
|
64
|
+
| `selector` | string | CSS selector to capture |
|
|
65
|
+
| `device` | string | Device preset: `"iphone14"`, `"pixel7"`, `"ipad"`, `"desktop"`, etc. |
|
|
66
|
+
| `meta` | boolean | Return page metadata as JSON |
|
|
67
|
+
|
|
68
|
+
## Error Handling
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
const { SnapAPIError } = require('snapapi-js');
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const image = await snap.screenshot('example.com');
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (err instanceof SnapAPIError) {
|
|
77
|
+
console.error(err.status, err.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Scheduled Monitors
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
// Create a monitor — captures every 15 minutes
|
|
86
|
+
const monitor = await snap.createMonitor({
|
|
87
|
+
url: 'https://example.com',
|
|
88
|
+
name: 'Homepage',
|
|
89
|
+
interval_minutes: 15,
|
|
90
|
+
params: { format: 'webp', fullPage: true },
|
|
91
|
+
webhook_url: 'https://your-app.com/webhook',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// List all monitors
|
|
95
|
+
const monitors = await snap.listMonitors();
|
|
96
|
+
|
|
97
|
+
// Pause / resume
|
|
98
|
+
await snap.updateMonitor(monitor.id, { status: 'paused' });
|
|
99
|
+
await snap.updateMonitor(monitor.id, { status: 'active' });
|
|
100
|
+
|
|
101
|
+
// Browse snapshots
|
|
102
|
+
const { snapshots } = await snap.listSnapshots(monitor.id, { limit: 10 });
|
|
103
|
+
|
|
104
|
+
// Download a snapshot image
|
|
105
|
+
const image = await snap.getSnapshot(monitor.id, snapshots[0].id);
|
|
106
|
+
fs.writeFileSync('snapshot.webp', image);
|
|
107
|
+
|
|
108
|
+
// Delete a monitor (cascades to snapshots)
|
|
109
|
+
await snap.deleteMonitor(monitor.id);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> Monitors require **Starter** tier or above. See [pricing](https://snapapi.tech/#pricing) for limits.
|
|
113
|
+
|
|
114
|
+
## Account Management
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
// Full account info
|
|
118
|
+
const account = await snap.account();
|
|
119
|
+
|
|
120
|
+
// Update settings
|
|
121
|
+
await snap.updateSettings({ name: 'My App' });
|
|
122
|
+
|
|
123
|
+
// Sign up (no API key needed)
|
|
124
|
+
const snap = new SnapAPI('placeholder');
|
|
125
|
+
const result = await snap.signup('you@example.com');
|
|
126
|
+
console.log(result.key); // Your new API key
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## License
|
|
130
|
+
|
|
131
|
+
MIT
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
declare class SnapAPIError extends Error {
|
|
2
|
+
name: 'SnapAPIError';
|
|
3
|
+
status: number;
|
|
4
|
+
body: unknown;
|
|
5
|
+
constructor(message: string, status: number, body: unknown);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface SnapAPIOptions {
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ScreenshotOptions {
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
format?: 'png' | 'jpeg' | 'webp';
|
|
16
|
+
quality?: number;
|
|
17
|
+
fullPage?: boolean;
|
|
18
|
+
delay?: number;
|
|
19
|
+
darkMode?: boolean;
|
|
20
|
+
blockAds?: boolean;
|
|
21
|
+
cache?: boolean;
|
|
22
|
+
selector?: string;
|
|
23
|
+
device?: 'iphone14' | 'iphone14pro' | 'iphone15' | 'pixel7' | 'pixel8' | 'galaxys23' | 'ipad' | 'ipadpro' | 'desktop';
|
|
24
|
+
meta?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface MetadataResult {
|
|
28
|
+
url: string;
|
|
29
|
+
title: string;
|
|
30
|
+
description: string;
|
|
31
|
+
og_title: string;
|
|
32
|
+
og_image: string;
|
|
33
|
+
og_type: string;
|
|
34
|
+
favicon: string;
|
|
35
|
+
viewport: string;
|
|
36
|
+
canonical: string;
|
|
37
|
+
language: string;
|
|
38
|
+
headings: Array<{ level: number; text: string }>;
|
|
39
|
+
links: Array<{ text: string; href: string }>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface RenderOptions {
|
|
43
|
+
width?: number;
|
|
44
|
+
height?: number;
|
|
45
|
+
format?: 'png' | 'jpeg' | 'webp';
|
|
46
|
+
quality?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface UsageResponse {
|
|
50
|
+
tier: string;
|
|
51
|
+
usage: {
|
|
52
|
+
month: string;
|
|
53
|
+
count: number;
|
|
54
|
+
};
|
|
55
|
+
limits: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface SignupResponse {
|
|
59
|
+
key: string;
|
|
60
|
+
tier: string;
|
|
61
|
+
limit: number;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface AccountSettings {
|
|
65
|
+
name?: string;
|
|
66
|
+
allowed_domains?: string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface MonitorParams {
|
|
70
|
+
url: string;
|
|
71
|
+
name?: string;
|
|
72
|
+
interval_minutes: number;
|
|
73
|
+
params?: Partial<ScreenshotOptions>;
|
|
74
|
+
webhook_url?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface MonitorUpdateParams {
|
|
78
|
+
name?: string;
|
|
79
|
+
interval_minutes?: number;
|
|
80
|
+
status?: 'active' | 'paused';
|
|
81
|
+
params?: Partial<ScreenshotOptions>;
|
|
82
|
+
webhook_url?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface Monitor {
|
|
86
|
+
id: string;
|
|
87
|
+
name: string;
|
|
88
|
+
url: string;
|
|
89
|
+
interval_minutes: number;
|
|
90
|
+
status: string;
|
|
91
|
+
params: Record<string, unknown>;
|
|
92
|
+
webhook_url: string | null;
|
|
93
|
+
created_at: string;
|
|
94
|
+
last_run: string | null;
|
|
95
|
+
next_run: string | null;
|
|
96
|
+
run_count: number;
|
|
97
|
+
error_count: number;
|
|
98
|
+
last_error: string | null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface Snapshot {
|
|
102
|
+
id: string;
|
|
103
|
+
monitor_id: string;
|
|
104
|
+
taken_at: string;
|
|
105
|
+
file_size: number;
|
|
106
|
+
status: string;
|
|
107
|
+
error: string | null;
|
|
108
|
+
meta: Record<string, unknown>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
interface SnapshotListResponse {
|
|
112
|
+
snapshots: Snapshot[];
|
|
113
|
+
total: number;
|
|
114
|
+
limit: number;
|
|
115
|
+
offset: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
declare class SnapAPI {
|
|
119
|
+
constructor(apiKey: string, options?: SnapAPIOptions);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Capture a screenshot of a URL.
|
|
123
|
+
* Returns a Buffer (image) when meta is false/omitted,
|
|
124
|
+
* or a metadata object when meta is true.
|
|
125
|
+
*/
|
|
126
|
+
screenshot(url: string, options?: ScreenshotOptions & { meta?: false }): Promise<Buffer>;
|
|
127
|
+
screenshot(url: string, options: ScreenshotOptions & { meta: true }): Promise<Record<string, unknown>>;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Extract metadata from a URL without taking a screenshot.
|
|
131
|
+
* Available on all plans.
|
|
132
|
+
*/
|
|
133
|
+
metadata(url: string): Promise<MetadataResult>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Render raw HTML to an image. Requires Starter tier or above.
|
|
137
|
+
*/
|
|
138
|
+
render(html: string, options?: RenderOptions): Promise<Buffer>;
|
|
139
|
+
|
|
140
|
+
/** Get current usage stats for this API key. */
|
|
141
|
+
usage(): Promise<UsageResponse>;
|
|
142
|
+
|
|
143
|
+
/** Get full account information. */
|
|
144
|
+
account(): Promise<Record<string, unknown>>;
|
|
145
|
+
|
|
146
|
+
/** Update account settings. */
|
|
147
|
+
updateSettings(settings: AccountSettings): Promise<Record<string, unknown>>;
|
|
148
|
+
|
|
149
|
+
/** Sign up for a free SnapAPI account. Does not require an API key. */
|
|
150
|
+
signup(email: string): Promise<SignupResponse>;
|
|
151
|
+
|
|
152
|
+
// ─── Monitors (Scheduled Screenshots) ───
|
|
153
|
+
|
|
154
|
+
/** Create a new monitor. Requires Starter tier or above. */
|
|
155
|
+
createMonitor(params: MonitorParams): Promise<Monitor>;
|
|
156
|
+
|
|
157
|
+
/** List all monitors for this API key. */
|
|
158
|
+
listMonitors(): Promise<Monitor[]>;
|
|
159
|
+
|
|
160
|
+
/** Get a single monitor by ID. */
|
|
161
|
+
getMonitor(id: string): Promise<Monitor>;
|
|
162
|
+
|
|
163
|
+
/** Update a monitor (name, interval, status, params, webhook_url). */
|
|
164
|
+
updateMonitor(id: string, updates: MonitorUpdateParams): Promise<Monitor>;
|
|
165
|
+
|
|
166
|
+
/** Delete a monitor and all its snapshots. */
|
|
167
|
+
deleteMonitor(id: string): Promise<void>;
|
|
168
|
+
|
|
169
|
+
/** List snapshots for a monitor. */
|
|
170
|
+
listSnapshots(monitorId: string, options?: { limit?: number; offset?: number }): Promise<SnapshotListResponse>;
|
|
171
|
+
|
|
172
|
+
/** Download a specific snapshot image. */
|
|
173
|
+
getSnapshot(monitorId: string, snapshotId: string): Promise<Buffer>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export = SnapAPI;
|
|
177
|
+
export { SnapAPIError };
|
package/index.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_BASE_URL = 'https://snapapi.tech';
|
|
4
|
+
|
|
5
|
+
class SnapAPIError extends Error {
|
|
6
|
+
constructor(message, status, body) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'SnapAPIError';
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.body = body;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function toSnakeCase(str) {
|
|
15
|
+
return str.replace(/[A-Z]/g, (ch) => '_' + ch.toLowerCase());
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
class SnapAPI {
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} apiKey - Your SnapAPI key (e.g. "snap_...")
|
|
21
|
+
* @param {object} [options]
|
|
22
|
+
* @param {string} [options.baseUrl] - API base URL
|
|
23
|
+
*/
|
|
24
|
+
constructor(apiKey, options = {}) {
|
|
25
|
+
if (!apiKey || typeof apiKey !== 'string') {
|
|
26
|
+
throw new SnapAPIError('API key is required', 0, null);
|
|
27
|
+
}
|
|
28
|
+
this.apiKey = apiKey;
|
|
29
|
+
this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Internal request helper.
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
async _request(method, path, { query, json, needsAuth = true } = {}) {
|
|
37
|
+
let url = `${this.baseUrl}${path}`;
|
|
38
|
+
|
|
39
|
+
if (query) {
|
|
40
|
+
const params = new URLSearchParams();
|
|
41
|
+
for (const [k, v] of Object.entries(query)) {
|
|
42
|
+
if (v !== undefined && v !== null) {
|
|
43
|
+
params.set(k, String(v));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const qs = params.toString();
|
|
47
|
+
if (qs) url += '?' + qs;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const headers = {};
|
|
51
|
+
if (needsAuth) {
|
|
52
|
+
headers['x-api-key'] = this.apiKey;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const fetchOptions = { method, headers };
|
|
56
|
+
|
|
57
|
+
if (json) {
|
|
58
|
+
headers['content-type'] = 'application/json';
|
|
59
|
+
fetchOptions.body = JSON.stringify(json);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const res = await fetch(url, fetchOptions);
|
|
63
|
+
|
|
64
|
+
if (!res.ok) {
|
|
65
|
+
let body = null;
|
|
66
|
+
let message = `SnapAPI request failed: ${res.status} ${res.statusText}`;
|
|
67
|
+
try {
|
|
68
|
+
body = await res.json();
|
|
69
|
+
if (body.error) message = body.error;
|
|
70
|
+
else if (body.message) message = body.message;
|
|
71
|
+
} catch {
|
|
72
|
+
// response wasn't JSON
|
|
73
|
+
}
|
|
74
|
+
throw new SnapAPIError(message, res.status, body);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return res;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Capture a screenshot of a URL.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} url - The webpage URL to capture
|
|
84
|
+
* @param {object} [options]
|
|
85
|
+
* @param {number} [options.width] - Viewport width in px
|
|
86
|
+
* @param {number} [options.height] - Viewport height in px
|
|
87
|
+
* @param {string} [options.format] - "png" | "jpeg" | "webp"
|
|
88
|
+
* @param {number} [options.quality] - Image quality 1-100 (jpeg/webp)
|
|
89
|
+
* @param {boolean} [options.fullPage] - Capture full scrollable page
|
|
90
|
+
* @param {number} [options.delay] - Wait ms before capture
|
|
91
|
+
* @param {boolean} [options.darkMode] - Emulate dark color scheme
|
|
92
|
+
* @param {boolean} [options.blockAds] - Block common ad networks
|
|
93
|
+
* @param {boolean} [options.cache] - Use cached result if available
|
|
94
|
+
* @param {string} [options.selector] - CSS selector to capture
|
|
95
|
+
* @param {string} [options.device] - Device preset: "iphone14", "pixel7", "ipad", "desktop", etc.
|
|
96
|
+
* @param {boolean} [options.meta] - Return page metadata instead of image
|
|
97
|
+
* @returns {Promise<Buffer|object>} Image buffer, or metadata object when meta=true
|
|
98
|
+
*/
|
|
99
|
+
async screenshot(url, options = {}) {
|
|
100
|
+
if (!url || typeof url !== 'string') {
|
|
101
|
+
throw new SnapAPIError('URL is required', 0, null);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const query = { url };
|
|
105
|
+
|
|
106
|
+
for (const [key, value] of Object.entries(options)) {
|
|
107
|
+
if (value === undefined || value === null) continue;
|
|
108
|
+
const snakeKey = toSnakeCase(key);
|
|
109
|
+
query[snakeKey] = value;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const wantMeta = !!options.meta;
|
|
113
|
+
|
|
114
|
+
const res = await this._request('GET', '/v1/screenshot', { query });
|
|
115
|
+
|
|
116
|
+
if (wantMeta) {
|
|
117
|
+
return res.json();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const arrayBuffer = await res.arrayBuffer();
|
|
121
|
+
return Buffer.from(arrayBuffer);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Extract metadata from a URL without taking a screenshot.
|
|
126
|
+
* Returns title, description, OG tags, favicon, headings, links, and more.
|
|
127
|
+
* Available on all plans.
|
|
128
|
+
* @param {string} url - The webpage URL to extract metadata from
|
|
129
|
+
* @returns {Promise<object>}
|
|
130
|
+
*/
|
|
131
|
+
async metadata(url) {
|
|
132
|
+
if (!url || typeof url !== 'string') {
|
|
133
|
+
throw new SnapAPIError('URL is required', 0, null);
|
|
134
|
+
}
|
|
135
|
+
const res = await this._request('GET', '/v1/metadata', { query: { url } });
|
|
136
|
+
return res.json();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Render raw HTML to an image (PNG, JPEG, or WebP).
|
|
141
|
+
* Perfect for OG cards, email previews, and dashboard exports.
|
|
142
|
+
* Requires Starter tier or above.
|
|
143
|
+
* @param {string} html - Full HTML string to render (max 2MB)
|
|
144
|
+
* @param {object} [options]
|
|
145
|
+
* @param {number} [options.width] - Viewport width in px (default 1200)
|
|
146
|
+
* @param {number} [options.height] - Viewport height in px (default 630)
|
|
147
|
+
* @param {string} [options.format] - "png" | "jpeg" | "webp" (default "png")
|
|
148
|
+
* @param {number} [options.quality] - Image quality 1-100 for jpeg/webp (default 90)
|
|
149
|
+
* @returns {Promise<Buffer>}
|
|
150
|
+
*/
|
|
151
|
+
async render(html, options = {}) {
|
|
152
|
+
if (!html || typeof html !== 'string') {
|
|
153
|
+
throw new SnapAPIError('html is required', 0, null);
|
|
154
|
+
}
|
|
155
|
+
const res = await this._request('POST', '/v1/render', {
|
|
156
|
+
json: {
|
|
157
|
+
html,
|
|
158
|
+
width: options.width || 1200,
|
|
159
|
+
height: options.height || 630,
|
|
160
|
+
format: options.format || 'png',
|
|
161
|
+
quality: options.quality || 90,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
const arrayBuffer = await res.arrayBuffer();
|
|
165
|
+
return Buffer.from(arrayBuffer);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get current usage stats for this API key.
|
|
170
|
+
* @returns {Promise<{ tier: string, usage: { month: string, count: number }, limits: object }>}
|
|
171
|
+
*/
|
|
172
|
+
async usage() {
|
|
173
|
+
const res = await this._request('GET', '/v1/usage');
|
|
174
|
+
return res.json();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get full account information.
|
|
179
|
+
* @returns {Promise<object>}
|
|
180
|
+
*/
|
|
181
|
+
async account() {
|
|
182
|
+
const res = await this._request('GET', '/v1/account');
|
|
183
|
+
return res.json();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Update account settings.
|
|
188
|
+
* @param {object} settings
|
|
189
|
+
* @param {string} [settings.name]
|
|
190
|
+
* @param {string[]} [settings.allowed_domains]
|
|
191
|
+
* @returns {Promise<object>}
|
|
192
|
+
*/
|
|
193
|
+
async updateSettings(settings) {
|
|
194
|
+
if (!settings || typeof settings !== 'object') {
|
|
195
|
+
throw new SnapAPIError('Settings object is required', 0, null);
|
|
196
|
+
}
|
|
197
|
+
const res = await this._request('PATCH', '/v1/account', { json: settings });
|
|
198
|
+
return res.json();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ─── Monitors (Scheduled Screenshots) ───
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Create a new monitor.
|
|
205
|
+
* @param {object} params
|
|
206
|
+
* @param {string} params.url - URL to monitor
|
|
207
|
+
* @param {string} [params.name] - Display name
|
|
208
|
+
* @param {number} params.interval_minutes - Capture interval in minutes
|
|
209
|
+
* @param {object} [params.params] - Screenshot options (width, height, format, etc.)
|
|
210
|
+
* @param {string} [params.webhook_url] - Webhook URL for notifications
|
|
211
|
+
* @returns {Promise<object>}
|
|
212
|
+
*/
|
|
213
|
+
async createMonitor(params) {
|
|
214
|
+
const res = await this._request('POST', '/v1/monitors', { json: params });
|
|
215
|
+
return res.json();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* List all monitors for this API key.
|
|
220
|
+
* @returns {Promise<object[]>}
|
|
221
|
+
*/
|
|
222
|
+
async listMonitors() {
|
|
223
|
+
const res = await this._request('GET', '/v1/monitors');
|
|
224
|
+
return res.json();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get a single monitor by ID.
|
|
229
|
+
* @param {string} id
|
|
230
|
+
* @returns {Promise<object>}
|
|
231
|
+
*/
|
|
232
|
+
async getMonitor(id) {
|
|
233
|
+
const res = await this._request('GET', `/v1/monitors/${id}`);
|
|
234
|
+
return res.json();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Update a monitor.
|
|
239
|
+
* @param {string} id
|
|
240
|
+
* @param {object} updates - Fields to update (name, interval_minutes, status, params, webhook_url)
|
|
241
|
+
* @returns {Promise<object>}
|
|
242
|
+
*/
|
|
243
|
+
async updateMonitor(id, updates) {
|
|
244
|
+
const res = await this._request('PATCH', `/v1/monitors/${id}`, { json: updates });
|
|
245
|
+
return res.json();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Delete a monitor and all its snapshots.
|
|
250
|
+
* @param {string} id
|
|
251
|
+
* @returns {Promise<void>}
|
|
252
|
+
*/
|
|
253
|
+
async deleteMonitor(id) {
|
|
254
|
+
await this._request('DELETE', `/v1/monitors/${id}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* List snapshots for a monitor.
|
|
259
|
+
* @param {string} monitorId
|
|
260
|
+
* @param {object} [options]
|
|
261
|
+
* @param {number} [options.limit] - Max results (default 50)
|
|
262
|
+
* @param {number} [options.offset] - Pagination offset
|
|
263
|
+
* @returns {Promise<object>}
|
|
264
|
+
*/
|
|
265
|
+
async listSnapshots(monitorId, options = {}) {
|
|
266
|
+
const res = await this._request('GET', `/v1/monitors/${monitorId}/snapshots`, { query: options });
|
|
267
|
+
return res.json();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Download a specific snapshot image.
|
|
272
|
+
* @param {string} monitorId
|
|
273
|
+
* @param {string} snapshotId
|
|
274
|
+
* @returns {Promise<Buffer>}
|
|
275
|
+
*/
|
|
276
|
+
async getSnapshot(monitorId, snapshotId) {
|
|
277
|
+
const res = await this._request('GET', `/v1/monitors/${monitorId}/snapshots/${snapshotId}`);
|
|
278
|
+
const arrayBuffer = await res.arrayBuffer();
|
|
279
|
+
return Buffer.from(arrayBuffer);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Sign up for a free SnapAPI account. Does not require an API key.
|
|
284
|
+
* @param {string} email
|
|
285
|
+
* @returns {Promise<{ key: string, tier: string, limit: number }>}
|
|
286
|
+
*/
|
|
287
|
+
async signup(email) {
|
|
288
|
+
if (!email || typeof email !== 'string') {
|
|
289
|
+
throw new SnapAPIError('Email is required', 0, null);
|
|
290
|
+
}
|
|
291
|
+
const res = await this._request('POST', '/v1/signup', {
|
|
292
|
+
json: { email },
|
|
293
|
+
needsAuth: false,
|
|
294
|
+
});
|
|
295
|
+
return res.json();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
module.exports = SnapAPI;
|
|
300
|
+
module.exports.SnapAPIError = SnapAPIError;
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "snapapi-sdk",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Official Node.js SDK for SnapAPI — screenshot, metadata extraction, and HTML rendering",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"screenshot",
|
|
10
|
+
"api",
|
|
11
|
+
"webpage-capture",
|
|
12
|
+
"snapapi"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18.0.0"
|
|
16
|
+
}
|
|
17
|
+
}
|