Skip to main content

Makra Python SDK

The official Python client for the Makra web extraction API.

Install

pip install makra

For local development from this repository:

pip install -e sdk/python

Quick start

from makra import Makra

with Makra(api_key="mk_live_...") as client:
    result = client.extract(
        urls=["https://example.com"],
        schema={"title": "The page title"},
    )
    print(result)

Every operation exists in two flavours: Makra (synchronous) and AsyncMakra (awaitable). The examples below use the synchronous client; the async one is identical with await and async with.

Three ways to run a workflow

Pick by how long the work takes and how much you want to watch it happen.

Mode Use when Method
Convenient You want one call to return the final answer extract / schema
Streaming You want live progress, e.g. to drive a UI extract_stream / schema_stream
Deferred The run is long, or the caller cannot stay online submit_extract / submit_schema

1. Convenient

extract and schema durably submit the workflow, poll it, and fetch its stored result. You still make one SDK call, but no HTTP connection has to stay open while the extraction runs. timeout is the total time the SDK waits; if it expires, the server-side run continues and the raised MakraTimeoutError includes its run_id. The default is 300 seconds per origin page. Paginated extracts (pagination.additional_pages) and sequential multi-URL extracts scale that budget automatically unless you pass timeout yourself.

from makra import (
    EventTypes,
    ExtractOptions,
    Iso3166Alpha2,
    Makra,
    ProxyContinents,
    ProxyRegion,
    ProxyRegionScopes,
    StreamDetailTypes,
    ValidationModes,
)

data = client.extract(
    urls=["https://example.com/products"],
    schema={"products": [{"name": "string", "price": "number"}]},
    config=ExtractOptions(
        validation_mode=ValidationModes.REPAIR,
        additional_pages=2,
        proxy_region=ProxyRegion.country(Iso3166Alpha2.DE),
        recovery_retry=True,
        recovery_retry_delay_ms=2000,
    ),
    timeout=120,
)

Nested dictionaries remain supported and produce the same wire payload:

data = client.extract(
    urls=["https://example.com/products"],
    schema={"products": [{"name": "string", "price": "number"}]},
    config={
        "validation_mode": ValidationModes.REPAIR,
        "pagination": {"enabled": True, "additional_pages": 2},
        "crawler": {
            "proxy": {
                "region": {
                    "scope": ProxyRegionScopes.COUNTRY,
                    "value": Iso3166Alpha2.DE,
                }
            },
            "recovery": {"retry": True, "retry_delay_ms": 2000},
        },
    },
)

Discover what a page contains before you write a schema:

page_schema = client.schema(
    "https://example.com/products",
    config={
        "crawler": {
            "proxy": {
                "region": {
                    "scope": ProxyRegionScopes.CONTINENT,
                    "value": ProxyContinents.EUROPE,
                }
            }
        }
    },
)

2. Streaming

The stream carries lifecycle and progress events, not the extracted data. When a terminal event arrives, fetch the stored result.

run_id = None
for event in client.extract_stream(urls=urls, schema=schema):
    run_id = event.run_id
    print(event.sequence, event.type, event.detail_type)
    if event.type == EventTypes.STEP_PROGRESS:
        if event.detail_type == StreamDetailTypes.RUN_TITLE_GENERATED:
            print("title:", event.payload.get("title"))
    if event.is_terminal:
        print("finished:", event.status, event.reason)

data = client.get_run_result(run_id)

Each WorkflowEvent exposes type, sequence, run_id, the raw payload, and the shortcuts is_terminal, detail_type, status, reason, success. Compare event.type to EventTypes and event.detail_type to StreamDetailTypes.

If the connection drops mid-run, the SDK reconnects to the run's event endpoint with Last-Event-ID, so you see every event exactly once. Silence longer than stream_idle_timeout (90s, versus the gateway's 15s heartbeat) counts as a dropped connection.

3. Deferred runs

Submit now, collect later — from a different process, if you like.

run = client.submit_extract(urls=urls, schema=schema)
print(run.id, run.state)          # queued

run.wait()                        # polls until terminal
data = run.result()

Use run_is_terminal and run_succeeded when you have a run mapping and need to distinguish infrastructure completion from domain success. A completed run with no success field is treated as successful; success: false is not.

from makra import run_is_terminal, run_succeeded

if run_is_terminal(snapshot) and run_succeeded(snapshot):
    data = client.get_run_result(snapshot["id"])

A handle also supports refresh(), stream(), and cancel(). Everything a handle does is available directly on the client too — get_run, list_runs, wait_for_run, stream_run_events, get_run_result, cancel_run — so a run id is all you need to resume from anywhere.

for event in client.stream_run_events(run_id, last_event_id=42):
    ...

Configuration

Settings resolve as explicit argument → environment variable → default.

Argument Environment Default
api_key MAKRA_API_KEY makra-development-key
base_url MAKRA_BASE_URL https://api.makralabs.org
timeout MAKRA_TIMEOUT (seconds) 300 per origin page
max_retries MAKRA_MAX_RETRIES 2
connect_timeout 10
stream_idle_timeout 90
retry_backoff 0.5
default_headers {}

default_headers may add tracing or application headers. It cannot override SDK-owned names (Api-Key, Content-Type, Accept, User-Agent, Idempotency-Key, Prefer, Last-Event-ID); pass api_key, idempotency_key, or last_event_id instead.

from makra import DEVELOPMENT_BASE_URL, Makra

client = Makra(base_url=DEVELOPMENT_BASE_URL, timeout=60, max_retries=4)

Authentication uses the Api-Key header. A client owns an HTTP connection pool, so build one per application and close it when done.

Retries and idempotency

Transient failures (408, 409, 425, 429, 5xx) are retried with exponential backoff and full jitter, honouring Retry-After when the server sends it.

Retrying a submission is only safe because the SDK attaches a fresh Idempotency-Key to every one: the gateway replays the original run instead of starting a second billable one. Pass your own idempotency_key to make a submission replayable across process restarts too.

Errors

Catch MakraError for anything the SDK raises, or a subclass to handle one condition.

MakraError
├── MakraAPIError                     .status_code .code .body .request_id
│   ├── MakraAuthenticationError      401
│   ├── MakraInsufficientCreditsError 402  .required_credits .available_credits
│   ├── MakraPermissionError          403
│   ├── MakraNotFoundError            404
│   ├── MakraInvalidRequestError      4xx  .field .reason .index
│   ├── MakraRateLimitError           429  .retry_after .concurrency
│   └── MakraServerError              5xx
├── MakraConnectionError              the request never reached the API
│   └── MakraTimeoutError
├── MakraStreamError                  .run_id
│   └── MakraResultError              .run_id .location   (also a MakraStreamError)
└── MakraRunFailedError               .run_id .state .run

MakraResultError is raised when a stored-result redirect is missing Location or the location is not an absolute HTTP(S) URL. It subclasses MakraStreamError so existing stream catches keep working; a future major version may reparent it under MakraError. Presigned query strings are never included in the message or location.

Malformed arguments raise ValueError before any network call, so a typo in a config key costs nothing.

from makra import MakraInsufficientCreditsError, MakraRateLimitError

try:
    client.extract(urls=urls, schema=schema)
except MakraInsufficientCreditsError as error:
    print("need", error.required_credits, "have", error.available_credits)
except MakraRateLimitError as error:
    print("retry after", error.retry_after)

Async

import asyncio
from makra import AsyncMakra

async def main():
    async with AsyncMakra() as client:
        async for event in client.extract_stream(urls=urls, schema=schema):
            print(event.type)
        run = await client.submit_schema("https://example.com")
        await run.wait()
        print(await run.result())

asyncio.run(main())

See ../SPEC.md for the complete contract.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

makra-0.0.4.tar.gz (50.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

makra-0.0.4-py3-none-any.whl (36.2 kB view details)

Uploaded Python 3

File details

Details for the file makra-0.0.4.tar.gz.

File metadata

  • Download URL: makra-0.0.4.tar.gz
  • Upload date:
  • Size: 50.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.0

File hashes

Hashes for makra-0.0.4.tar.gz
Algorithm Hash digest
SHA256 ed1146f73de9cf1d88d54487b0d5bb717401715cd32263b0aa12bae87e2f21a4
MD5 4b5b6409219258e164d80d0dc4ed3af8
BLAKE2b-256 fcd7580599d97d296fe5d6d859b829936f41f674f447a892039b2fa28636aa8c

See more details on using hashes here.

File details

Details for the file makra-0.0.4-py3-none-any.whl.

File metadata

  • Download URL: makra-0.0.4-py3-none-any.whl
  • Upload date:
  • Size: 36.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.0

File hashes

Hashes for makra-0.0.4-py3-none-any.whl
Algorithm Hash digest
SHA256 ab24edec1b5e30ba9df6b02c4de704d5c38741215b88a5e344cc6f89807c3f92
MD5 067ba2e0f8f0258fdadad1a65f7d73da
BLAKE2b-256 cdd4afa4ae87c50e8d59f687f3744408d5a0fea709beada82e3930a0b3838799

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.0.4 This release

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page