PageShot API
A powerful, developer-friendly API for capturing screenshots of any webpage. Built on Chromium for pixel-perfect accuracy, completely free with no API key required.
https://pageshot.info/v1
Get Started
PageShot is completely free. No API key required. Just send a request with a URL and get your screenshot.
Quickstart
Take your first screenshot in under 30 seconds:
# Just pass a URL — no API key needed
curl "https://pageshot.info/v1/screenshot?url=https://example.com" \
--output screenshot.png
# That's it! Open screenshot.png to see the result.
Authentication
GET /v1/screenshot
GET /v1/screenshot?url=https://example.com
Returns a screenshot image directly. Ideal for embedding in <img> tags or downloading.
Example Request
GET /v1/screenshot?url=https://example.com&width=1920&height=1080&format=png&dark_mode=true
Response Headers
| Header | Description |
|---|---|
X-Screenshot-Time | Capture duration (e.g., 2341ms) |
Content-Type | image/png, image/jpeg, or image/webp |
POST /v1/screenshot
POST /v1/screenshot
More flexible screenshot capture. Supports JSON body and optional JSON response with base64 image.
Request Body (JSON)
{
"url": "https://example.com",
"width": 1920,
"height": 1080,
"full_page": true,
"format": "png",
"dark_mode": true,
"block_ads": true,
"css": "header { display: none !important; }",
"selector": ".main-content",
"delay": 2000,
"response": "json"
}
JSON Response (when response=json)
{
"success": true,
"data": {
"url": "https://example.com",
"image": "data:image/png;base64,iVBORw0KGgo...",
"format": "png",
"width": 1920,
"height": 1080,
"size": 245632,
"captureTime": "2341ms"
}
}
GET /v1/status
GET /v1/status
Check API operational status. No authentication required.
{
"status": "operational",
"service": "PageShot API",
"version": "2.0.0",
"uptime": 86400.123,
"timestamp": "2026-02-12T12:00:00.000Z"
}
Parameters Reference
| Parameter | Type | Default | Description |
|---|---|---|---|
url | string | required | The webpage URL to capture |
width | integer | 1280 | Viewport width (320-3840) |
height | integer | 720 | Viewport height (200-2160) |
full_page | boolean | false | Capture entire scrollable page |
format | string | png | Output: png, jpeg, webp |
quality | integer | 80 | JPEG/WebP quality (1-100) |
dark_mode | boolean | false | Force dark color scheme |
block_ads | boolean | false | Block ads and trackers |
delay | integer | 0 | Wait before capture (ms, max 10000) |
device_scale | float | 1 | Device scale factor (0.5-3) |
selector | string | null | CSS selector for element capture |
css | string | null | Custom CSS to inject |
user_agent | string | null | Custom User-Agent string |
timeout | integer | 30000 | Page load timeout (ms, 5000-60000) |
response | string | binary | Set to json for base64 response |
Code Examples
Node.js / JavaScript
// Using fetch (Node 18+) — no API key needed
const response = await fetch('https://pageshot.info/v1/screenshot', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://example.com',
width: 1280,
height: 720,
format: 'png'
})
});
const buffer = Buffer.from(await response.arrayBuffer());
require('fs').writeFileSync('screenshot.png', buffer);
console.log(`Captured in ${response.headers.get('X-Screenshot-Time')}`);
Python
import requests
# No API key needed — just send the request
response = requests.post(
'https://pageshot.info/v1/screenshot',
json={
'url': 'https://example.com',
'width': 1280,
'height': 720,
'full_page': True,
'format': 'png'
}
)
with open('screenshot.png', 'wb') as f:
f.write(response.content)
print(f"Captured in {response.headers['X-Screenshot-Time']}")
C# / .NET
// No API key needed
using var client = new HttpClient();
var payload = new {
url = "https://example.com",
width = 1920,
height = 1080,
full_page = true,
dark_mode = true
};
var response = await client.PostAsJsonAsync(
"https://pageshot.info/v1/screenshot", payload);
var bytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync("screenshot.png", bytes);
PHP
// No API key needed
$ch = curl_init('https://pageshot.info/v1/screenshot');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json'
],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://example.com',
'width' => 1920,
'height' => 1080,
'format' => 'png'
])
]);
$image = curl_exec($ch);
curl_close($ch);
file_put_contents('screenshot.png', $image);
Go
// No API key needed
payload := map[string]any{
"url": "https://example.com",
"width": 1920,
"height": 1080,
"format": "png",
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://pageshot.info/v1/screenshot",
bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
os.WriteFile("screenshot.png", data, 0644)
Error Handling
All errors return JSON with an error field and descriptive message.
| Status | Code | Description |
|---|---|---|
| 400 | INVALID_JSON | Malformed JSON request body |
| 400 | SELECTOR_NOT_FOUND | CSS selector not found on page |
| 400 | PAGE_TOO_TALL | Full-page exceeds 15,000px height |
| 413 | PAYLOAD_TOO_LARGE | Request body exceeds 1MB |
| 429 | Rate limit exceeded | 30 requests per minute per IP |
| 502 | NETWORK_ERROR | Target URL unreachable |
| 503 | CONCURRENT_LIMIT | Too many simultaneous captures (max 5) |
| 504 | TIMEOUT | Page load exceeded timeout |
| 500 | CAPTURE_FAILED | Generic screenshot failure |
| 500 | CONVERSION_FAILED | Image format conversion error |
Example Error Response
{
"error": "NETWORK_ERROR",
"message": "Could not reach the target URL. Verify the URL is accessible.",
"url": "https://invalid-domain.example",
"docs": "/docs#errors"
}
Rate Limits
| Limit | Value | Scope |
|---|---|---|
| API requests | 30 / minute | Per IP address |
| Concurrent renders | 5 | Per IP address |
| Page load timeout | 30 seconds | Per request |
| Max viewport | 3840 x 2160 | Per request |
Rate limit info is returned in response headers:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
X-RateLimit-Reset: 1707696000
Changelog
v2.0.0 (February 2026) — Completely Free
- Removed API key requirement — all endpoints now open
- Removed usage limits and monthly quotas
- Removed pricing tiers — everything is free, all features included
- Improved browser compatibility (better Chromium flags)
- Changed default example URL for better reliability
- Rate limiting now per IP address (30 req/min)
- Removed /v1/usage endpoint (no longer needed)
v1.0.1 (February 2026) - Hardened Build
- WebP format support via Sharp
- Structured error codes (TIMEOUT, NETWORK_ERROR, etc.)
- SSRF protection hardened (IPv6-mapped addresses)
- Graceful shutdown on SIGTERM/SIGINT
- Browser warmup on startup + /ready endpoint
- /health checks browser connectivity
- GET /v1/usage endpoint
- gzip compression, smart page wait
- API keys Map bounded at 10,000 entries
- 50+ security and reliability improvements
v1.0.0 (February 2026) - Initial Release
- GET and POST screenshot endpoints
- Full-page capture, dark mode, ad blocking
- CSS injection and element selector capture
- Device emulation and custom viewports
- API key authentication with auto-registration
- Rate limiting and usage tracking
Support
Need help? Here's how to reach us:
- Documentation: You're reading it!
- GitHub Issues: github.com/pageshot/api/issues
- Email: support@pageshot.info
- Status Page: /v1/status