Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled
- Batch screenshot endpoint: take 1-10 screenshots in a single request - Concurrent processing with Promise.allSettled (partial success support) - Upfront quota check for all URLs before processing - Per-URL SSRF validation via existing takeScreenshot() - Added incrementUsage() to usage middleware for granular tracking - 10 new tests covering all edge cases - Updated OpenAPI docs (JSDoc on route) - Updated Node.js and Python SDK READMEs with batch method docs
267 lines
6.7 KiB
Markdown
267 lines
6.7 KiB
Markdown
# SnapAPI Node.js SDK
|
||
|
||
Official Node.js client for [SnapAPI](https://snapapi.eu) — the EU-hosted screenshot API.
|
||
|
||
## Installation
|
||
|
||
```bash
|
||
npm install snapapi
|
||
```
|
||
|
||
## Quick Start
|
||
|
||
```typescript
|
||
import { SnapAPI } from 'snapapi';
|
||
import fs from 'fs';
|
||
|
||
const snap = new SnapAPI('your-api-key');
|
||
|
||
// Simple screenshot
|
||
const png = await snap.capture('https://example.com');
|
||
fs.writeFileSync('screenshot.png', png);
|
||
```
|
||
|
||
## Usage
|
||
|
||
### Basic Screenshot
|
||
|
||
```typescript
|
||
const screenshot = await snap.capture('https://example.com');
|
||
```
|
||
|
||
### With Options
|
||
|
||
```typescript
|
||
const screenshot = await snap.capture('https://example.com', {
|
||
format: 'jpeg',
|
||
width: 1920,
|
||
height: 1080,
|
||
quality: 90,
|
||
});
|
||
```
|
||
|
||
### Full-Page Capture
|
||
|
||
```typescript
|
||
const screenshot = await snap.capture({
|
||
url: 'https://example.com/blog',
|
||
fullPage: true,
|
||
format: 'png',
|
||
deviceScale: 2, // Retina
|
||
});
|
||
```
|
||
|
||
### Mobile Viewport
|
||
|
||
```typescript
|
||
const screenshot = await snap.capture({
|
||
url: 'https://example.com',
|
||
width: 375,
|
||
height: 812,
|
||
deviceScale: 2,
|
||
});
|
||
```
|
||
|
||
### Wait for Dynamic Content
|
||
|
||
```typescript
|
||
const screenshot = await snap.capture({
|
||
url: 'https://example.com/dashboard',
|
||
waitForSelector: '#chart-loaded',
|
||
waitUntil: 'networkidle2',
|
||
});
|
||
```
|
||
|
||
### Dark Mode Capture
|
||
|
||
```typescript
|
||
// Capture in dark mode (prefers-color-scheme: dark)
|
||
const darkScreenshot = await snap.capture({
|
||
url: 'https://example.com',
|
||
darkMode: true,
|
||
format: 'png',
|
||
});
|
||
```
|
||
|
||
### Custom User Agent
|
||
|
||
```typescript
|
||
// Set a custom User-Agent string for the request
|
||
const screenshot = await snap.capture({
|
||
url: 'https://example.com',
|
||
userAgent: 'Mozilla/5.0 (compatible; SnapAPI/1.0)',
|
||
format: 'png',
|
||
});
|
||
```
|
||
|
||
### Hide Elements Before Capture
|
||
|
||
```typescript
|
||
// Hide cookie banners, popups, ads
|
||
const cleanScreenshot = await snap.capture({
|
||
url: 'https://example.com',
|
||
hideSelectors: [
|
||
'.cookie-banner',
|
||
'.popup-overlay',
|
||
'#advertisement',
|
||
'.tracking-notice'
|
||
],
|
||
});
|
||
|
||
// Hide single element
|
||
const singleHide = await snap.capture('https://example.com', {
|
||
hideSelectors: '.newsletter-popup',
|
||
});
|
||
```
|
||
|
||
### Custom CSS Injection
|
||
|
||
```typescript
|
||
// Inject custom CSS before capture
|
||
const styled = await snap.capture({
|
||
url: 'https://example.com',
|
||
css: 'body { background: #1a1a2e !important; color: #eee !important; font-family: "Comic Sans MS" }',
|
||
});
|
||
|
||
// Combine with other options
|
||
const combined = await snap.capture({
|
||
url: 'https://example.com',
|
||
css: '.hero { padding: 80px 0 } h1 { font-size: 48px }',
|
||
darkMode: true,
|
||
hideSelectors: ['.cookie-banner'],
|
||
});
|
||
```
|
||
|
||
### JavaScript Injection
|
||
|
||
```typescript
|
||
// Execute custom JavaScript before capture
|
||
const interactiveScreenshot = await snap.capture({
|
||
url: 'https://example.com',
|
||
js: `
|
||
// Dismiss modal popup
|
||
document.querySelector('.modal-overlay')?.remove();
|
||
|
||
// Scroll to specific content
|
||
window.scrollTo(0, 500);
|
||
|
||
// Click button to reveal content
|
||
document.querySelector('#show-more-btn')?.click();
|
||
|
||
// Wait for animation to complete
|
||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||
`,
|
||
});
|
||
|
||
// Combine with other options for complex scenarios
|
||
const complexCapture = await snap.capture({
|
||
url: 'https://example.com/app',
|
||
js: 'document.querySelector(".sidebar").style.display = "none";',
|
||
css: 'body { zoom: 0.8 }',
|
||
waitForSelector: '#content-loaded',
|
||
hideSelectors: ['.ad-banner', '.cookie-notice'],
|
||
});
|
||
```
|
||
|
||
### Crop Specific Areas (Clip)
|
||
|
||
```typescript
|
||
// Crop a specific rectangular area from the screenshot
|
||
const croppedScreenshot = await snap.capture({
|
||
url: 'https://example.com',
|
||
clip: {
|
||
x: 100, // X coordinate (pixels from left)
|
||
y: 50, // Y coordinate (pixels from top)
|
||
width: 800, // Width of the crop area
|
||
height: 600, // Height of the crop area
|
||
},
|
||
});
|
||
|
||
// Useful for capturing specific UI elements or sections
|
||
const headerScreenshot = await snap.capture({
|
||
url: 'https://example.com',
|
||
clip: { x: 0, y: 0, width: 1280, height: 120 }, // Top banner only
|
||
format: 'png',
|
||
});
|
||
```
|
||
|
||
## API Reference
|
||
|
||
### `new SnapAPI(apiKey, config?)`
|
||
|
||
| Parameter | Type | Description |
|
||
|-----------|------|-------------|
|
||
| `apiKey` | `string` | Your SnapAPI API key (required) |
|
||
| `config.baseUrl` | `string` | API base URL (default: `https://snapapi.eu`) |
|
||
| `config.timeout` | `number` | Request timeout in ms (default: `30000`) |
|
||
|
||
### `snap.capture(url, options?)` / `snap.capture(options)`
|
||
|
||
Returns a `Promise<Buffer>` containing the screenshot image.
|
||
|
||
| Option | Type | Default | Description |
|
||
|--------|------|---------|-------------|
|
||
| `url` | `string` | — | URL to capture (required) |
|
||
| `format` | `'png' \| 'jpeg' \| 'webp'` | `'png'` | Output format |
|
||
| `width` | `number` | `1280` | Viewport width (320–3840) |
|
||
| `height` | `number` | `800` | Viewport height (200–2160) |
|
||
| `fullPage` | `boolean` | `false` | Capture full scrollable page |
|
||
| `quality` | `number` | `80` | JPEG/WebP quality (1–100) |
|
||
| `waitForSelector` | `string` | — | CSS selector to wait for |
|
||
| `deviceScale` | `number` | `1` | Device pixel ratio (1–3) |
|
||
| `delay` | `number` | `0` | Extra delay in ms (0–5000) |
|
||
| `waitUntil` | `string` | `'domcontentloaded'` | Load event to wait for |
|
||
| `darkMode` | `boolean` | `false` | Emulate prefers-color-scheme: dark |
|
||
| `hideSelectors` | `string \| string[]` | — | CSS selectors to hide before capture |
|
||
| `css` | `string` | — | Custom CSS to inject before capture (max 5000 chars) |
|
||
| `clip` | `object` | — | Crop rectangle: `{x, y, width, height}` (mutually exclusive with fullPage/selector) |
|
||
|
||
### `snap.batch(urls, options?)`
|
||
|
||
Take multiple screenshots in a single request. Each URL counts as one screenshot toward usage limits.
|
||
|
||
```typescript
|
||
const results = await snap.batch(
|
||
['https://example.com', 'https://example.org'],
|
||
{ format: 'jpeg', width: 1920, height: 1080 }
|
||
);
|
||
|
||
for (const result of results) {
|
||
if (result.status === 'success') {
|
||
fs.writeFileSync(`${result.url}.jpg`, Buffer.from(result.image, 'base64'));
|
||
} else {
|
||
console.error(`Failed: ${result.url} — ${result.error}`);
|
||
}
|
||
}
|
||
```
|
||
|
||
- **Max 10 URLs per batch**
|
||
- All options (format, width, height, etc.) are shared across all URLs
|
||
- Returns partial results — some may succeed while others fail
|
||
- Response is always JSON with `{ results: [...] }`
|
||
|
||
### `snap.health()`
|
||
|
||
Returns API health status.
|
||
|
||
### Error Handling
|
||
|
||
```typescript
|
||
import { SnapAPI, SnapAPIError } from 'snapapi';
|
||
|
||
try {
|
||
const screenshot = await snap.capture('https://example.com');
|
||
} catch (err) {
|
||
if (err instanceof SnapAPIError) {
|
||
console.error(`API error ${err.status}: ${err.detail}`);
|
||
}
|
||
}
|
||
```
|
||
|
||
## EU-Hosted & GDPR Compliant
|
||
|
||
SnapAPI runs entirely on EU infrastructure (Germany). Your data never leaves the EU. [Learn more](https://snapapi.eu).
|
||
|
||
## License
|
||
|
||
MIT — [Cloonar Technologies GmbH](https://snapapi.eu)
|