Extract
POST /v1/extract — turn URLs into clean markdown.
POST /v1/extract fetches one or more URLs and returns clean markdown. Handles HTML, PDF, DOCX, and SPA hydration; auto-escalates to a real Chrome render when needed.
This endpoint requires an Idempotency-Key header. The Python and Node.js SDKs auto-generate one per call. If you call it directly, set your own UUID.
Quick example
from brime import Brime
client = Brime()
result = client.extract(urls=[
"https://example.com",
"https://en.wikipedia.org/wiki/Generative_artificial_intelligence",
])
for r in result.results:
print(r.url, r.method, r.content_type, len(r.markdown))
for f in result.failed:
print("FAIL", f.url, f.error.code, f.error.message)import { Brime } from "@brime-ai/brime";
const client = new Brime();
const result = await client.extract({
urls: ["https://example.com", "https://en.wikipedia.org/wiki/Generative_artificial_intelligence"],
});
for (const r of result.results) {
console.log(r.url, r.method, r.content_type, r.markdown.length);
}
for (const f of result.failed) {
console.log("FAIL", f.url, f.error.code, f.error.message);
}curl https://api.brime.dev/v1/extract \
-H "Authorization: Bearer $BRIME_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"urls": ["https://example.com", "https://en.wikipedia.org/wiki/Generative_artificial_intelligence"]}'Request
| Field | Type | Default | Description |
|---|---|---|---|
urls | string | string[] | required | 1–10 URLs. |
clean | bool | true | Smart-clean HTML→markdown pipeline (readability-style noise strip, CMS template cleanup, citation strip). false falls back to a raw regex strip + htmd. See Smart-clean below. |
include_metadata | bool | false | Adds debug fields (timings, detection) under metadata on each result. The unified metadata block (title, description, author, published_date, canonical, og_image, language) is always emitted regardless of this flag. |
per_url_timeout_ms | integer | 25000 | 5,000–30,000. |
Headers
| Header | Required | Notes |
|---|---|---|
Authorization | yes | Bearer sk-brime-... |
Idempotency-Key | yes | Any string. SDK auto-generates UUID4 if you don't set. |
Response
{
"results": [
{
"url": "https://example.com",
"markdown": "# Example Domain\n\nThis domain is for use in...",
"method": "worker_static",
"content_type": "html",
"status": 200,
"latency_ms": 312,
"metadata": {
"title": "Example Domain",
"description": null,
"author": null,
"published_date": null,
"canonical": "https://example.com/",
"og_image": null,
"language": "en"
}
}
],
"failed": [
{
"url": "https://blocked.example.com",
"error": {
"code": "bot_challenge",
"message": "Bot challenge detected; browser tier also failed",
"needs_browser": false
}
}
],
"request_id": "req_01J...",
"credits_used": 2,
"latency_ms": 1845
}Result fields
| Field | Type | When present | Notes |
|---|---|---|---|
url | string | always | The deduped input URL. |
markdown | string | always | Extracted text (markdown for HTML/PDF, plain text for DOCX / hydration fallback). |
method | string | always | See Tier dispatch. |
content_type | string | always | "html", "pdf", or "docx". |
status | integer | usually | Origin HTTP status. |
latency_ms | integer | usually | End-to-end per-URL latency. |
render_latency_ms | integer | chrome_render only | Browser-render time. |
metadata | object | always (when worker emits any field) | Unified page metadata, see below. |
detection | string | when include_metadata=true | Engine signal trail (e.g. "static", "bot:datadome,escalated"). |
metadata block
Always an object when present; each sub-field is a non-empty string or null. The block reflects what the engine could derive from the page; DOCX results always have all-null metadata.
| Field | Source priority |
|---|---|
title | <meta og:title> → <meta twitter:title> → <title> |
description | <meta og:description> → <meta name=description> |
author | <meta article:author> → <meta name=author> |
published_date | <meta article:published_time> → name=date → pubdate → dc.date.issued → JSON-LD datePublished |
canonical | <link rel=canonical> → <meta og:url> → request URL |
og_image | <meta og:image> → <meta twitter:image> |
language | <html lang> → <meta og:locale> |
Tier dispatch
method | Tier | What ran |
|---|---|---|
worker_pdf | static | PDF parsed and converted to markdown. |
worker_docx | static | DOCX parsed to plain text. |
worker_static | static | HTML cleaned and converted to markdown. |
worker_hydration | static | Single-page app hydration data extracted as JSON. |
worker_rust_md | static | Legacy method retained for cached-response parsing. |
chrome_render | browser | Full headless-browser render. |
The browser tier auto-escalates when:
- A bot challenge is detected (Cloudflare, hCaptcha, DataDome, etc.).
- A single-page app returned no content after static hydration.
- A static fetch returned a non-2xx status.
Pricing is 1 credit per deduped input URL regardless of tier — Chrome escalations are not surcharged. See pricing.
failed[].error.code
| Code | Meaning |
|---|---|
fetch_failed | Origin returned non-2xx (HTTP status in detection). |
bot_challenge | Bot/captcha pattern detected; Tier 2 also failed. |
garbled | Body was binary or non-text. |
spa_no_content | SPA without hydration; Tier 2 also empty. |
render_failed | Chrome render call failed, returned empty, or returned thin content (text_len < 100). |
network_error | DNS, TCP, or fetch abort. |
unsupported_format | File type not in supported set. |
internal_error | Catch-all for unexpected engine error. |
Smart-clean
The HTML → markdown pipeline runs a deterministic readability-style pre-processor by default. Six phases:
- DOM parse (
linkedom). - Noise selector strip —
<script>/<style>/<nav>/<footer>/<aside>, cookie banners, ad containers, framework chrome (Wikipedia sidebars, etc.). - Semantic content cascade (
<article>/<main>/[role="main"]priority, multi-container merge for CMS pages). - Density-scoring fallback when the cascade returns empty.
htmd_wasmHTML→markdown conversion.- Markdown post-process: CMS template tokens stripped, AEM UUID content-references removed, inline citations (
[1],[1,2]) trimmed, "References" / "See also" section-cutoff.
Pass clean: false to opt out — you'll get the pre-refactor pipeline (regex strip → htmd, no post-process). Same response shape, lower latency (~180 ms faster on average), but noisier output. Use it for A/B testing or when smart-clean over-trims a specific site.
Smart-clean applies to both worker_static and chrome_render paths. PDF, DOCX, and hydration outputs are unaffected.
Edge cases
- Mixed success: a request with 5 URLs may return 4 in
resultsand 1 infailed. The 200 status applies to the request as a whole; per-URL outcomes live in those arrays. - Idempotent retries: if you re-send with the same
Idempotency-Keywithin 24 h, you get the cached response and pay nothing. - Empty
urls:400 invalid_request. - More than 10 URLs:
400 invalid_request. chrome_renderthin output: A successful Chrome render that yields fewer than 100 characters is demoted tofailed[]withcode: "render_failed"rather than returned as an emptyresults[]entry.
Errors
| Status | code | Meaning |
|---|---|---|
| 400 | invalid_request | Malformed body, empty urls, >10 URLs. |
| 401 | unauthorized | Missing/revoked API key. |
| 429 | rate_limited | Per-key budget hit. |
| 502 | upstream_error | Browser tier or fetcher degraded. |