Skip to content
Docs menu

Docs

Quickstart

Pageteer is a Browser API and a Search API behind one key. Requests are JSON over HTTPS, and every response says how many credits it used and how many you have left. This page takes you from no account to a rendered page and a search in about two minutes.

1. Get an API key

Create an account (every account gets 1,000 free credits a month), open API keys in the dashboard and create a key. The full key (pk_live_ followed by 40 letters and digits) is shown once. Keep it server-side, in an environment variable:

shell
export PAGETEER_API_KEY="pk_live_…"

Building or running CI? Create a test key (pk_test_…) instead: it is free and returns realistic fake responses without fetching real sites.

Check that it works with the free usage endpoint (0 credits):

curl
curl https://api.104-236-84-15.sslip.io/v1/usage -H "Authorization: Bearer $PAGETEER_API_KEY"
200 OK
{
  "period": "2026-10",
  "plan": "free",
  "granted": 1000,
  "used": 0,
  "remaining": 1000,
  "overage_cap": 0,
  "billable": 0,
  "spend_limit_cents": 0,
  "estimated_cost_cents": 0,
  "resets_at": "2026-11-01T00:00:00.000Z"
}

2. Render a page

POST /v1/render fetches a page and returns the formats you ask for. This request uses a plain HTTP fetch (mode: "http", 1 credit) and asks for Markdown and the page's links:

curl
curl -X POST https://api.104-236-84-15.sslip.io/v1/render \
  -H "Authorization: Bearer $PAGETEER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.iana.org/help/example-domains","mode":"http","formats":["markdown","links"]}'
JavaScript (fetch)
const res = await fetch('https://api.104-236-84-15.sslip.io/v1/render', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.PAGETEER_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "url": "https://www.iana.org/help/example-domains",
    "mode": "http",
    "formats": [
      "markdown",
      "links"
    ]
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error.message);
console.log(data, res.headers.get('x-credits-remaining'));
Python (requests)
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['PAGETEER_API_KEY']}"}
res = requests.post(
    "https://api.104-236-84-15.sslip.io/v1/render",
    headers=headers,
    json={
        "url": "https://www.iana.org/help/example-domains",
        "mode": "http",
        "formats": ["markdown", "links"],
    },
    timeout=120,
)
res.raise_for_status()
print(res.json(), res.headers["x-credits-remaining"])
200 OK (links trimmed)
x-request-id: req_cb52637b80354f88977b5c7628e1ea8e
x-credits-used: 1
x-credits-remaining: 966

{
  "url": "https://www.iana.org/help/example-domains",
  "final_url": "https://www.iana.org/help/example-domains",
  "status": 200,
  "title": "Example Domains",
  "markdown": "# Example Domains\n\nA number of domains such as `example.com` and `example.org` are maintained\nfor documentation purposes. …\n\n## Further Reading\n\n- [IANA-managed Reserved Domains](https://www.iana.org/domains/reserved)\n…",
  "links": [
    "https://www.iana.org/",
    "https://www.iana.org/domains",
    "https://www.iana.org/domains/reserved",
    "…"
  ],
  "tier_used": "none",
  "mode_used": "http",
  "attempts": [{ "tier": "none", "status": 200, "ms": 210 }],
  "cached": false,
  "credits": 1
}
  • status is the target site's HTTP status. A 404 page is still a successful render (and is charged), so check it.
  • Pages that need JavaScript: send mode: "browser" (5 credits). Leave mode out (auto) and the engine decides; you pay for the mode it used.
  • Sites that block bots: add unblock: "auto". See Unblocking tiers.

GET /v1/search returns web results as JSON (2 credits; answers served from the shared 6-hour search cache cost 0).

curl
curl "https://api.104-236-84-15.sslip.io/v1/search?q=headless+browser+api&n=3" \
  -H "Authorization: Bearer $PAGETEER_API_KEY"
JavaScript (fetch)
const res = await fetch(`https://api.104-236-84-15.sslip.io/v1/search?${new URLSearchParams({"q":"headless browser api","n":"3"})}`, {
  headers: { Authorization: `Bearer ${process.env.PAGETEER_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error.message);
console.log(data, res.headers.get('x-credits-remaining'));
Python (requests)
import os
import requests

headers = {"Authorization": f"Bearer {os.environ['PAGETEER_API_KEY']}"}
res = requests.get(
    "https://api.104-236-84-15.sslip.io/v1/search",
    headers=headers,
    params={
        "q": "headless browser api",
        "n": 3,
    },
    timeout=120,
)
res.raise_for_status()
print(res.json(), res.headers["x-credits-remaining"])
200 OK (snippets trimmed)
x-cache: MISS
x-credits-used: 2

{
  "query": "headless browser api",
  "results": [
    {
      "title": "BrowserCat | Headless Browser API",
      "url": "https://www.browsercat.com/",
      "snippet": "You connect to a single endpoint. BrowserCat picks the backend best suited to each job …",
      "engine": "metacrawler",
      "engines": ["swisscows", "metacrawler", "yandex"],
      "published_date": null,
      "position": 1
    },
    {
      "title": "Headless Browser API - HeadlessBrowserAPI",
      "url": "https://headlessbrowserapi.com/",
      "snippet": "HeadlessBrowserAPI handles JavaScript rendering using headless browsers …",
      "engine": "metacrawler",
      "engines": ["swisscows", "metacrawler", "yandex"],
      "published_date": null,
      "position": 2
    }
  ],
  "unresponsive_engines": [],
  "cached": false,
  "credits": 2
}

4. Or use the JavaScript client

@pageteer/client wraps every endpoint with types, retries on 429/5xx, job polling and typed errors:

TypeScript
import { Pageteer } from '@pageteer/client';

const pageteer = new Pageteer(); // reads PAGETEER_API_KEY
const page = await pageteer.render('https://www.iana.org/help/example-domains', { mode: 'http', formats: ['markdown'] });
const hits = await pageteer.search('headless browser api', { n: 3 });
console.log(page.title, hits.results[0]?.url, hits.meta.creditsRemaining);

Using an AI coding agent or assistant? The MCP server gives it web search and page fetching as tools.

5. Handle errors

Errors are JSON with a stable error.code. Only successful responses are charged: every error is free.

403 Forbidden
{
  "error": {
    "code": "forbidden_target",
    "message": "Private, loopback and reserved network targets are not allowed"
  }
}

Retry 429 (after Retry-After) and 5xx with backoff; fix the request for other 4xx. The errors reference lists every code and what to do.

Next steps

Base URL: https://api.104-236-84-15.sslip.io (all endpoints are under /v1). Please read the Acceptable Use Policy before you start: no logins, checkouts or form submissions, and no personal-data harvesting.