Bowmark AIdocs

Read any page (markdown, text or HTML)

Read any web page as markdown, text or HTML — one page or many at once, taking a browser only when the page actually needs one.

Also known as: reader, fetch, scrape, page, webpage, url, markdown, extract, crawl, article

Call it

bowmark.read.page(url: string, options?: ReadOptions): Promise<ReadResult>
bowmark.read.pages(urls: string[], options?: ReadOptions): Promise<ReadResult[]>

Functions

FunctionWhat it does
pageLoads one page and returns its content.
pagesThe same read over many urls, six in flight at a time, results in the order the urls were given.

Types

type ReadFormat = "markdown" | "text" | "cleanHtml" | "html"
type ReadStrategy = "auto" | "fetch" | "browser"

type ReadOptions = {
  format?: ReadFormat        // default "markdown"
  strategy?: ReadStrategy    // default "auto" — plain GET, browser only if needed
  maxChars?: number          // default 200000; over it, content is cut + truncated:true
  timeoutMs?: number         // default 20000, per leg
}

type ReadResult = {
  url: string                // FINAL url after redirects
  requestedUrl: string       // the url you passed
  status: number             // 0 = never completed; see error
  ok: boolean
  title: string | null       // <title>, else first <h1>, else null
  content: string
  format: ReadFormat
  servedBy: "fetch" | "browser"   // which rung actually paid for this
  escalated: boolean              // the GET was tried and rejected
  escalationReason: string | null // WHY a browser was needed. Set even under
                                  // strategy:"fetch", where one was warranted but
                                  // not taken — so under-reading is visible
  chars: number
  truncated: boolean
  error: string | null       // set INSTEAD of throwing; a dead url in pages() never
                             // costs you the other results
  wall: { vendor: string; cleared: boolean } | null   // the bot wall this page is
                             // behind, if a rendered look found one. Reported even
                             // when we could NOT clear it, so a block is a named
                             // fact rather than an empty page
  warnings: string[]
}

Examples

// Read one page as markdown. The default strategy takes the cheap rung and
// only opens a browser if this page turns out to need one.
const page = await bowmark.read.page("https://example.com/pricing");
log(`${page.title} — ${page.chars} chars via ${page.servedBy}`);
return { title: page.title, content: page.content };
// Many pages in one script — the shape this exists for. Six run at a time,
// and a dead url comes back as a VALUE, so it never costs you the rest.
const urls = [
  "https://a.example.com/docs/intro",
  "https://b.example.com/pricing",
  "https://c.example.com/changelog",
];
const pages = await bowmark.read.pages(urls, { maxChars: 20000 });
for (const p of pages.filter(p => !p.ok)) log(`failed ${p.requestedUrl}: ${p.error}`);
const ok = pages.filter(p => p.ok);
// Which ones cost a browser — worth knowing before you scale the list up.
log(`${ok.filter(p => p.escalated).length}/${ok.length} needed a browser`);
return ok.map(p => ({ url: p.url, title: p.title, content: p.content }));

Providers behind it

None — this capability needs no site. It computes the answer, or reads a public API that publishes it.