Python SDK
Keep customer data and events in sync from your Python service. The client provides resource methods, structured errors, and pagination helpers for the maxclicks Public API.
- Zero required runtime dependencies (pure standard library
urllib). - Fully type-hinted, ships a
py.typedmarker. - Python 3.9+.
Current API coverage
The SDK groups Public API calls by resource. Use the API reference for request fields, response payloads, and operation semantics, and the examples below for language-specific method calls.
Pagination helpers follow the API traversal limits. An iterator or method named all stops at the traversal cap; use scoped queries when working with larger datasets.
Install
Install the SDK with pip:
pip install maxclicks
Initialize
Pass your API key, or set MAXCLICKS_API_KEY and construct with no arguments.
from maxclicks import Maxclicks
client = Maxclicks("max_...") # or set MAXCLICKS_API_KEY
me = client.me()
print(me["space"])
client.me() identifies the calling key, its owner, the bound space, and role.
Configuration
Construct the client with any of these options.
| Option | Default | Notes |
|---|---|---|
api_key | MAXCLICKS_API_KEY env | Bearer key. Required for all but public endpoints. |
base_url | https://api.maxclicks.ai/v1 | Use maxclicks.STAGE_BASE_URL for staging. |
timeout | 60.0 | Per-request timeout in seconds. |
max_retries | 2 | Extra attempts on 429, plus 5xx and transport/timeout errors for reads and keyed writes. |
default_headers | {} | Merged into every request. |
on_warning | None | (warnings: list[str], ctx) callback. |
Response shape
Responses are the unwrapped payload of the API's { "data": ... } envelope, returned as plain dict and list values. Wire keys are preserved verbatim in camelCase, for example record["firstName"] and schema["namePlural"], because record and event payloads carry user-defined custom-attribute keys. SDK method names and keyword arguments use snake_case.
Example
Upsert a contact, iterate every record, fire an event, and send a template.
contact = client.records.upsert("students", {"email": "[email protected]", "firstName": "Ada"})
print(contact["id"])
for record in client.records.list("students"):
print(record["id"], record.get("email"))
records.upsert creates or updates by identity (contact: id/userId/email/phone; object: id/externalId). records.create never updates an existing record and returns 409 on conflict.
Template, broadcast, segment and webhook ids are opaque strings the API returns; do not assume a uuid or a fixed prefix. Schemas take either a slug ("students") or an id, and workflows.trigger takes the reference id of the workflow's incoming-webhook step.
Pagination
List methods return a Page that holds the first page and iterates lazily across every following page. Iteration stops when hasMore is false or the offset cap of 10,000 is reached.
page = client.records.list("students", limit=100)
page.data # this page's items
page.pagination # {"limit", "offset", "totalCount", "hasMore"}
page.warnings # warnings for this page
for record in page: # every item across all pages, auto-fetching
...
everything = client.records.list("students").all() # collect into a list
events.list is cursor-paginated and returns a CursorPage with the same interface. Its pagination is {"limit", "nextCursor", "hasMore"}.
for event in client.events.list("purchase_completed", limit=200):
print(event["id"])
Errors
Every failure raises a typed exception. All API errors carry status, code, type, message, headers, and the raw body.
| Exception | Status |
|---|---|
MaxclicksBadRequestError | 400 |
MaxclicksAuthenticationError | 401 |
MaxclicksPermissionError | 403 |
MaxclicksNotFoundError | 404 |
MaxclicksConflictError | 409 |
MaxclicksUnprocessableEntityError | 422 |
MaxclicksRateLimitError | 429 (has .retry_after_seconds) |
MaxclicksServerError | 5xx |
MaxclicksConnectionError | transport failure |
MaxclicksTimeoutError | timeout |
MaxclicksConfigurationError | programmer error, raised before any request |
MaxclicksError is the base class for all of them.
from maxclicks import MaxclicksNotFoundError, MaxclicksRateLimitError, MaxclicksError
try:
client.records.get("students", "missing-id")
except MaxclicksNotFoundError as error:
print(error.status, error.code, error.message)
except MaxclicksRateLimitError as error:
print("retry after", error.retry_after_seconds, "seconds")
except MaxclicksError as error:
print("something went wrong:", error)
Reads are retried on 429, any 5xx, and transport/timeout errors, up to max_retries extra attempts, using exponential backoff with full jitter (base ~0.5s, cap ~8s). A write is retried on those only when you pass idempotency_key; without one, a timed-out write is reported to you rather than repeated, because it may already have landed. The client also retries 429 responses according to its configured retry budget. A Retry-After header sets the delay floor.
Idempotency
Write endpoints that support it accept an idempotency_key. Retrying with the same key returns the original result instead of acting twice.
client.records.upsert(
"students",
{"email": "[email protected]", "firstName": "Ada"},
idempotency_key="signup-2026-07-18-ada",
)
Pass an idempotency key to writes such as records.create, records.upsert, suppressions.batch_create, suppressions.batch_delete, templates.send, broadcasts.send, and workflows.trigger. See the supported API writes and retry contract before replaying an operation.
Warnings
The API may attach warnings to a response, in the body's warnings array or, for empty-body endpoints like workflows.trigger, in the URL-encoded maxclicks-Warning-Message response header. Provide on_warning to receive them.
client = Maxclicks(on_warning=lambda warnings, ctx: print(ctx.method, ctx.path, warnings))
Rate limits
Per API key: 100 requests/second for reads and 25 requests/second for writes. Exceeding a limit returns 429, which the SDK retries with the server's Retry-After delay.
Resource surface
API resources are reachable as me, schemas, attributes, records, suppressions, events, domains, senders, topics, segments, broadcasts, templates, webhooks, workflows, forms, and emails.
| Resource | Methods |
|---|---|
client.me() | Current key context (a method on the client itself) |
client.schemas | list get |
client.attributes | list |
client.records | create upsert list get update delete audit_trail |
client.suppressions | list create batch_create batch_delete delete |
client.events | fire fire_batch list |
client.domains | list get |
client.senders | list |
client.topics | list get |
client.segments | list create get delete count list_contacts |
client.broadcasts | list get update send list_runs get_metrics |
client.templates | list get send |
client.webhooks | create list get update delete rotate_secret |
client.workflows | list get trigger pause unpause list_runs get_run |
client.forms | submit confirm_double_opt_in |
client.emails | unsubscribe_one_click |
Create schemas, attributes, domains, senders, topics, templates and broadcasts in the maxclicks app, then use resource methods to read them or act on prepared work. Manage API keys and CSV imports in the app. See the API reference for request fields and response details.
Set the entity up once in the app, then trigger, read and measure it from Python: build a broadcast in the app and call broadcasts.send, design a template in the app and call templates.send, publish a workflow in the app and call workflows.trigger.
Retry outcomes
A repeated write can return a recorded result, including an error. If the API reports idempotency_outcome_unknown, inspect the affected resource or run history before creating a new operation. Keep the original key and payload while you reconcile. See the idempotency guide for supported operations and retention.