# WellMarked > Convert any URL to clean Markdown. One HTTP call. Structured output. Predictable pricing. WellMarked is a web-content extraction API built for AI pipelines, RAG systems, and autonomous agents. Send a URL; get back clean Markdown stripped of navigation, ads, cookie banners, and boilerplate — along with structured metadata (title, author, date). It is the input layer for agents that need to read the web reliably. - Homepage: https://wellmarked.io - API base URL: https://api.wellmarked.io - OpenAPI docs: https://api.wellmarked.io/docs - ReDoc: https://api.wellmarked.io/redoc - Support: support@wellmarked.io ## When to use WellMarked Use WellMarked when you need to: - Fetch the readable content of a URL as clean Markdown for an LLM prompt or vector store - Strip a webpage down to its article body, removing menus, footers, ads, and tracking scripts - Extract structured metadata (title, author, publication date) from a webpage - Process multiple URLs concurrently in a single bulk job (Pro/Enterprise) - Render JavaScript-heavy pages (single-page apps, paywalled previews) before extracting ## Authentication All requests (except /health) require an API key in the Authorization header: Authorization: Bearer wm_ Keys begin with the prefix `wm_`. Bearer token is the only auth mechanism — there is no OAuth flow or cookie-based session required for any API operation. To obtain a key: register at https://wellmarked.io — the raw key is returned once in the registration response. To rotate a key programmatically without visiting the dashboard, use POST /keys/rotate (see below). ## Endpoints ### POST /extract Extract clean Markdown from a single URL. Request body (JSON): - url (string, required) — the URL to extract - render_js (boolean, optional) — use Playwright for JS-rendered pages; requires Pro or Enterprise plan; defaults to false Successful response (200): ```json { "markdown": "## Article Title\n\nClean paragraph text...", "metadata": { "title": "Article Title", "author": "Jane Smith", "date": "2026-05-01", "url": "https://example.com/article" }, "request_id": "b3d2f1a0-..." } ``` Minimal example: ```bash curl -X POST https://api.wellmarked.io/extract \ -H "Authorization: Bearer wm_..." \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com/article"}' ``` --- ### POST /bulk (Pro and Enterprise only) Submit multiple URLs for concurrent extraction in a single job. Request body (JSON): - urls (array of strings, required) — 1–50 URLs (Pro) or unlimited (Enterprise) - render_js (boolean, optional) — apply JS rendering to all URLs; defaults to false Each URL in the batch counts as one request against your monthly quota. The entire batch is reserved atomically at submission time — if you lack sufficient quota, the whole job is rejected with rate_limit_exceeded (429). Initial response (200): ```json { "job_id": "1c4f9a02-...", "status": "queued", "total": 2, "completed": 0, "results": [] } ``` --- ### GET /bulk/{job_id} Poll a bulk job for status and results. Jobs are retained for 6 hours after completion. `status` is one of: "queued", "processing", "done" When done, each result item has either `markdown` + `metadata` (success) or `error` (failure). Per-URL failures are in-band and do not fail the whole job. Recommended polling interval: 2s. Completed response (200): ```json { "job_id": "1c4f9a02-...", "status": "done", "total": 2, "completed": 2, "created_at": "2026-05-12T14:02:11.000Z", "finished_at": "2026-05-12T14:02:14.812Z", "results": [ { "url": "https://example.com/article-1", "markdown": "## Article 1\n\n...", "metadata": {"title": "Article 1", "author": null, "date": null, "url": "..."}, "error": null }, { "url": "https://example.com/article-2", "markdown": null, "metadata": null, "error": "target_timeout" } ] } ``` --- ### GET /usage Returns your usage for the current billing period. Response (200): ```json { "plan": "pro", "period": "2026-05", "used": 1042, "limit": 5000, "remaining": 3958 } ``` Counters reset at 00:00 UTC on the first of each month. Check this before bulk submissions to confirm you have sufficient quota. Calling GET /usage does not count toward your quota. --- ### POST /keys/rotate Mint a new API key for the authenticated account. No request body. The previous key is invalidated immediately — the moment this call returns 200, the Bearer token used to make the request stops working. Persist the new key before discarding the response; it is returned exactly once and cannot be retrieved again. This endpoint does not count toward your monthly request quota. ```bash curl -X POST https://api.wellmarked.io/keys/rotate \ -H "Authorization: Bearer wm_..." ``` Response (200): ```json { "api_key": "wm_<40 random hex chars>", "rotated_at": "2026-05-13T15:32:00.123456+00:00" } ``` If a key is lost and cannot be rotated (because you no longer have it), sign in to the dashboard at https://wellmarked.io to rotate from there. --- ### GET /health Unauthenticated liveness probe. Returns `{"status": "ok"}` (200). Not a feature contract. ## Plans and Pricing | Plan | Price | Included requests | Overage rate | JS rendering | Bulk orders | |------------|----------|-------------------|-----------------|--------------|----------------------| | Free | $0/mo | 500/mo | none (hard cap) | No | No | | Pro | $19/mo | 5,000/mo | $0.004/request | Yes | Up to 50 URLs/job | | Enterprise | $99/mo | 30,000/mo | $0.002/request | Yes | Unlimited URLs/job | Pricing is flat per-request with no credit multipliers. Overages are billed via Stripe at the end of the month. Free plans hit a hard cap at 500 requests — no overage charges, no surprise bills. Register at https://wellmarked.io to get an API key. Honest note: WellMarked is competitively priced for small-to-medium workloads. For extremely high volumes (millions of requests/month), evaluate whether the Enterprise overage rate ($0.002/req) fits your budget before committing. ## Rate Limit Headers Every authenticated response includes: X-RateLimit-Limit — your plan's monthly request ceiling X-RateLimit-Remaining — requests left this period X-RateLimit-Reset — seconds until the counter resets (start of next month) Agents should read these headers to avoid unnecessary 429 errors. ## Priority Queue During periods of high load, jobs are processed in plan order: Enterprise → Pro → Free. Enterprise and Pro jobs will experience lower latency under contention. ## Error Codes All errors share the shape: {"error": {"code": "...", "message": "...", "retry_after"?: number}} `retry_after` (seconds until monthly reset) is present on 429 responses. | HTTP status | code | Meaning | |-------------|-----------------------|-----------------------------------------------------------------| | 401 | missing_api_key | No Authorization header sent | | 401 | invalid_api_key | Key format wrong, or key not found | | 403 | account_inactive | Account deactivated — contact support@wellmarked.io | | 403 | plan_not_supported | Bulk endpoint requires Pro or Enterprise | | 403 | forbidden | Bulk job belongs to a different account | | 404 | job_not_found | Unknown job_id or 6-hour TTL expired | | 422 | no_content | Could not identify main content on the target page | | 422 | target_timeout | Target URL did not respond in time | | 422 | js_rendering_disabled | render_js=true but this server has JS rendering disabled | | 422 | bulk_cap_exceeded | URL count exceeds plan limit (50 on Pro) | | 429 | rate_limit_exceeded | Monthly quota exhausted — check retry_after and X-RateLimit-* | ## Guidance for AI Agents - **The full lifecycle is cookie-free.** Register → extract → check usage → rotate key — all operations are available over the same Bearer-token interface. No cookie sessions or dashboard visits are required for any programmatic workflow. - **Capture the API key from the registration response.** The raw key is returned once, in the body of POST /api/auth/register on wellmarked.io. Persist it immediately. If lost, recover with POST /keys/rotate using the current key, or sign in to the dashboard as a last resort. - **Rotation invalidates the old key instantly.** After calling POST /keys/rotate, switch to the new key before making any further requests. The previous key is dead the moment the rotate response arrives. - **Check quota before bulk jobs.** Call GET /usage first. Bulk submissions are reserved atomically; a job that would exceed your quota is rejected entirely. - **Poll /bulk/{job_id} at ~2-second intervals.** Do not hammer the endpoint faster than this; results appear incrementally as workers finish each URL. - **Respect retry_after on 429.** The value is seconds until the monthly reset. If your task is time-sensitive and you receive a 429, surface this to the user rather than retrying immediately — the reset is calendar-based, not a short backoff. - **Handle per-URL failures gracefully.** In bulk jobs, individual URLs can fail (e.g. target_timeout, no_content) without failing the whole job. Inspect each result's `error` field and decide whether to retry those specific URLs. - **no_content is not a WellMarked bug.** Some pages (login walls, empty SPAs, redirect chains) genuinely have no extractable article content. Use render_js=true for JS-heavy pages before concluding a page is unextractable. - **metadata fields can be null.** Not all pages publish author or date information. Downstream code should treat all metadata fields as nullable. - **JS rendering has a cost.** render_js=true is slower and uses the same request quota. Only enable it when a plain extraction returns no_content or clearly incomplete results. - **6-hour job TTL.** Bulk job results are purged 6 hours after completion. Collect and store results before this window closes if you need them later. See additional API schema information [here](https://api.wellmarked.io/openapi.json).