Skip to main content
ReaderClient is the high-level API you’ll use for 99% of self-hosted Reader workloads. It owns the HeroCore instance and the browser pool, exposes scrape() and crawl(), and handles lazy initialization.

Constructor

new ReaderClient(options?: ReaderClientOptions)
The constructor does not touch the browser. It only stores configuration. Initialization happens lazily on the first scrape() or crawl() call.

ReaderClientOptions

interface ReaderClientOptions {
  verbose?: boolean;
  showChrome?: boolean;
  browserPool?: BrowserPoolConfig;
  proxies?: ProxyConfig[];
  proxyPools?: ProxyPoolConfig;
  proxyRotation?: "round-robin" | "random";
  skipTLSVerification?: boolean;
}
OptionTypeDefaultDescription
verbosebooleanfalseEnable Pino logging
showChromebooleanfalseShow the browser window (debugging)
browserPoolBrowserPoolConfig{ size: 2 }Browser pool configuration
proxiesProxyConfig[]-Flat proxy list for round-robin rotation
proxyPoolsProxyPoolConfig-Multi-tier proxy pools (datacenter, residential)
proxyRotation"round-robin" | "random""round-robin"Rotation strategy within a pool
skipTLSVerificationbooleantrueSkip TLS certificate verification
See BrowserPoolConfig and ProxyConfig for the nested types.

Methods

async start(): Promise<void>
Pre-warm the client. Initializes HeroCore and the browser pool without running a scrape. Optional - scrape() and crawl() will initialize automatically if you haven’t called start().
async scrape(options: ScrapeOptions): Promise<ScrapeResult>
Scrape one or more URLs. See ScrapeOptions and ScrapeResult.
async crawl(options: CrawlOptions): Promise<CrawlResult>
Discover and optionally scrape pages on a site. See CrawlOptions and CrawlResult.
isReady(): boolean
Returns true if the client has been initialized (via start() or a prior scrape()/crawl() call).
async close(): Promise<void>
Shut down browsers and release resources. Auto-runs on process exit - call explicitly for fast cleanup.
hasProxyTier(tier: "datacenter" | "residential"): boolean
getProxyForTier(tier: "datacenter" | "residential"): ProxyConfig | undefined
Helpers for checking proxy pool availability. Useful when you want to gate behavior on whether a residential pool is configured.

Lifecycle

ReaderClient is lazy by design:
  1. new ReaderClient() - constructor does nothing expensive
  2. First call to scrape() or crawl() - triggers HeroCore startup and browser pool initialization (1-2 seconds)
  3. Subsequent calls - reuse the warm pool
  4. Auto cleanup on SIGTERM/SIGINT/process exit
  5. Explicit close() - tears down browsers immediately
Reuse a single client instance for the lifetime of your process. Don’t create-and-close per request.

Server pattern

import { ReaderClient } from "@vakra-dev/reader";
import express from "express";

const reader = new ReaderClient({
  browserPool: { size: 5 },
});

const app = express();
app.use(express.json());

app.post("/scrape", async (req, res) => {
  try {
    const result = await reader.scrape({
      urls: [req.body.url],
      formats: ["markdown"],
    });
    res.json(result);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

process.on("SIGTERM", async () => {
  await reader.close();
  process.exit(0);
});

app.listen(3001);

Where to go next

scrape()

Signature, options, and return type.

crawl()

Signature, options, and return type.