docs

HTTP API

The base URL, the auth header, and the endpoints — for a caller with no MCP client and nothing installed.

Bowmark is an HTTP API behind an API key. Nothing needs to be installed and no MCP client is required — if your code can send a request, it can use Bowmark.

This page is the reference: the base URL, the header, the endpoints. For worked examples in Node, Python and curl, go to Scripting § No install at all.

Base URL and auth

https://api.bowmark.ai

Not bowmark.ai — that host serves the marketing site and these docs, and will 404 an API path. Every endpoint below takes the same header:

Authorization: Bearer $BOWMARK_API_KEY

OpenAPI spec

The endpoints on this page as an OpenAPI 3.1 spec, for Postman, Swagger UI or an SDK generator. It needs no key:

https://api.bowmark.ai/openapi.json

Getting a key

There is no anonymous tier and no public browsing: every call — GET /v1/library included — is refused with 401 without a key. Sign up at bowmark.ai/sign-up, create a key at bowmark.ai/dashboard/keys. The first $10 of usage a month is free with no card; add one at bowmark.ai/dashboard/billing to go past it.

The two calls

Everything a caller does is one of these, in this order — read the library, then send a script written against it.

GET /v1/library?query=…Read the vocabulary. Returns the typed function library for a task or a site. Read-only, touches no site, costs nothing, and an unrecognized query returns the whole index rather than an error — so the call cannot dead-end.
POST /v1/runExecute a script written against that library, on the live sites. Body is {"script":"…"}; the script is async JavaScript and returns its result.
curl -sS "https://api.bowmark.ai/v1/library?query=flights" \
  -H "Authorization: Bearer $BOWMARK_API_KEY"
curl -sS -X POST https://api.bowmark.ai/v1/run \
  -H "Authorization: Bearer $BOWMARK_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"script":"const { offers } = await bowmark.pcparts.search(\"RTX 5080\"); return offers.slice(0,3);"}'

run answers an envelope: ok, status, result, logs, error and ms are always present, plus runId, queuedMs, incomplete, emptyHanded, notes, needs, meta and notice when they are relevant to what happened. Full field-by-field reference, including exactly when each optional field shows up: Scripting § Reading the response. What you may write inside script — the bowmark.* globals (including bowmark.read.page for sites with no provider), log, talk, and pay — is Scripting.

`/v1/library` returns markdown by default

It is a document meant to go straight into a model's context, so the default response is text/markdown. Send Accept: application/json to get the machine shape instead.

Recipes

For a common job you can skip writing a script. POST /v1/recipes/:id runs a ready-made one with the inputs you send, and returns the same envelope as run. GET /v1/recipes lists them, and needs no key. Full reference: Recipes.

read.page and browser_agent — the two fallbacks

These are globals your script calls inside run(), not separate endpoints — the two calls above are still the whole HTTP surface. This is the reference for their parameters and return shape; Scripting has the full walkthrough, error codes and worked examples for both.

bowmark.read.page(url, options?) — reads one page directly when GET /v1/library returns no provider for the site.

Param
urlrequired
format"markdown" (default) or "html"
maxCharscap on returned content, default 200,000

Returns { ok, title, content, format, chars, truncated, warnings, url, requestedUrl, status, error, needsLogin, escalationReason, wall, json?, headers? } — ok: false on a wall, a login page, or an empty shell, not only on a network failure.

bowmark.read.pages(urls: string[], options?: ReadOptions): Promise<ReadResult[]> — the same call over several URLs at once, returning one result object per page in the same shape as read.page. One dead URL in the batch does not lose the others; each page's own error carries its own failure.

Field-by-field reference for every field above, and when each one is set: Scripting § No provider for a site? Use read.page.

bowmark.browser_agent.start({ task, outputSchema? }) — hands a transactional task (a booking, a form, a login walkthrough) to a hosted browser agent. One-off only; never for a scheduled or repeating check.

Call
start({ task, outputSchema? })returns a session id; outputSchema (JSON Schema) gets validated JSON back instead of free text
status(id, { waitMs? })idle | running | needs_input
send(id, answer)answers a needs_input pause
stop(id) / list()release a session / see what you're holding — max 3 open at once

Callable only through run() — session() and the bare top-level client refuse it with code: "run_only", which is never an API-key problem. Full reference, costs and the merchant_verification_failed safety check: Scripting § When nothing else works: the browser agent.

Errors and rate limits

POST /v1/run answers with an HTTP status that tells you, without parsing the body, whether the call itself was accepted — status inside the envelope is a separate, narrower thing: whether your script succeeded. A failed script, or one that paused on a login, is still a 200.

HTTP statusWhenWhat to do
200The call was accepted and run — including a script that threw (status: "error") or paused for a login (status: "needs_user")Read status in the body
401No key, a bad key, or (anonymous callers only) past the free run limitCreate or fix the key; www-authenticate names the next step for an anonymous caller
402The account is out of free usage, at its spend cap, or its card was declinedFix billing at bowmark.ai/dashboard/billing — waiting does not clear this
429Too many runs in flight at once (currently 16 concurrent)Read Retry-After and meta.concurrency (limit, inFlight, retryAfterSeconds); nothing was started and nothing was charged
503The queue or an identity lookup failed on our sideRetry; this is never your script's fault

A code on a thrown error (from the typed client, or inside your own script's own reasoning) is a second, finer-grained vocabulary — no_api_key, wire_refused, bad_argument, unknown_function, rung_undeclared, run_only, merchant_verification_failed — one run() call can throw any of these from your own process. Full table, and what to do for each: Scripting § What it throws.

The only hard numeric ceiling on /v1/run itself is 16 concurrent runs per account. Past it you get the 429 above. There is no separate per-minute request limit on calling run or library. A vendor behind a provider — a site a capability or provider calls — may have its own limit; that shows up in incomplete.failures on that call, not as an HTTP status on your own request.

Generating types

GET /v1/manifest?units=…The same library as a machine-readable manifest, for a code generator rather than a model. units is required and comma-separated (?units=flights,providers.kayak) — there is deliberately no whole-catalog form, so call /v1/library first to find the ids.

You usually do not need this: the @bowmark/web and bowmark-web packages are generated from it and ship the types already.

What is not here

The routes above, plus Recipes, are the whole caller-facing surface. Everything else under /v1/ — the login handoff, secrets, files, saved connections, the per-company MCP servers — is machinery the SDK and the sandbox drive on your behalf, documented where it is used rather than as a route list. If you are reaching for one of those directly, email support@bowmark.ai and tell us what you are building; that is a gap in this page, not in the API.

Where to go next

  • Quickstart — one curl, a real answer off three live retailers.
  • Scripting § No install at all — the same two endpoints with worked Node, Python and curl examples, and the Python urllib User-Agent caveat that costs a 403 if you miss it.
  • Installation — if your client does speak MCP, wire it up once and skip the HTTP layer entirely.
  • Browser Agent — the fallback for a page that needs a form filled, a login walked through, or a wizard driven; a plain run() call, not a separate surface.

Reading this with an agent?

This page as plain text: /docs/http-api.md. The whole site as one file: https://bowmark.ai/llms-full.txt. Index of every page: https://bowmark.ai/llms.txt.