Record your web page
as video

Pagecorder is an API that loads your URL in a browser, captures the screen and audio at 60 FPS, and delivers an MP4. Generate video from HTML, CSS, and JavaScript programmatically.


How it works

Add two lines of JavaScript to your page. Pagecorder does the rest.

1

Add the Pagecorder calls to your page

Call window.pagecorder('start') when your page is ready to begin recording, and window.pagecorder('stop') when it should end. These calls are silently ignored in normal browsers, so your page works everywhere.

<script>
  // Start recording when the page loads
  window.pagecorder('start');

  // ... your animation or dynamic content runs ...

  // Stop after 10 seconds
  setTimeout(() => window.pagecorder('stop'), 10000);
</script>
2

Submit a recording job via the API

POST the URL of your page (or a ZIP archive containing it) to the Pagecorder API. You get back a job ID immediately.

POST /rapid/render
{
  "url": "https://files.example.com/my-animation.zip",
  "width": 1920,
  "height": 1080
}
3

Poll for results

Poll the job status endpoint until the recording is done. The result includes the URL of your MP4 file, recording duration, and diagnostic information.

GET /rapid/render/42

{
  "state": { "stage": "done" },
  "result": {
    "url": "https://cdn.example.com/recordings/42.mp4",
    "durationSeconds": 29,
    "droppedFrames": [],
    "httpErrors": []
  }
}

Use ZIP archives for best results

The recommended way to use Pagecorder is to bundle your entire website into a ZIP archive. The archive is downloaded and served locally during recording, which means faster loading, no network errors, and fully reproducible results.

Faster loading

No network requests during recording. Assets load from local disk instantly.

🔒

No network errors

All assets (images, fonts, scripts) are available locally. No failed HTTP requests.

🔄

Reproducible

Results are not affected by CDN latency, rate limiting, or transient network issues.

ZIP structure
my-animation.zip
  index.html       // Entry point (required)
  styles.css
  script.js
  assets/
    logo.png
    font.woff2

Host the ZIP anywhere with a public URL and pass it as the url parameter. You can also pass a regular text/html URL for quick tests.


API Reference

Two endpoints. Submit a job, poll for the result.

POST /rapid/render

Submit a new recording job. Returns the job object immediately.

Field Type Default Description
url string -- URL to record (text/html or .zip). Required.
width number 1920 Viewport width (1-3840 px).
height number 1080 Viewport height (1-2160 px).
upload object Server default Custom FTP upload target.
timeouts object See docs Per-stage timeout overrides (ms).
GET /rapid/render/:id

Get the status of a recording job. Poll until state.stage is "done" or failedReason is present.

Job lifecycle

scheduled delayed started downloading loading rendering uploading done

Timeouts

Stage Default Maximum Description
downloading 5 min 15 min Download a ZIP archive.
loading 1 min 15 min Page load + pagecorder('start').
rendering 1 hour 6 hours Recording between start and stop.
uploading 15 min 1 hour Upload MP4 to FTP.

Billing

Billed per second of actual recording duration. Billing is triggered when you fetch a completed job for the first time. Failed jobs are not billed.

Job limits

Plan Max unbilled jobs
BASIC 1
PRO 3
ULTRA 5
MEGA 10

An "unbilled" job is any job in progress or completed but not yet fetched. Fetch your results to free up slots.


Debugging

Every completed job includes diagnostic data to help you identify and fix issues.

Dropped frames

The droppedFrames array reports frames that could not be rendered at 60 FPS, causing visual stuttering. This means your page is too computationally expensive to render in real time.

{
  "droppedFrames": [
    { "dropped": 3, "time": "00:12.450", "reason": "rendering too slow" }
  ]
}

Common causes: heavy CSS animations (box-shadow, blur filters), Canvas/WebGL operations exceeding the frame budget, large DOM reflows, or expensive synchronous JavaScript.

Fixes: use transform and opacity for animations, reduce viewport size, simplify the DOM, or use a ZIP archive to eliminate network slowdowns.

HTTP errors

The httpErrors array reports HTTP requests your page made that returned 4xx or 5xx status codes. These indicate missing or inaccessible assets.

{
  "httpErrors": [
    { "status": 404, "method": "GET", "url": "https://example.com/font.woff2" }
  ]
}

Fix: ensure all assets are publicly accessible, or use a ZIP archive to bundle everything locally and eliminate HTTP errors entirely.

Browser logs

The logs array captures console.log, console.warn, and console.error messages from your page during recording.


Examples

const RAPIDAPI_KEY = "your-rapidapi-key";
const RAPIDAPI_HOST = "pagecorder.p.rapidapi.com";

// 1. Submit a recording job
const submitRes = await fetch(`https://${RAPIDAPI_HOST}/rapid/render`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-RapidAPI-Key": RAPIDAPI_KEY,
    "X-RapidAPI-Host": RAPIDAPI_HOST,
  },
  body: JSON.stringify({
    url: "https://files.example.com/animation.zip",
    width: 1280,
    height: 720,
  }),
});
const { id } = await submitRes.json();

// 2. Poll until done
let result;
while (true) {
  const res = await fetch(`https://${RAPIDAPI_HOST}/rapid/render/${id}`, {
    headers: {
      "X-RapidAPI-Key": RAPIDAPI_KEY,
      "X-RapidAPI-Host": RAPIDAPI_HOST,
    },
  });
  const job = await res.json();

  if (job.state.stage === "done") {
    result = job.result;
    break;
  }
  if (job.failedReason) throw new Error(job.failedReason);
  await new Promise(r => setTimeout(r, 30000));
}

console.log(`Recording: ${result.url}`);
console.log(`Duration: ${result.durationSeconds}s`);
import requests
import time

RAPIDAPI_KEY = "your-rapidapi-key"
RAPIDAPI_HOST = "pagecorder.p.rapidapi.com"

HEADERS = {
    "X-RapidAPI-Key": RAPIDAPI_KEY,
    "X-RapidAPI-Host": RAPIDAPI_HOST,
}

# 1. Submit a recording job
job = requests.post(
    f"https://{RAPIDAPI_HOST}/rapid/render",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={
        "url": "https://files.example.com/animation.zip",
        "width": 1280,
        "height": 720,
    },
).json()

job_id = job["id"]

# 2. Poll until done
while True:
    job = requests.get(
        f"https://{RAPIDAPI_HOST}/rapid/render/{job_id}",
        headers=HEADERS,
    ).json()

    if job["state"]["stage"] == "done":
        result = job["result"]
        print(f"Recording: {result['url']}")
        print(f"Duration: {result['durationSeconds']}s")
        break

    if job.get("failedReason"):
        raise Exception(f"Job failed: {job['failedReason']}")

    time.sleep(30)
Ready to start? Get your API key on RapidAPI and start recording in minutes.
Get API Key on RapidAPI