WellMarked Docs
SDKs & integrations

Python SDK

pip install wellmarked — sync and async clients, typed responses, typed exceptions, and automatic job polling.

pip install wellmarked

The official Python client wraps every endpoint with typed responses, sync and async clients, and HTTP status codes mapped onto typed exceptions.

Quickstart

from wellmarked import WellMarked

with WellMarked(api_key="wm_...") as wm:
    result = wm.extract("https://example.com")
    print(result.markdown)
    print(result.metadata.title, "by", result.metadata.author)

The client is a context manager so the underlying HTTP connection pool is closed deterministically. api_key falls back to the WELLMARKED_API_KEY environment variable.

The surface

from wellmarked import WellMarked

with WellMarked(api_key="wm_...") as wm:
    # Single extract
    result = wm.extract("https://example.com")

    # Bulk
    job = wm.bulk(["https://a.example", "https://b.example"])
    job = wm.wait_for_job(job.job_id)

    # Crawl — the same wait_for_job works on either kind of job_id
    job = wm.crawl("https://docs.example.com", depth=2)
    job = wm.wait_for_job(job.job_id)

    # Usage + rotate
    usage = wm.get_usage()
    rotated = wm.rotate_key()   # the client swaps to the new key for you

wait_for_job is polymorphic

It returns a BulkJob or a CrawlJob based on the API's kind discriminator. Check isinstance(job, CrawlJob) or job.kind before reading crawl-specific fields such as truncated or item.depth.

Async

AsyncWellMarked has the same method names.

from wellmarked import AsyncWellMarked

async with AsyncWellMarked(api_key="wm_...") as wm:
    result = await wm.extract("https://example.com")

Errors

Exceptions inherit from WellMarkedError. The ones you will actually catch:

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

Each carries code, status_code, retry_after, retry_after_ms, and request_id.

import time
from wellmarked import WellMarked, RateLimitError

with WellMarked(api_key="wm_...") as wm:
    try:
        result = wm.extract("https://example.com")
    except RateLimitError as e:
        if e.code == "rate_limit_too_fast":
            time.sleep(e.retry_after_ms / 1000)
        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 max_retries (default 2).

Webhook verification

from wellmarked import verify_webhook, WebhookVerificationError

payload = verify_webhook(
    secret=os.environ["WELLMARKED_WEBHOOK_SECRET"],
    headers=request.headers,    # case-insensitive; dict works too
    body=raw_body_bytes,        # raw bytes, NOT a parsed object
)

All arguments are keyword-only. See Webhooks.

Full reference

wellmarked on PyPI