POST /v1/batch
Read, score, or extract up to 50 URLs in a single call — an explicit list or a whole site. 1 credit per URL; failed URLs are refunded.
Endpoint
[02]POST https://api.buildonto.dev/v1/batch
Authorization: Bearer onto_sk_live_YOUR_KEY
Content-Type: application/jsonRequest body
[03]Supply either urls (an explicit list) or site (a URL whose pages are auto-discovered). Discovery reads /sitemap.xml at the site root, and falls back to the links on the page you pass. Only same-host URLs are kept. There is no fresh option.
Explicit list of http(s) URLs to process (max 50). URLs past the 50th, and URLs that can't be parsed, are dropped silently. Use this OR "site".
URL of a site whose pages are auto-discovered on the same host. Use this OR "urls".
What to do per URL. Default: 'read-and-score'. An unknown value silently becomes 'read-and-score'. 'read' returns Markdown only; 'extract' returns JSON-LD/OpenGraph/meta plus the score fields where scoring is available.
Site mode only: max pages to discover, 1–50 (default 25).
Response
[04]Success (200): a results array, one entry per URL. A page that can't be read (robots, WAF, 404) comes back with ok: false and an error — it never fails the whole batch. Per-URL fields depend on mode: markdown for read modes, structured + counts for extract. Both read-and-score and extract also carry aio_score, grade and hallucination_risk whenever scoring is available (PDFs carry text but no score). Batch markdown is cut at 12,000 characters per URL. A cut page ends with …[truncated by /v1/batch — call /v1/read on this URL for the full page]. There is no per-result truncated flag, so check for that marker. Call /v1/read on that URL for the full body.
A success item has url, ok: true, title, reduction_percent and the mode's fields above. A failed item is { url, ok: false, error: { code, message } }. The top level has status, mode, source ("urls" or "site"), requested, succeeded, results and cache.
Onto fetches 8 URLs at a time, in fixed rounds. A slow URL holds up the rest of its round.
{
"status": "success",
"mode": "read-and-score",
"source": "urls",
"requested": 3,
"succeeded": 2,
"results": [
{
"url": "https://stripe.com/pricing",
"ok": true,
"title": "Pricing — Stripe",
"aio_score": 88,
"grade": "Good",
"hallucination_risk": "low",
"reduction_percent": 96.4,
"markdown": "# Stripe pricing\n\n…"
},
{
"url": "https://blocked.example.com",
"ok": false,
"error": { "code": "ROBOTS_BLOCKED", "message": "Target site's robots.txt disallows AI crawlers." }
}
],
"cache": { "hit": false, "ttl_seconds": 3600 }
}Errors: see error codes. Whole-request errors: INVALID_REQUEST (400, no valid urls/site), INVALID_URL (400, site could not be parsed or points at a private or internal host), UNAUTHORIZED (401), NO_RESULTS (404, site discovery found nothing — in practice discovery always returns at least the start URL), RATE_LIMITED (429), CONCURRENT_LIMIT (429), PAYMENT_REQUIRED (402). Per-URL failures are reported inline, not as a top-level error.
Billing
[05]A batch of N URLs costs N credits (N is at most 50). The credits are taken before any URL is fetched, then every ok: false result is refunded.
The quota check is all-or-nothing. If N is more than your monthly plan credits left, all N are billed to bought credits on a paid plan. On Free, the whole batch gets RATE_LIMITED. A batch holds one concurrency slot, however many URLs it has.
Response headers: X-Onto-Cache, X-RateLimit-Remaining, X-RateLimit-Reset, X-Onto-Billed, X-Concurrent-Remaining, and X-Credits-Remaining when the batch was billed to credits. The credit headers already include the refunds.
Examples
[06]cURL — explicit list:
curl -X POST https://api.buildonto.dev/v1/batch \
-H "Authorization: Bearer $ONTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://stripe.com/pricing", "https://stripe.com/docs"],
"mode": "read-and-score"
}'cURL — whole site, extract mode:
curl -X POST https://api.buildonto.dev/v1/batch \
-H "Authorization: Bearer $ONTO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "site": "https://stripe.com", "limit": 25, "mode": "extract" }'Node (fetch):
const res = await fetch('https://api.buildonto.dev/v1/batch', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.ONTO_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
urls: ['https://stripe.com/pricing', 'https://stripe.com/docs'],
mode: 'read-and-score',
}),
});
const data = await res.json();
for (const r of data.results) {
// r.markdown is capped at 12,000 chars by /v1/batch
if (r.ok) console.log(r.url, r.aio_score, r.markdown.length);
}