Official Python client for the Email Funnel AI integration API. Connect with your project key and secret, then call typed, resource-oriented methods — no manual URLs, headers, or JSON.
- Python 3.9+
- Zero dependencies (standard library only)
- Fully typed, resource-oriented API with full endpoint coverage
pip install emailfunnelaifrom emailfunnelai import EmailFunnelAi
client = EmailFunnelAi(
project_key="pk_your_project_key",
project_secret="sk_your_secret_key",
)
# base_url defaults to https://app.emailfunnel.ai — pass it only for staging or local dev.
# 1. Confirm your credentials
client.validate()
# 2. Get or create a list
new_list = client.lists.create("Newsletter signups")
# 3. Sync a contact into it
client.contacts.sync(
{"email": "jane@example.com", "first_name": "Jane", "source_type": "custom_saas"}
).to_list(new_list["id"])Every group is an attribute of the client and reads as resource.verb.
# Single contact
client.contacts.sync({"email": "jane@example.com", "source_type": "app"}).to_list(list_id)
# Bulk (up to 500; the server queues automatically above 100 rows)
client.contacts.bulk(
[
{"email": "a@example.com", "source_type": "import"},
{"email": "b@example.com", "source_type": "import"},
],
source_type="import",
).to_list(list_id)
# Real-time webhook event
client.contacts.webhook("user.created", {"email": "jane@example.com"}).to_list(list_id)client.lists.all()
client.lists.create("My leads", "Optional description")
client.lists.find(list_id)client.bindings.all()
client.bindings.create("custom_crm", list_id, {"sync_enabled": True})
client.bindings.find(binding_id)
client.bindings.update(binding_id, {"sync_enabled": False})
client.bindings.delete(binding_id)
client.bindings.status(binding_id, "completed", errors_count=0)client.field_mappings.config("custom_crm")
client.field_mappings.for_binding(binding_id).get()
client.field_mappings.for_binding(binding_id).update({"email": "email", "first_name": "fname"})
client.field_mappings.for_binding(binding_id).reset()client.auto_tagging.rules("custom_crm")
client.auto_tagging.preview("custom_crm", {"email": "jane@example.com", "plan": "enterprise"})client.analytics.dashboard()
client.analytics.heatmap({"range": 60, "email_type": "all"})
client.analytics.funnels()
client.analytics.campaigns()
client.analytics.forms()client.sso.generate("owner@example.com") # signed login URL
client.sso.team_members()Successful calls return the unwrapped data payload. Any error response raises a
typed exception:
from emailfunnelai import ApiError, TransportError
try:
client.contacts.sync({"email": "invalid"}).to_list(list_id)
except ApiError as error:
error.status # 422
error.error_type # "validation_error"
error.messages # {"contact.email": ["The email field is required."]}
error.retry_after # set on 429 rate limits
except TransportError:
... # network / timeout failure (no HTTP response)Both extend EmailFunnelError, so you can catch that to handle any SDK failure.
Branch on error_type for specific conditions — e.g. a contact list that has been
deactivated rejects new members with a 409 list_inactive:
try:
client.contacts.sync(contact).to_list(list_id)
except ApiError as error:
if error.error_type == "list_inactive":
... # The target list is inactive — reactivate it or pick another list.error_type |
Status | Meaning |
|---|---|---|
invalid_credentials |
401 | Missing/invalid project key or secret |
inactive_project |
403 | The connected project is inactive |
validation_error |
422 | Request body failed validation (messages set) |
invalid_email / suppressed |
422 | Email is undeliverable or suppressed |
list_inactive |
409 | Target contact list is inactive and rejects new members |
rate_limit_exceeded |
429 | 1000 req/hour cap hit (retry_after set) |
sync_failed |
500 | Unexpected sync failure |
The client uses urllib by default. Pass any object implementing the
HttpClient protocol (e.g. a requests-backed client) to add pooling, proxies,
or logging:
client = EmailFunnelAi(project_key=..., project_secret=..., http_client=my_client)pip install -e ".[dev]"
pytestTests mock HTTP and never touch the network. A coverage test asserts the SDK exposes every documented endpoint.
MIT © Email Funnel AI