Skip to main content
website screenshot api developer tools puppeteer web automation

Website Screenshot API: Automate Captures with Python, Node & PHP

By Webshot Team · · 8 min read

Automating visual monitoring, preview generation, and website auditing often requires taking accurate, high-resolution screenshots of web pages at scale. However, self-hosting headless browsers like Chrome or managing browser automation instances can be complex, resource-heavy, and error-prone. Modern web applications rely heavily on client-side JavaScript, Single Page Application (SPA) frameworks, dynamic rendering, and lazy-loaded assets, making simple HTML scraping insufficient for capturing accurate visual state.

Using a dedicated website screenshot API resolves these infrastructure challenges by outsourcing page execution to a cloud rendering service. In this guide, you will learn how to integrate dynamic website capturing directly into your applications using Python, Node.js, and PHP via Webshot—a free, anonymous, and developer-friendly screenshot engine.

Understanding Webshot's Free Website Screenshot API

Webshot provides a simple HTTP endpoint designed to render dynamic web content and return full-page image files instantly. Built on a stack featuring PHP, TiCore, Node.js, and Puppeteer with the puppeteer-extra-plugin-stealth plugin, the engine executes full headless Chrome instances to load JavaScript-heavy SPAs and trigger lazy-loaded media assets seamlessly.

Unlike traditional screenshot services, Webshot requires zero account creation, zero login, zero API keys, zero credit cards, and adds zero watermarks to your outputs. The exact same rendering engine and rate limits power both the interactive tool at the Webshot homepage and the backend public API endpoint.

Key Architectural Specifications

  • API Endpoint: POST https://webshot.site/api/capture
  • Authentication: None required (no API key or token)
  • Request Format: JSON payload (Content-Type: application/json)
  • Response Output: Direct binary image stream (Content-Type: image/png, image/jpeg, or image/webp)
  • Supported Formats: JPG, PNG, and WebP (defaults to JPG if omitted)
  • Render Mode: Full-page viewport capturing with stealth headless Chrome

Quick Command Line Example

You can quickly test the endpoint using curl from any terminal. Executing the command below sends a request to render https://example.com in PNG format and saves the binary payload directly into screenshot.png:

curl -X POST https://webshot.site/api/capture -H "Content-Type: application/json" -d '{"url":"https://example.com","format":"png"}' --output screenshot.png

How to Use the Website Screenshot API in Python

Python is standard for data pipelines, automated testing, and web scraping workflows. Implementing Webshot into your Python automation scripts requires sending a standard HTTP POST request and writing the resulting binary stream to disk.

The script below uses the popular requests library to query the Webshot API docs endpoint, inspect response status codes, evaluate rate limit headers, and handle binary responses safely.

import requests

def capture_website(target_url, output_path="screenshot.png", img_format="png"):
    api_endpoint = "https://webshot.site/api/capture"
    payload = {
        "url": target_url,
        "format": img_format
    }
    headers = {
        "Content-Type": "application/json"
    }

    try:
        response = requests.post(api_endpoint, json=payload, headers=headers)
        
        # Check HTTP Status Codes
        if response.status_code == 200:
            with open(output_path, "wb") as f:
                f.write(response.content)
            print(f"Success: Screenshot saved to {output_path}")
        elif response.status_code == 400:
            print("Error 400: Invalid URL or SSRF protection triggered (private IPs blocked).")
        elif response.status_code == 429:
            retry_after = response.headers.get("Retry-After", "unknown")
            print(f"Error 429: Rate limit exceeded. Retry after {retry_after} seconds.")
        elif response.status_code == 500:
            print("Error 500: Capture failure on the remote headless browser.")
        else:
            print(f"Unexpected Status Code: {response.status_code}")

        # Inspecting Rate Limit Headers
        print("Limit:", response.headers.get("X-RateLimit-Limit"))
        print("Remaining:", response.headers.get("X-RateLimit-Remaining"))
        print("Reset:", response.headers.get("X-RateLimit-Reset"))

    except requests.exceptions.RequestException as e:
        print(f"Network exception occurred: {e}")

if __name__ == "__main__":
    capture_website("https://example.com", "example.png", "png")

How to Use the Website Screenshot API in Node.js

Node.js backends and serverless functions can trigger page generation without incurring local memory overhead from heavy browser processes. Using native JavaScript promises and the modern fetch API (available in Node.js v18+), you can request full-page screenshots and stream binary buffers to local storage or cloud buckets.

Here is a complete asynchronous Node.js script using native fs promises and standard fetch calls:

const fs = require('fs/promises');

async function captureScreenshot(url, outputPath, format = 'png') {
  const endpoint = 'https://webshot.site/api/capture';
  
  try {
    const response = await fetch(endpoint, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ url, format })
    });

    console.log(`X-RateLimit-Limit: ${response.headers.get('X-RateLimit-Limit')}`);
    console.log(`X-RateLimit-Remaining: ${response.headers.get('X-RateLimit-Remaining')}`);

    if (response.status === 200) {
      const arrayBuffer = await response.arrayBuffer();
      const buffer = Buffer.from(arrayBuffer);
      await fs.writeFile(outputPath, buffer);
      console.log(`Screenshot saved successfully to ${outputPath}`);
    } else if (response.status === 400) {
      console.error('Error 400: Bad request. Check URL format or SSRF block rules.');
    } else if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      console.error(`Error 429: Rate limit exceeded. Retry after ${retryAfter} seconds.`);
    } else if (response.status === 500) {
      console.error('Error 500: Capture failed on server side.');
    }
  } catch (error) {
    console.error('Network request failed:', error);
  }
}

captureScreenshot('https://example.com', 'node_example.png', 'png');

How to Use the Website Screenshot API in PHP

Since Webshot's core integration utilizes PHP and TiCore along with Puppeteer workers, connecting PHP application backends via cURL is seamless. PHP developers can easily execute HTTP POST requests to capture visual artifacts of external URLs.

The snippet below demonstrates setting up cURL parameters, capturing header values, writing image file streams, and validating HTTP status codes in PHP:

<?php

function capture_website($url, $outputFile = 'capture.png', $format = 'png') {
    $apiUrl = 'https://webshot.site/api/capture';
    
    $payload = json_encode([
        'url' => $url,
        'format' => $format
    ]);

    $ch = curl_init($apiUrl);
    
    // Setup response header tracking
    $responseHeaders = [];
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json'
    ]);
    
    // Capture response headers for rate-limit analysis
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$responseHeaders) {
        $len = strlen($header);
        $headerParts = explode(':', $header, 2);
        if (count($headerParts) < 2) {
            return $len;
        }
        $responseHeaders[trim($headerParts[0])] = trim($headerParts[1]);
        return $len;
    });

    $result = curl_exec($ch);
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($statusCode === 200) {
        file_put_contents($outputFile, $result);
        echo "Screenshot successfully saved to {$outputFile}\n";
    } elseif ($statusCode === 400) {
        echo "Error 400: Invalid URL parameter or private IP address blocked via SSRF rules.\n";
    } elseif ($statusCode === 429) {
        $retryAfter = $responseHeaders['Retry-After'] ?? 'unknown';
        echo "Error 429: Rate limit hit. Retry after {$retryAfter} seconds.\n";
    } else {
        echo "Error {$statusCode}: Screenshot capture failed.\n";
    }
}

capture_website('https://example.com', 'php_example.png', 'png');
?>

Best Practices: Rate Limits, SSRF Security, and Error Handling

When connecting programmatic automation scripts to the Webshot API, following best practices around HTTP status codes and capacity boundaries ensures high service uptime and predictable execution.

Rate Limiting with Token Bucket Algorithm

To ensure system stability, the public engine limits client requests using a continuous token bucket algorithm based on IP address tracking. The strict operational limits are:

  • Quota: 5 capture operations per 15-minute window per IP address.
  • Refill: Continuously refills over time rather than resetting abruptly at hourly boundaries.
  • Headers: Every API response contains four informative rate limit headers:
    • X-RateLimit-Limit: Total allowed capacity in current window (5).
    • X-RateLimit-Remaining: Remaining available request tokens.
    • X-RateLimit-Reset: Unix timestamp indicating full bucket reset time.
    • Retry-After: Time in seconds to pause execution before retrying when throttled.
If you need higher concurrency, custom limits, or enterprise capacity, you can contact the owner directly at sales@tuxxin.com or submit the form available on the Webshot API docs page.

HTTP Error Status Codes Reference

Your client application should explicitly process three key non-200 HTTP response codes:

  • HTTP 400 Bad Request: Issued when the requested URL is invalid or malformed. Additionally, Webshot enforces Server-Side Request Forgery (SSRF) protections: targets resolving to internal, loopback, or private IP addresses (such as 127.0.0.1 or 10.0.0.0/8) are blocked.
  • HTTP 429 Too Many Requests: Issued when your IP exhausts its 5-capture token bucket. Always read the Retry-After header to calculate backoff delay.
  • HTTP 500 Internal Server Error: Occurs when the remote headless Chrome rendering process fails or times out while generating the target view.

Privacy and No Retention Guarantee

Webshot is owned and operated by Tuxxin with an open implementation policy. There is zero data retention or disk caching of captured screenshots, and the platform enforces no user tracking beyond privacy-conscious Google Analytics 4 (GA4) on web pages. Check out our latest updates on the blog for continuous engine improvements.

FAQ: Website Screenshot API

Is this website screenshot API really 100% free?

Yes, Webshot is a 100% free and anonymous tool. You can use the engine via the web interface or via the direct HTTP POST endpoint without registering, entering credit card details, or injecting required API keys.

How does Webshot handle JavaScript-heavy SPAs and lazy loading?

Webshot runs headless Chrome instances with the puppeteer-extra-plugin-stealth plugin. This allows the renderer to fully evaluate client-side JavaScript, render modern Single Page Applications, and trigger lazy-loaded media before capturing the full-page layout.

What happens if I hit the rate limit?

When you exceed 5 requests in a 15-minute window, the API returns an HTTP 429 status code along with a Retry-After header specifying the required wait time in seconds. Inspecting the X-RateLimit-Remaining header in your code allows you to manage workflow execution effectively.

What visual output formats are available?

The API supports three output formats: JPG, PNG, and WebP. You specify your preference in the JSON payload using the "format" key (e.g., {"format": "webp"}). If omitted, the API defaults to returning a standard JPG image.

Conclusion

Automating website screenshot workflows no longer requires managing bloated headless browser fleets or paying expensive SaaS subscriptions. Using Webshot's free public API, developers can execute full-page captures across Python, Node.js, and PHP backends in just a few lines of code.

Ready to start capturing web pages automatically? Head over to the Webshot homepage to test URLs interactively, or check the complete technical specifications on the Webshot API docs page!

Written by the Webshot team at Tuxxin

We build and operate the capture engine behind every screenshot on this site.

Try Webshot — it's free

Capture full-page screenshots of any website with no signup, no watermarks, no API key.

Take a Screenshot   View API Docs
Share: 𝕏 Twitter Facebook LinkedIn