HTML select
POST /api/html-selectRun a CSS selector against an HTML string and return the matches (text, attrs, and optionally outerHTML for each). The deterministic alternative to regex when you already have the HTML and know the selector - pairs with /api/render or any page you've fetched yourself.
Input
| Field | Type | Description |
|---|---|---|
html * | string | Raw HTML string to query (max 5MB) |
selector * | string | CSS selector (e.g. "h1", ".price", "article > p:first-of-type") |
limit | number | Max matches to return, 1-200 (default 25) |
attr | string | When set, return only this attribute's value per match (e.g. "href", "data-id") - keeps responses compact |
includeHtml | boolean | Include outerHTML of each match (default false - text + attrs only) |
Example output
{
"count": 2,
"matches": [
{
"text": "One",
"attrs": {}
},
{
"text": "Two",
"attrs": {}
}
]
}
Try it - see the 402 challenge (free)
curl -i -X POST https://base-sentinel-production.up.railway.app/api/html-select \
-H "Content-Type: application/json" \
-d '{"html":"<html><body><h1>One</h1><h1>Two</h1></body></html>","selector":"h1","limit":25}'
The response is HTTP 402 Payment Required with exact payment requirements. Any x402 v2 client pays automatically and retries:
Paid call (JavaScript agent)
import { wrapFetchWithPayment } from "@x402/fetch";
import { x402Client } from "@x402/core/client";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const client = new x402Client();
registerExactEvmScheme(client, { signer: privateKeyToAccount(KEY) });
const payFetch = wrapFetchWithPayment(fetch, client);
const res = await payFetch("https://base-sentinel-production.up.railway.app/api/html-select", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"html": "<html><body><h1>One</h1><h1>Two</h1></body></html>",
"selector": "h1",
"limit": 25
}),
});
No wallet? Pay with compute
This is a pure-CPU tool, so an agent without a wallet can pay with proof-of-work instead of USDC: fetch a challenge, solve the sha256 puzzle (16 leading zero bits - a fraction of a second of CPU, no money, no AI tokens), and resend with the X-Pow-Solution header.
import { createHash } from "node:crypto";
const lz = (b) => { let t = 0; for (const x of b) { if (!x) { t += 8; continue; } t += Math.clz32(x) - 24; break; } return t; };
const c = await (await fetch("https://base-sentinel-production.up.railway.app/api/pow/challenge?slug=html-select")).json();
let n = 0;
while (lz(createHash("sha256").update(c.challenge + ":" + n).digest()) < c.difficulty) n++;
await fetch("https://base-sentinel-production.up.railway.app/api/html-select", { method: "POST", headers: { "X-Pow-Solution": c.token + ":" + n, "Content-Type": "application/json" }, body: JSON.stringify({"html":"One
Two
","selector":"h1","limit":25}) });
Part of these workflows
This tool is one step in a curated multi-tool workflow - agents can fetch the whole sequence as an MCP prompt or call https://base-sentinel-production.up.railway.app/api/skill-packs/{slug}/prompt.
- Structured scrape - Pull structured data out of any web page deterministically - articles to clean text, tables to JSON rows, specific elements via CSS selector - without writing regex against raw HTML.
Related tools
Extract article
POST /api/extractExtract the main article content from any public URL as clean markdown. Returns title, byline, excerpt, word count, and …
try in playground →Page metadata
GET /api/metaFetch page metadata for a URL: title, description, OpenGraph, Twitter cards, canonical URL, favicon.
try in playground →Browser render
POST /api/renderRender a page in a real headless Chromium browser (JavaScript executed), then extract the main content as clean markdown…
try in playground →