Claude-skill-registry browserless

Browserless cloud browser automation service. Run headless Chrome at scale for scraping, screenshots, and PDF generation. Use for cloud browser automation, scalable scraping, or headless Chrome as a service.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/browserless" ~/.claude/skills/majiayu000-claude-skill-registry-browserless && rm -rf "$T"
manifest: skills/data/browserless/SKILL.md
source content

Browserless Skill

Complete guide for Browserless - headless Chrome as a service.

Quick Reference

Key Features

FeatureDescription
REST APIHTTP endpoints
WebSocketPuppeteer/Playwright connection
ScreenshotsPage captures
PDFDocument generation
ScrapingData extraction
FunctionsCustom scripts

Endpoints

/screenshot - Capture screenshots
/pdf - Generate PDFs
/content - Get page HTML
/scrape - Extract data
/function - Run custom code

1. Setup

Self-Hosted (Docker)

docker run -d \
  -p 3000:3000 \
  -e "TOKEN=your-token" \
  -e "CONCURRENT=10" \
  -e "QUEUED=10" \
  --name browserless \
  ghcr.io/browserless/chromium

Docker Compose

services:
  browserless:
    image: ghcr.io/browserless/chromium
    ports:
      - "3000:3000"
    environment:
      - TOKEN=your-secure-token
      - CONCURRENT=10
      - QUEUED=50
      - TIMEOUT=60000
      - MAX_PAYLOAD_SIZE=5mb
      - HEALTH_CHECK=true
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 4G

Cloud Service

Sign up at browserless.io
Get API token
Use: wss://chrome.browserless.io?token=YOUR_TOKEN

2. REST API

Screenshots

# Basic screenshot
curl -X POST "http://localhost:3000/screenshot?token=your-token" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}' \
  -o screenshot.png

# With options
curl -X POST "http://localhost:3000/screenshot?token=your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "options": {
      "fullPage": true,
      "type": "png",
      "quality": 80
    },
    "viewport": {
      "width": 1920,
      "height": 1080
    },
    "waitForSelector": "#content"
  }' \
  -o screenshot.png

PDF Generation

curl -X POST "http://localhost:3000/pdf?token=your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "options": {
      "format": "A4",
      "printBackground": true,
      "margin": {
        "top": "1cm",
        "right": "1cm",
        "bottom": "1cm",
        "left": "1cm"
      }
    }
  }' \
  -o document.pdf

Get Content

# Get HTML
curl -X POST "http://localhost:3000/content?token=your-token" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}' \
  -o page.html

Scrape Data

curl -X POST "http://localhost:3000/scrape?token=your-token" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "elements": [
      {"selector": "h1", "name": "title"},
      {"selector": ".price", "name": "price"},
      {"selector": "img", "name": "images", "attr": "src"}
    ]
  }'

3. JavaScript Integration

Using Puppeteer

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.connect({
    browserWSEndpoint: "ws://localhost:3000?token=your-token",
  });

  const page = await browser.newPage();
  await page.goto("https://example.com");

  const title = await page.title();
  console.log("Title:", title);

  await browser.close();
})();

Using Playwright

const { chromium } = require("playwright");

(async () => {
  const browser = await chromium.connectOverCDP(
    "ws://localhost:3000?token=your-token",
  );

  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto("https://example.com");
  const title = await page.title();
  console.log("Title:", title);

  await browser.close();
})();

REST API with Fetch

async function takeScreenshot(url) {
  const response = await fetch(
    "http://localhost:3000/screenshot?token=your-token",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        url,
        options: { fullPage: true },
      }),
    },
  );

  const buffer = await response.arrayBuffer();
  return Buffer.from(buffer);
}

async function generatePDF(url) {
  const response = await fetch("http://localhost:3000/pdf?token=your-token", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      url,
      options: { format: "A4", printBackground: true },
    }),
  });

  return await response.arrayBuffer();
}

async function scrapeData(url, elements) {
  const response = await fetch(
    "http://localhost:3000/scrape?token=your-token",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ url, elements }),
    },
  );

  return await response.json();
}

4. Python Integration

Using Pyppeteer

import asyncio
from pyppeteer import connect

async def main():
    browser = await connect(
        browserWSEndpoint='ws://localhost:3000?token=your-token'
    )

    page = await browser.newPage()
    await page.goto('https://example.com')

    title = await page.title()
    print(f'Title: {title}')

    await browser.close()

asyncio.run(main())

Using Playwright

from playwright.async_api import async_playwright
import asyncio

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(
            'ws://localhost:3000?token=your-token'
        )

        page = await browser.new_page()
        await page.goto('https://example.com')

        title = await page.title()
        print(f'Title: {title}')

        await browser.close()

asyncio.run(main())

REST API with Requests

import requests

def screenshot(url, options=None):
    response = requests.post(
        'http://localhost:3000/screenshot',
        params={'token': 'your-token'},
        json={
            'url': url,
            'options': options or {'fullPage': True}
        }
    )
    return response.content

def pdf(url, options=None):
    response = requests.post(
        'http://localhost:3000/pdf',
        params={'token': 'your-token'},
        json={
            'url': url,
            'options': options or {
                'format': 'A4',
                'printBackground': True
            }
        }
    )
    return response.content

def scrape(url, elements):
    response = requests.post(
        'http://localhost:3000/scrape',
        params={'token': 'your-token'},
        json={
            'url': url,
            'elements': elements
        }
    )
    return response.json()

# Usage
screenshot_data = screenshot('https://example.com')
with open('screenshot.png', 'wb') as f:
    f.write(screenshot_data)

data = scrape('https://example.com', [
    {'selector': 'h1', 'name': 'title'},
    {'selector': '.price', 'name': 'price'}
])
print(data)

5. Function API

Custom Functions

// POST /function
{
  "code": `
    export default async ({ page }) => {
      await page.goto('https://example.com');

      // Custom logic
      await page.waitForSelector('.loaded');

      const data = await page.evaluate(() => {
        return {
          title: document.title,
          items: Array.from(document.querySelectorAll('.item'))
            .map(el => ({
              name: el.querySelector('.name').textContent,
              price: el.querySelector('.price').textContent
            }))
        };
      });

      return { data, type: 'application/json' };
    }
  `
}

With Context

{
  "code": `
    export default async ({ page, context }) => {
      const { url, searchTerm } = context;

      await page.goto(url);
      await page.type('#search', searchTerm);
      await page.click('#submit');
      await page.waitForNavigation();

      const results = await page.$$eval('.result', els =>
        els.map(el => el.textContent)
      );

      return { data: results, type: 'application/json' };
    }
  `,
  "context": {
    "url": "https://example.com",
    "searchTerm": "puppeteer"
  }
}

6. Advanced Options

Viewport and Device

{
  "url": "https://example.com",
  "viewport": {
    "width": 375,
    "height": 812,
    "deviceScaleFactor": 2,
    "isMobile": true,
    "hasTouch": true
  },
  "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)"
}

Wait Conditions

{
  "url": "https://example.com",
  "waitForSelector": "#content",
  "waitForTimeout": 2000,
  "waitForEvent": "networkidle0",
  "waitForFunction": "window.loaded === true"
}

Authentication

{
  "url": "https://example.com",
  "authenticate": {
    "username": "user",
    "password": "pass"
  },
  "cookies": [
    {
      "name": "session",
      "value": "abc123",
      "domain": "example.com"
    }
  ]
}

JavaScript Injection

{
  "url": "https://example.com",
  "addScriptTag": [
    { "content": "window.injected = true;" },
    { "url": "https://cdn.example.com/script.js" }
  ],
  "addStyleTag": [{ "content": "body { background: red; }" }]
}

7. Proxy Support

Using Proxy

{
  "url": "https://example.com",
  "launch": {
    "args": ["--proxy-server=http://proxy.example.com:8080"]
  }
}

Authenticated Proxy

const browser = await puppeteer.connect({
  browserWSEndpoint: "ws://localhost:3000?token=your-token",
});

const page = await browser.newPage();

await page.authenticate({
  username: "proxy-user",
  password: "proxy-pass",
});

await page.goto("https://example.com");

8. Docker Configuration

Full Configuration

services:
  browserless:
    image: ghcr.io/browserless/chromium
    ports:
      - "3000:3000"
    environment:
      # Authentication
      - TOKEN=your-secure-token

      # Concurrency
      - CONCURRENT=10
      - QUEUED=50

      # Timeouts
      - TIMEOUT=60000
      - CONNECTION_TIMEOUT=30000

      # Resources
      - MAX_PAYLOAD_SIZE=10mb
      - MAX_CPU_PERCENT=99
      - MAX_MEMORY_PERCENT=99

      # Features
      - ENABLE_CORS=true
      - ENABLE_API_GET=true
      - HEALTH_CHECK=true
      - DEBUG=browserless*

      # Function limits
      - FUNCTION_ENABLE_INCOGNITO_MODE=true
      - FUNCTION_EXTERNALS=["lodash","moment"]

      # Proxy
      - DEFAULT_LAUNCH_ARGS=["--proxy-server=http://proxy:8080"]

    volumes:
      - ./downloads:/downloads
      - ./tmp:/tmp

    restart: unless-stopped

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/"]
      interval: 30s
      timeout: 10s
      retries: 3

    deploy:
      resources:
        limits:
          cpus: "2"
          memory: 4G

9. Monitoring

Health Check

curl http://localhost:3000/
# Returns: {"status": "ok", ...}

curl http://localhost:3000/stats
# Returns queue and session stats

Metrics Endpoint

curl http://localhost:3000/metrics
# Prometheus-compatible metrics

Debugging

# Enable debug logging
docker run -e "DEBUG=browserless*" ghcr.io/browserless/chromium

# View live sessions
curl http://localhost:3000/sessions

10. Common Patterns

Batch Processing

const urls = ["https://example1.com", "https://example2.com"];

async function batchScreenshots(urls) {
  const results = await Promise.all(
    urls.map(async (url) => {
      const response = await fetch(
        "http://localhost:3000/screenshot?token=your-token",
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ url, options: { fullPage: true } }),
        },
      );
      return { url, screenshot: await response.arrayBuffer() };
    }),
  );
  return results;
}

Retry Logic

async function withRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise((r) => setTimeout(r, 1000 * (i + 1)));
    }
  }
}

const screenshot = await withRetry(() => takeScreenshot("https://example.com"));

Queue Management

const pLimit = require('p-limit');

const limit = pLimit(5); // Max 5 concurrent

const urls = [...]; // Many URLs

const results = await Promise.all(
  urls.map(url =>
    limit(() => takeScreenshot(url))
  )
);

11. Troubleshooting

Common Issues

Connection timeout:

// Increase connection timeout
const browser = await puppeteer.connect({
  browserWSEndpoint: "ws://localhost:3000?token=your-token",
  timeout: 60000,
});

Memory issues:

# Docker resource limits
deploy:
  resources:
    limits:
      memory: 8G

Queue full:

# Increase queue size
docker run -e "QUEUED=100" ghcr.io/browserless/chromium

Session leak:

// Always close browser
try {
  // ... operations
} finally {
  await browser.close();
}

Best Practices

  1. Use tokens - Secure your instance
  2. Set timeouts - Prevent hanging sessions
  3. Limit concurrency - Based on resources
  4. Close sessions - Prevent memory leaks
  5. Use health checks - Monitor availability
  6. Queue management - Handle bursts
  7. Retry logic - Handle transient failures
  8. Resource limits - Docker constraints
  9. Use functions - Complex workflows
  10. Monitor metrics - Track performance