SnapAPI Documentation

One API key. Screenshots, PDFs, scraping, content extraction, video recording, and OG images. Ship in minutes, scale to millions.

🔑 Authentication

All API requests are authenticated with an API key. Pass it in the X-Api-Key header.

HTTP Header
X-Api-Key: sk_live_xxxxxxxxxxxxxxxxxxxx

You can also pass the key as the access_key query parameter for quick tests in the browser, but the header method is required for production.

Query parameter (testing only)
curl "https://api.snapapi.pics/v1/screenshot?access_key=sk_live_xxx&url=https://example.com" --output out.png
Keep your key secret

Never expose your API key in client-side code, public repos, or URLs you share. Always call the API from your server.

Manage keys from your dashboard. You can generate multiple keys and revoke them individually.

Quick Start

Capture your first screenshot in under 60 seconds.

  1. 1

    Get your API key

    Sign up at snapapi.pics/register. You get 200 free requests — no credit card required.

  2. 2

    Make your first request

    curl -X POST "https://api.snapapi.pics/v1/screenshot" \
      -H "X-Api-Key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"url": "https://example.com"}' \
      --output screenshot.png
    import fs from 'fs';
    
    const res = await fetch('https://api.snapapi.pics/v1/screenshot', {
      method: 'POST',
      headers: {
        'X-Api-Key': 'YOUR_API_KEY',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ url: 'https://example.com' }),
    });
    
    fs.writeFileSync('screenshot.png', Buffer.from(await res.arrayBuffer()));
    console.log('Saved screenshot.png');
    import requests
    
    r = requests.post(
        'https://api.snapapi.pics/v1/screenshot',
        headers={'X-Api-Key': 'YOUR_API_KEY'},
        json={'url': 'https://example.com'},
    )
    with open('screenshot.png', 'wb') as f:
        f.write(r.content)
    print('Saved screenshot.png')
    package main
    
    import (
        "bytes"
        "encoding/json"
        "io"
        "net/http"
        "os"
    )
    
    func main() {
        body, _ := json.Marshal(map[string]string{"url": "https://example.com"})
        req, _ := http.NewRequest("POST", "https://api.snapapi.pics/v1/screenshot", bytes.NewBuffer(body))
        req.Header.Set("X-Api-Key", "YOUR_API_KEY")
        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)
    }
    <?php
    $ch = curl_init('https://api.snapapi.pics/v1/screenshot');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => ['X-Api-Key: YOUR_API_KEY', 'Content-Type: application/json'],
        CURLOPT_POSTFIELDS     => json_encode(['url' => 'https://example.com']),
    ]);
    file_put_contents('screenshot.png', curl_exec($ch));
    curl_close($ch);
    require 'net/http'
    require 'json'
    
    uri = URI('https://api.snapapi.pics/v1/screenshot')
    req = Net::HTTP::Post.new(uri, {
      'X-Api-Key'    => 'YOUR_API_KEY',
      'Content-Type' => 'application/json'
    })
    req.body = { url: 'https://example.com' }.to_json
    
    res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
    File.write('screenshot.png', res.body, mode: 'wb')
  3. 3

    You're done

    The API returns the image binary directly. Save it, stream it, or upload it to S3. Explore the full parameter reference below.

🌐 Base URL & Versioning

Base URL
https://api.snapapi.pics/v1

All endpoints are HTTPS only. Request and response bodies use JSON unless the endpoint returns binary (images, PDFs, videos). The current stable version is v1.

OpenAPI / Swagger

A machine-readable spec is available at /openapi.json and as interactive Swagger UI at /v1/docs.


📷 Screenshot API

Capture pixel-perfect screenshots of any URL. Returns the image binary. Supports PNG, JPEG, WebP, and AVIF. Response time is typically 1–4 seconds.

POST /v1/screenshot Try it

Parameters

ParameterTypeDefaultDescription
urlrequiredstringURL to capture. Must include protocol (https://)
formatstringpngOutput format: png · jpeg · webp · avif
widthinteger1280Viewport width in pixels (100–3840)
heightinteger800Viewport height in pixels (100–2160)
fullPagebooleanfalseCapture the full scrollable page height
qualityinteger80Compression quality 1–100 (jpeg / webp / avif only)
delayinteger0Wait N ms after page load before capturing (0–30000)
selectorstringCSS selector — capture only that element
blockAdsbooleanfalseBlock ad networks before capture
blockCookieBannersbooleanfalseAuto-dismiss cookie consent overlays
darkModebooleanfalseEmulate prefers-color-scheme: dark
deviceScaleFactornumber1Device pixel ratio (1, 1.5, 2, 3) for retina screenshots
userAgentstringOverride the browser's User-Agent string
waitUntilstringloadNavigation event: load · domcontentloaded · networkidle
cachebooleantrueReturn cached result if available (10-min TTL)
markdownstringRender a markdown string to image instead of loading a URL

Example

curl -X POST "https://api.snapapi.pics/v1/screenshot" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://stripe.com",
    "width": 1920,
    "height": 1080,
    "fullPage": true,
    "format": "webp",
    "quality": 90,
    "blockAds": true,
    "blockCookieBanners": true,
    "delay": 500
  }' \
  --output stripe.webp
const res = await fetch('https://api.snapapi.pics/v1/screenshot', {
  method: 'POST',
  headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    url: 'https://stripe.com',
    width: 1920, height: 1080, fullPage: true,
    format: 'webp', quality: 90,
    blockAds: true, blockCookieBanners: true, delay: 500,
  }),
});
const buf = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('stripe.webp', buf);
import requests

r = requests.post(
    'https://api.snapapi.pics/v1/screenshot',
    headers={'X-Api-Key': 'YOUR_API_KEY'},
    json={
        'url': 'https://stripe.com',
        'width': 1920, 'height': 1080, 'fullPage': True,
        'format': 'webp', 'quality': 90,
        'blockAds': True, 'blockCookieBanners': True, 'delay': 500,
    },
)
with open('stripe.webp', 'wb') as f:
    f.write(r.content)
payload, _ := json.Marshal(map[string]interface{}{
    "url": "https://stripe.com", "width": 1920, "height": 1080,
    "fullPage": true, "format": "webp", "quality": 90,
    "blockAds": true, "delay": 500,
})
req, _ := http.NewRequest("POST", "https://api.snapapi.pics/v1/screenshot", bytes.NewBuffer(payload))
req.Header.Set("X-Api-Key", "YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
os.WriteFile("stripe.webp", data, 0644)

Response

On success, returns the raw image binary with the corresponding Content-Type header (image/png, image/jpeg, etc.).

200 OK — image/png binary
<binary image data>
💡
Tip: use WebP for the best size/quality ratio

WebP at quality 85 is typically 40% smaller than PNG with imperceptible quality loss. Use "format":"webp","quality":85 as a default.

📄 PDF Generation

Convert any URL to a PDF document. Supports A4, Letter, Legal, and custom page sizes. Response is the PDF binary. Typical response time is 2–6 seconds.

POST /v1/pdf Try it

Parameters

ParameterTypeDefaultDescription
urlrequiredstringURL to convert to PDF
pageSizestringa4Page format: a4 · letter · legal · tabloid
landscapebooleanfalseUse landscape orientation
printBackgroundbooleantrueInclude background colors and images
marginTopstring0Top margin, e.g. "20mm" or "1in"
marginBottomstring0Bottom margin
marginLeftstring0Left margin
marginRightstring0Right margin
scalenumber1CSS scale factor (0.1–2.0)
delayinteger0Wait N ms after load before generating
waitUntilstringloadNavigation event to wait for

Example

curl -X POST "https://api.snapapi.pics/v1/pdf" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/invoice",
    "pageSize": "a4",
    "printBackground": true,
    "marginTop": "10mm",
    "marginBottom": "10mm"
  }' \
  --output invoice.pdf
const res = await fetch('https://api.snapapi.pics/v1/pdf', {
  method: 'POST',
  headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    url: 'https://example.com/invoice',
    pageSize: 'a4', printBackground: true,
    marginTop: '10mm', marginBottom: '10mm',
  }),
});
require('fs').writeFileSync('invoice.pdf', Buffer.from(await res.arrayBuffer()));
r = requests.post(
    'https://api.snapapi.pics/v1/pdf',
    headers={'X-Api-Key': 'YOUR_API_KEY'},
    json={'url': 'https://example.com/invoice', 'pageSize': 'a4',
          'printBackground': True, 'marginTop': '10mm'},
)
with open('invoice.pdf', 'wb') as f:
    f.write(r.content)

🧠 Extract API

Extract structured content from any webpage. Returns clean markdown, article text, metadata, links, or images. Ideal for LLM pipelines and RAG systems.

POST /v1/extract Try it

Parameters

ParameterTypeDefaultDescription
urlrequiredstringURL to extract content from
typestringmarkdownExtraction type — see table below
selectorstringCSS selector to scope extraction to a specific element
maxLengthintegerTruncate output to N characters
blockAdsbooleanfalseBlock ads before extraction
waitUntilstringnetworkidleNavigation event to wait for
delayinteger0Extra wait in ms after navigation

Extraction Types

typeReturnsBest for
markdownClean markdown with headings, links, code blocksLLM input, RAG
articleTitle, byline, excerpt, and body via ReadabilityNews / blog extraction
structuredJSON: title, author, date, description, word countData pipelines
textPlain text, no formattingSearch indexing
htmlRaw inner HTMLCustom parsing
linksArray of {text, href}Link analysis, crawling
imagesArray of {src, alt, width, height}Image extraction
metadataTitle, OG tags, Twitter cards, faviconSEO analysis

Example — LLM pipeline

curl -X POST "https://api.snapapi.pics/v1/extract" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://openai.com/blog/gpt-4", "type": "markdown"}'
const { data } = await fetch('https://api.snapapi.pics/v1/extract', {
  method: 'POST',
  headers: { 'X-Api-Key': 'YOUR_API_KEY', 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://openai.com/blog/gpt-4', type: 'markdown' }),
}).then(r => r.json());

// Feed directly to an LLM
const completion = await openai.chat.completions.create({
  model: 'gpt-4o',
  messages: [
    { role: 'system', content: 'Summarize the following article.' },
    { role: 'user', content: data },
  ],
});
import requests, openai

r = requests.post(
    'https://api.snapapi.pics/v1/extract',
    headers={'X-Api-Key': 'YOUR_API_KEY'},
    json={'url': 'https://openai.com/blog/gpt-4', 'type': 'markdown'},
).json()

markdown = r['data']

# Feed directly to GPT-4o
response = openai.chat.completions.create(
    model='gpt-4o',
    messages=[
        {'role': 'system', 'content': 'Summarize this article concisely.'},
        {'role': 'user', 'content': markdown},
    ],
)
print(response.choices[0].message.content)

Response

200 OK — application/json
{
  "success": true,
  "data": "# GPT-4 Technical Report\n\nGPT-4 is a large multimodal model...",
  "metadata": {
    "title": "GPT-4 Technical Report",
    "wordCount": 2847,
    "url": "https://openai.com/blog/gpt-4"
  }
}

🤖 Scrape API

Scrape structured data from any URL using a headless browser with stealth mode. Handles JavaScript-rendered pages, SPAs, and bot-detection.

POST /v1/scrape Try it

Parameters

ParameterTypeDefaultDescription
urlrequiredstringURL to scrape
waitUntilstringnetworkidleload · domcontentloaded · networkidle
delayinteger0Extra wait in ms after navigation completes
selectorstringWait for this CSS selector before returning
blockAdsbooleanfalseBlock ad networks
userAgentstringOverride User-Agent

Response

200 OK
{
  "success": true,
  "url": "https://news.ycombinator.com",
  "title": "Hacker News",
  "html": "<html>...full rendered HTML...</html>",
  "text": "Ask HN: ... Show HN: ...",
  "links": [
    { "text": "Guidelines", "href": "https://news.ycombinator.com/newsguidelines.html" }
  ],
  "statusCode": 200
}

🎥 Video Recording NEW

Record a browser session as an MP4 video. Captures page animations, scroll behavior, and dynamic content. Response is the video binary.

POST /v1/video Try it

Parameters

ParameterTypeDefaultDescription
urlrequiredstringURL to record
widthinteger1280Viewport width (480–1920)
heightinteger720Viewport height (360–1080)
durationinteger5000Recording duration in ms (1000–30000)
fpsinteger25Frames per second (10–60)
scrollDurationintegerAuto-scroll the page over N ms
delayinteger500Wait N ms before starting recording
blockAdsbooleanfalseBlock ad networks
Response time

Video requests are processed synchronously. Expect 5–35 seconds depending on duration. For long recordings, consider using the webhook/async flow.

🤖 AI Analyze BETA

Extract content from a URL and run it through an LLM with your prompt. Combines the Extract API with AI analysis in one call.

POST /v1/analyze
Currently in beta

The Analyze endpoint is temporarily limited while we upgrade AI credits. Requests may return a graceful error. Use Extract + your own LLM call as a reliable alternative.

Parameters

ParameterTypeDefaultDescription
urlrequiredstringURL to analyze
promptrequiredstringInstruction for the LLM, e.g. "Summarize this page in 3 bullet points"
modelstringclaude-3-haikuLLM model to use
maxTokensinteger1024Maximum tokens in the response

🖼 OG Image

Generate Open Graph preview images from any URL. Returns a 1200×630 image optimized for social sharing on Twitter, LinkedIn, Slack, and Discord.

POST /v1/screenshot

Use the Screenshot endpoint with "width":1200,"height":630 for standard OG images.

OG image preset
curl -X POST "https://api.snapapi.pics/v1/screenshot" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yoursite.com/blog/my-post",
    "width": 1200,
    "height": 630,
    "format": "jpeg",
    "quality": 92,
    "blockAds": true,
    "blockCookieBanners": true
  }' \
  --output og.jpg

🔗 Webhooks & Async

For long-running jobs (video recording, large PDFs), submit the request asynchronously and receive the result via webhook when processing is complete.

Async Request

Add "async": true and a "webhookUrl" to any request body.

Async screenshot with webhook
{
  "url": "https://example.com",
  "fullPage": true,
  "async": true,
  "webhookUrl": "https://your-server.com/webhooks/snapapi"
}

The API immediately returns a job ID:

202 Accepted
{ "jobId": "job_abc123", "status": "queued" }

Webhook Payload

When the job completes, we POST the following JSON to your webhookUrl:

POST to your webhookUrl
{
  "jobId": "job_abc123",
  "status": "completed",
  "imageUrl": "https://cdn.snapapi.pics/results/job_abc123.png",
  "completedAt": "2026-03-17T12:34:56.789Z"
}
💡
Webhook retries

We retry failed webhooks with exponential backoff: 10 s, 30 s, 2 min, 10 min, 1 hour. Respond with HTTP 2xx to acknowledge.

📊 Usage API

Query your current billing period usage programmatically.

GET /v1/usage
cURL
curl "https://api.snapapi.pics/v1/usage" \
  -H "X-Api-Key: YOUR_API_KEY"
200 OK
{
  "plan": "pro",
  "requestsUsed": 4271,
  "requestsLimit": 50000,
  "resetAt": "2026-04-01T00:00:00.000Z",
  "periodStart": "2026-03-01T00:00:00.000Z"
}

📦 SDKs & Client Libraries

Official SDKs for all major languages. Every SDK supports all endpoints, handles auth, retries, and TypeScript types where applicable.

JavaScript / TypeScript

Full TypeScript types. Works in Node.js 18+ and modern browsers (Bun and Deno compatible).

Installation

npm install @snapapi/sdk

Quick example

import { SnapAPI } from '@snapapi/sdk';

const snap = new SnapAPI({ apiKey: process.env.SNAPAPI_KEY! });

// Screenshot
const img = await snap.screenshot({
  url: 'https://stripe.com',
  format: 'webp',
  fullPage: true,
  width: 1440,
});
await Bun.write('stripe.webp', img);

// Extract markdown for LLM
const { data } = await snap.extract({
  url: 'https://news.ycombinator.com',
  type: 'markdown',
});
console.log(data.substring(0, 500));

// Generate PDF
const pdf = await snap.pdf({
  url: 'https://example.com/invoice',
  pageSize: 'a4',
  marginTop: '10mm',
});
await Bun.write('invoice.pdf', pdf);

Error handling

import { SnapAPI, SnapAPIError } from '@snapapi/sdk';

const snap = new SnapAPI({ apiKey: process.env.SNAPAPI_KEY! });

try {
  const img = await snap.screenshot({ url: 'https://example.com' });
} catch (err) {
  if (err instanceof SnapAPIError) {
    console.error(err.code);    // e.g. 'RATE_LIMITED'
    console.error(err.status);  // e.g. 429
    console.error(err.message);
  }
}

View on GitHub →

Python

Python 3.8+. Supports both sync and async (asyncio) usage. Full type hints via TypedDict.

Installation

pip install snapapi

Quick example

from snapapi import SnapAPI

snap = SnapAPI(api_key="sk_live_xxx")

# Screenshot
img_bytes = snap.screenshot(
    url="https://stripe.com",
    format="webp",
    full_page=True,
    width=1440,
)
with open("stripe.webp", "wb") as f:
    f.write(img_bytes)

# Extract
result = snap.extract(
    url="https://news.ycombinator.com",
    type="markdown",
)
print(result["data"][:500])

# Async variant
import asyncio
from snapapi import AsyncSnapAPI

async def main():
    async with AsyncSnapAPI(api_key="sk_live_xxx") as snap:
        img = await snap.screenshot(url="https://example.com")
    return img

asyncio.run(main())

Error handling

from snapapi import SnapAPI, SnapAPIError

snap = SnapAPI(api_key="sk_live_xxx")

try:
    img = snap.screenshot(url="https://example.com")
except SnapAPIError as e:
    print(e.code)     # e.g. "RATE_LIMITED"
    print(e.status)   # e.g. 429
    print(e.message)

View on GitHub →

Go

Go 1.20+. Idiomatic Go with context support, typed structs, and zero external dependencies.

Installation

go get github.com/Sleywill/snapapi-go

Quick example

package main

import (
    "context"
    "os"
    "github.com/Sleywill/snapapi-go"
)

func main() {
    client := snapapi.NewClient(os.Getenv("SNAPAPI_KEY"))
    ctx := context.Background()

    // Screenshot
    data, err := client.Screenshot(ctx, snapapi.ScreenshotOptions{
        URL:      "https://stripe.com",
        Format:   "webp",
        FullPage: true,
        Width:    1440,
    })
    if err != nil {
        log.Fatal(err)
    }
    os.WriteFile("stripe.webp", data, 0644)

    // Extract
    result, err := client.Extract(ctx, snapapi.ExtractOptions{
        URL:  "https://news.ycombinator.com",
        Type: "markdown",
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result.Data[:500])
}

View on GitHub →

PHP

PHP 8.0+. PSR-4 autoloading via Composer. Uses Guzzle under the hood.

Installation

composer require sleywill/snapapi-php

Quick example

<?php
require 'vendor/autoload.php';

use SnapAPI\Client;
use SnapAPI\SnapAPIException;

$snap = new Client(getenv('SNAPAPI_KEY'));

// Screenshot
$image = $snap->screenshot([
    'url'       => 'https://stripe.com',
    'format'    => 'webp',
    'fullPage'  => true,
    'width'     => 1440,
]);
file_put_contents('stripe.webp', $image);

// Extract
$result = $snap->extract([
    'url'  => 'https://news.ycombinator.com',
    'type' => 'markdown',
]);
echo substr($result['data'], 0, 500);

// Error handling
try {
    $pdf = $snap->pdf(['url' => 'https://example.com']);
} catch (SnapAPIException $e) {
    echo $e->getCode() . ': ' . $e->getMessage();
}

View on GitHub →

Swift

Swift 5.7+. Native async/await. Works on iOS 15+, macOS 12+, and Linux. Distributed as a Swift Package.

Installation — Package.swift

// Package.swift
.package(url: "https://github.com/Sleywill/snapapi-swift", from: "2.0.0")

Quick example

import SnapAPI

let snap = SnapAPIClient(apiKey: ProcessInfo.processInfo.environment["SNAPAPI_KEY"]!)

// Screenshot
let imageData = try await snap.screenshot(
    ScreenshotOptions(
        url: "https://stripe.com",
        format: .webp,
        fullPage: true,
        width: 1440
    )
)
try imageData.write(to: URL(fileURLWithPath: "stripe.webp"))

// Extract
let result = try await snap.extract(
    ExtractOptions(url: "https://news.ycombinator.com", type: .markdown)
)
print(String(result.data.prefix(500)))

// Error handling
do {
    let pdf = try await snap.pdf(PDFOptions(url: "https://example.com"))
} catch let error as SnapAPIError {
    print("Error \(error.statusCode): \(error.code) — \(error.message)")
}

View on GitHub →

Kotlin

Kotlin 1.7+ / JVM 11+. Coroutine-native. Distributed via JitPack.

Installation — build.gradle.kts

repositories { maven("https://jitpack.io") }

dependencies {
    implementation("com.github.Sleywill.snapapi:kotlin:2.0.0")
}

Quick example

import com.snapapi.SnapAPI
import com.snapapi.ScreenshotOptions
import com.snapapi.ExtractOptions
import java.io.File

suspend fun main() {
    val snap = SnapAPI(apiKey = System.getenv("SNAPAPI_KEY"))

    // Screenshot
    val imageBytes = snap.screenshot(
        ScreenshotOptions(
            url = "https://stripe.com",
            format = "webp",
            fullPage = true,
            width = 1440,
        )
    )
    File("stripe.webp").writeBytes(imageBytes)

    // Extract
    val result = snap.extract(
        ExtractOptions(url = "https://news.ycombinator.com", type = "markdown")
    )
    println(result.data.take(500))

    snap.close()
}

View on GitHub →

Ruby

Ruby 3.0+. Available as a gem. Uses Faraday for HTTP.

Installation

gem install snapapi

Quick example

require 'snapapi'

snap = SnapAPI::Client.new(api_key: ENV['SNAPAPI_KEY'])

# Screenshot
image = snap.screenshot(
  url: 'https://stripe.com',
  format: 'webp',
  full_page: true,
  width: 1440
)
File.write('stripe.webp', image, mode: 'wb')

# Extract
result = snap.extract(
  url: 'https://news.ycombinator.com',
  type: 'markdown'
)
puts result[:data][0..500]

# Error handling
begin
  pdf = snap.pdf(url: 'https://example.com')
rescue SnapAPI::Error => e
  puts "#{e.code}: #{e.message}"
end

View on GitHub →

Java

Java 11+. Uses the built-in java.net.http.HttpClient. Distributed via JitPack.

Installation — pom.xml (Maven)

<repositories>
  <repository>
    <id>jitpack.io</id>
    <url>https://jitpack.io</url>
  </repository>
</repositories>

<dependency>
  <groupId>com.github.Sleywill.snapapi</groupId>
  <artifactId>java</artifactId>
  <version>2.0.0</version>
</dependency>

Quick example

import com.snapapi.SnapAPI;
import com.snapapi.ScreenshotOptions;
import com.snapapi.ExtractOptions;
import java.nio.file.Files;
import java.nio.file.Path;

public class Example {
    public static void main(String[] args) throws Exception {
        SnapAPI snap = new SnapAPI(System.getenv("SNAPAPI_KEY"));

        // Screenshot
        byte[] image = snap.screenshot(new ScreenshotOptions()
            .url("https://stripe.com")
            .format("webp")
            .fullPage(true)
            .width(1440));
        Files.write(Path.of("stripe.webp"), image);

        // Extract
        var result = snap.extract(new ExtractOptions()
            .url("https://news.ycombinator.com")
            .type("markdown"));
        System.out.println(result.getData().substring(0, 500));
    }
}

View on GitHub →


🚨 Error Handling

All errors return JSON with a machine-readable error.code field.

Error format
{
  "success": false,
  "error": {
    "code": "INVALID_URL",
    "message": "The provided URL is invalid. Please include the protocol (https://)."
  }
}

Error Code Reference

HTTPCodeDescription
400INVALID_URLURL is invalid or missing protocol
400INVALID_PARAMSOne or more parameters failed validation
401UNAUTHORIZEDInvalid or missing API key
402QUOTA_EXCEEDEDMonthly request quota exhausted
403FORBIDDENFeature not available on your plan
408TIMEOUTPage load exceeded the timeout limit
422CONTENT_VALIDATION_FAILEDContent validation conditions not met
429RATE_LIMITEDToo many requests — slow down and retry
500CAPTURE_FAILEDBrowser failed to capture the page
503SERVICE_UNAVAILABLETemporary overload — retry with backoff

Retry strategy

JavaScript — exponential backoff
async function screenshotWithRetry(options, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch('https://api.snapapi.pics/v1/screenshot', {
      method: 'POST',
      headers: { 'X-Api-Key': process.env.SNAPAPI_KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify(options),
    });

    if (res.ok) return Buffer.from(await res.arrayBuffer());

    const { error } = await res.json();
    const retryable = res.status === 429 || res.status >= 500;

    if (!retryable || attempt === maxRetries) {
      throw new Error(`SnapAPI error ${res.status}: ${error.code}`);
    }

    // Exponential backoff: 1s, 2s, 4s
    await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt)));
  }
}

Rate Limits

Limits are enforced per API key. Exceeding the per-minute limit returns HTTP 429. Monthly quota exhaustion returns 402.

PlanRequests / MinConcurrentRequests / Month
Free101200
Starter6055,000
Pro1201050,000
EnterpriseCustomCustomUnlimited

Rate limit headers are returned on every response:

Response headers
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1742209320
Retry-After: 3   (only on 429)
Need more?

Enterprise plans include dedicated infrastructure, custom limits, and SLA guarantees. Contact us.

Caching

Screenshot and extract results are cached for 10 minutes by URL + parameter hash. Cache hits don't count against your monthly quota.

Disable cache

Pass "cache": false to always get a fresh capture.

{ "url": "https://example.com", "cache": false }

Cache busting

Changing any parameter produces a new cache key. To force a refresh without disabling cache globally, append a timestamp to a custom header field or use "cache": false once, then let the new result warm the cache.

🧠 LLM Pipelines

SnapAPI + Extract is the fastest way to feed web content into GPT-4o, Claude, or any other LLM.

import OpenAI from 'openai';

const openai = new OpenAI();
const SNAP_KEY = process.env.SNAPAPI_KEY;

async function summarizePage(url) {
  // Step 1: extract markdown from any URL
  const { data: markdown } = await fetch('https://api.snapapi.pics/v1/extract', {
    method: 'POST',
    headers: { 'X-Api-Key': SNAP_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ url, type: 'markdown' }),
  }).then(r => r.json());

  // Step 2: send to GPT-4o
  const completion = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [
      { role: 'system', content: 'You are a concise summarizer. Output 3 bullet points.' },
      { role: 'user', content: markdown },
    ],
    max_tokens: 512,
  });

  return completion.choices[0].message.content;
}

const summary = await summarizePage('https://openai.com/blog/gpt-4');
console.log(summary);
import os
import requests
from openai import OpenAI

client = OpenAI()
SNAP_KEY = os.environ['SNAPAPI_KEY']

def summarize_page(url: str) -> str:
    # Step 1: extract markdown
    r = requests.post(
        'https://api.snapapi.pics/v1/extract',
        headers={'X-Api-Key': SNAP_KEY},
        json={'url': url, 'type': 'markdown'},
    )
    markdown = r.json()['data']

    # Step 2: send to GPT-4o-mini
    completion = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[
            {'role': 'system', 'content': 'Summarize in 3 bullet points.'},
            {'role': 'user', 'content': markdown},
        ],
        max_tokens=512,
    )
    return completion.choices[0].message.content

print(summarize_page('https://openai.com/blog/gpt-4'))

📋 OpenAPI Specification

The complete OpenAPI 3.1 spec is available for use with Postman, Insomnia, Stoplight, or any code generator.