WellMarked Docs
SDKs & integrations

JavaScript / TypeScript SDK

npm install wellmarked — dual ESM and CJS, bundled types, runs on Node, Deno, Bun, Cloudflare Workers, and the browser.

npm install wellmarked

Dual ESM + CJS with bundled .d.ts. Runs on Node 18+, Deno, Bun, Cloudflare Workers, and the browser.

Quickstart

import { WellMarked } from "wellmarked";

const wm = new WellMarked({ apiKey: "wm_..." });
const result = await wm.extract("https://example.com");
console.log(result.markdown);
console.log(`${result.metadata.title} by ${result.metadata.author}`);

apiKey falls back to the WELLMARKED_API_KEY environment variable — on Node.js only, since the other supported runtimes have no process.env.

The surface

import { WellMarked } from "wellmarked";

const wm = new WellMarked({ apiKey: "wm_..." });

// Single extract
const result = await wm.extract("https://example.com");

// Bulk
let job = await wm.bulk(["https://a.example", "https://b.example"]);
job = await wm.waitForJob(job.jobId);

// Crawl — the same waitForJob works on either kind of jobId
job = await wm.crawl("https://docs.example.com", { depth: 2 });
job = await wm.waitForJob(job.jobId);

// Usage + rotate
const usage = await wm.getUsage();
const rotated = await wm.rotateKey();   // the client swaps to the new key for you

waitForJob widens the type

It returns BulkJob | CrawlJob, discriminated by kind. If you reassign a variable across the call, declare the union up front:

import { WellMarked, type BulkJob, type CrawlJob } from "wellmarked";

let job: BulkJob | CrawlJob = await wm.bulk([...]);
job = await wm.waitForJob(job.jobId);

Errors

Exceptions inherit from WellMarkedError:

ExceptionStatus
AuthenticationError401
PermissionDeniedError403
NotFoundError404
UnprocessableEntityError422
RateLimitError429
InternalServerError5xx
APIConnectionErrortransport failure, no response

Each carries code, statusCode, retryAfter, retryAfterMs, and requestId.

import { WellMarked, RateLimitError } from "wellmarked";

try {
  const result = await wm.extract("https://example.com");
} catch (e) {
  if (e instanceof RateLimitError) {
    if (e.code === "rate_limit_too_fast") {
      await sleep(e.retryAfterMs!);
    } else {
      // monthly quota spent — retrying will not help
    }
  }
}

See Errors for every code the API can emit.

Retries and idempotency

A submission that fails on a retryable error is retried internally reusing the same idempotency key, so an SDK-level retry can never double-submit. Tune the attempt count with maxRetries (default 2).

Webhook verification

import { verifyWebhook, WebhookVerificationError } from "wellmarked";

const payload = await verifyWebhook({
  secret: process.env.WELLMARKED_WEBHOOK_SECRET!,
  headers: req.headers,
  body: rawBody,   // bytes, NOT a parsed JSON object
});

Built on crypto.subtle, so it works everywhere the client does with no runtime-specific imports. See Webhooks.

Full reference

wellmarked on npm