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 |
|---|---|---|
| REST | The run is short and you just want the 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. REST
The connection is held until the run finishes, so timeout is really "how
long may this workflow take". It defaults to 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file makra-0.0.3.tar.gz.
File metadata
- Download URL: makra-0.0.3.tar.gz
- Upload date:
- Size: 49.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98180f2268fd81c0680c73770dbb78d3f2c0e4e4d51e34858aa0e62b741161e1
|
|
| MD5 |
9d68e25ca62a78f8510a34177acb70cf
|
|
| BLAKE2b-256 |
898d9db624d8c0c06266a323b8c44ac50819be1e165a718ec7d5e02c0674674b
|
File details
Details for the file makra-0.0.3-py3-none-any.whl.
File metadata
- Download URL: makra-0.0.3-py3-none-any.whl
- Upload date:
- Size: 35.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c245ea26c7481c355cd0c03ae864d0abad2d84cae18477aa9ad341160a55ba3
|
|
| MD5 |
ec4e58e15e2537317652d9c3cde8006a
|
|
| BLAKE2b-256 |
cf908e560fbaec5f18a50c0b1c25541a66c84f7f7f6d3d8f9ec95e1b1774a465
|