Headless browser & search API for AI agents | skill.md | health | source
kamai gives your agent a real Chromium browser, a Brave-backed search API, per-domain memory, and PDF brochure generation — over plain HTTPS JSON. No SDK required.
Base URL: https://kamai.minai.work
Identify your app on authenticated routes with either header:
x-api-key: <key> or x-wallet-address: 0x… (Celo).
Sister apps receive an API key from the kamai operators — it bypasses payment entirely.
Without a key, requests are paid from a USDC credit balance (first request each day is free).
curl -X POST https://kamai.minai.work/api/v1/browse \
-H "Content-Type: application/json" \
-H "x-api-key: <your-key>" \
-d '{"url": "https://example.com"}'
Legacy routes need no auth at all — sister backends can call
POST /browse, POST /search/web, POST /search/image
and /browse/memories directly. The /api/v1/… equivalents exist
for credit-paying callers; sister keys make them behave identically.
POST /api/v1/browse · legacy alias /browse
Navigates a real headless Chromium to a URL, optionally performs actions (fill forms, click through flows), then extracts clean text, links, and form fields. Stealth measures are applied to avoid headless detection. JavaScript dialogs (alert/confirm/prompt) are auto-accepted and logged.
{
"url": "https://example.gov.ph/search",
"actions": [
{ "action": "type", "selector": "#q", "text": "business permits" },
{ "action": "click_and_wait", "selector": "button#go" },
{ "action": "wait", "selector": ".results" }
],
"selector": ".results", // optional: narrow extraction to one element
"timeout": 15000 // optional, ms (default 15000, max 30000)
}
// Response
{
"ok": true,
"url": "https://example.gov.ph/results",
"title": "Search Results",
"text": "…page content as plain text…",
"links": [{ "text": "Permit application", "href": "https://…" }],
"forms": [{ "tag": "input", "type": "text", "name": "q", "selector": "#q" }],
"memories": ["Use /Indexes/index for keyword search instead of homepage"],
"actions_performed": ["typed \"business permits\" into #q", "clicked button#go → navigated"],
"sessionId": "…", // your persistent session (see Sessions)
"length": 4500
}
Safety: file:, data:, localhost and private-IP URLs are blocked. Text is capped at 30 000 chars.
Up to 20 actions per request, executed in order before extraction. All click actions auto-scroll the element into view and dismiss cookie/consent overlays first. CSS selectors and Playwright text=… selectors are supported everywhere.
| Action | Params | What it does |
|---|---|---|
type | selector, text | Clear field and type text |
click | selector | Click element (500 ms pause) |
click_and_wait | selector | Click, wait for navigation |
submit | selector? | Programmatic form submit (ASP.NET postbacks) |
select | selector, value | Choose a dropdown option |
wait | selector, timeout? | Wait for element to appear |
wait_ms | ms | Pause (max 5000) |
scroll_to | selector | Scroll element into view (centered) |
js_click | selector | Force-click via JS — bypasses overlays/pointer interception |
set_date | selector, value | Set any date picker, value yyyy-mm-dd |
evaluate | text | Run arbitrary JS on the page |
Sessions are automatic. kamai keeps a persistent browser context per caller identity (API key → wallet → IP), so cookies, auth state, and localStorage carry across requests: log in once, stay logged in. Idle sessions expire after 30 minutes.
For explicit control, create a session via POST /api/v1/session
and pass its sessionId in browse requests;
GET/DELETE /api/v1/session/:id inspect and destroy it.
Brave Search proxy — one paid subscription on our side, so your app never manages Brave keys or rate limits.
POST /api/v1/search/web · legacy alias /search/web
{ "q": "Tim Cook age", "count": 5, "country": "US" }
// → { "ok": true, "source": "llm_context" | "web",
// "results": [{ "title", "url", "description", "content"?, "age"? }] }
count 1–20 (default 5) · country 2-letter code or ALL ·
freshness pd|pw|pm|py · maxTokens context budget (default 4096).
Uses Brave's LLM Context API and falls back to standard web search automatically.
POST /api/v1/search/image · legacy alias /search/image
{ "q": "Eiffel Tower at night", "count": 10, "safesearch": "strict" }
// → { "ok": true, "results": [{ "title", "url", "imageUrl", "thumbnailUrl",
// "width", "height", "source" }] }
count 1–50 (default 5) · safesearch strict (default) or off.
Every browse response includes a memories array — learnings saved by agents from
previous visits to that domain. When your agent discovers a better navigation path, save it;
all future callers to that domain benefit.
| Route | Purpose | |
|---|---|---|
| GET | /browse/memories?domain=example.com | Read learnings (omit domain to list all) |
| POST | /browse/memories | Save { "domain", "learning", "strategy"? } |
| DELETE | /browse/memories/:id | Remove a learning |
Also mounted at /api/v1/browse/memories. Free on both paths.
A memory with a strategy field (yt-dlp, github-api) overrides how kamai fetches that domain.
Generate multi-page corporate PDFs from structured JSON. Templates: corporate-overview, product-showcase, event-invitation.
| Route | Purpose | |
|---|---|---|
| GET | /api/v1/brochure/templates | free — list templates + required fields |
| POST | /api/v1/brochure/generate | paid — render a new brochure |
| PATCH | /api/v1/brochure/:id | paid — merge changes & re-render |
| GET | /api/v1/brochure/:id/download | free — the PDF itself, no auth (shareable link) |
Full content schema (sections, products, charts, contact info, images as URLs or base64) is in skill.md.
| Request | Cost |
|---|---|
| Browse (no actions) | $0.009 |
| Browse with actions | $0.013 |
| Search (web or image) | $0.003 |
| Brochure generate / update | $0.050 |
| Brochure templates & downloads, memories, health | free |
Sister keys bypass payment entirely. Credit callers: first request each day is free, then
balance is deducted per request. When balance runs out, the API returns
402 with machine-readable deposit instructions (USDC on Celo, min $0.10).
Deposit via POST /api/v1/deposit with your tx hash;
check balance at GET /api/v1/deposit/balance;
manage your account and API key at GET /api/v1/account.
| Route | Auth | |
|---|---|---|
| POST | /api/v1/browse | key / wallet (legacy /browse: none) |
| POST | /api/v1/search/web · /api/v1/search/image | key / wallet (legacy /search/*: none) |
| GET·POST·DEL | /browse/memories · /api/v1/browse/memories | none |
| POST | /api/v1/brochure/generate | key / wallet |
| PATCH | /api/v1/brochure/:id | key / wallet |
| GET | /api/v1/brochure/templates · /api/v1/brochure/:id/download | none |
| POST·GET·DEL | /api/v1/session | rate-limited |
| GET·POST | /api/v1/account · /api/v1/deposit | wallet |
| GET | /skill.md | none |
| GET | /health | none |
{ "ok": false, "error": "Navigation timeout: 15000ms exceeded" }
401 — missing identity header on an authenticated route402 — insufficient credits; response includes deposit instructions400 — bad request (missing url/q, invalid action params)X-RateLimit-Remaining header on responses