Bento Python Guide
Install the SDK from GitHub, configure BentoAPI with your Site UUID plus API keys, and reuse the client across web requests, jobs, and serverless handlers.
Use the Bento Python SDK to batch events, sync subscribers, and trigger transactional mail from Flask, Django, FastAPI, or any worker queue.
Getting Started
Beginner Guide
Intermediate Guide
Advanced Guide
Reference
Getting Started
Step 1
Install the Bento Python SDK
Install the SDK with your framework’s package manager and keep it alongside your other HTTP clients so configuration, logging, and credentials stay centralized.
Using PIP
bashpip install git+https://github.com/bentonow/bento-python-sdk.gitStep 2
Configure the client
Store your Site UUID, publishable key, and secret key in environment variables or a secrets manager. Register the Bento services during your app’s bootstrap so every controller, job, or command reuses the same client.
Client Setup
pythonfrom bento_api import BentoAPI
bento = BentoAPI(
site_uuid='YOUR_SITE_UUID',
username='YOUR_PUBLISHABLE_KEY',
password='YOUR_SECRET_KEY'
)Beginner Guide
Tracking your first event
Events are the fastest way to add subscribers, kick off automations, and capture context in a single API call. Track onboarding milestones, purchases, or page views, then personalize with Liquid.
- Pair every event with subscriber metadata so flows can branch without extra imports.
- Include transactional context (cart items, amounts, IDs) for downstream personalization.
- Prefer events over bespoke subscriber mutations—automations can add tags or update fields later.
| Detail | Liquid tag |
|---|---|
| Product details | {{ event.details.product.size }} |
| Purchase amount | {{ event.details.value.amount }} |
| Transaction ID | {{ event.details.unique.key }} |
| Payment method | {{ event.details.value.payment_method }} |
Track a simple page view
pythonevents = [
{
'type': '$pageView',
'email': 'user@example.com',
'details': {
'url': '/home',
'title': 'Home Page'
}
}
]
bento.batch_create_events(events)Track a form submission
pythonevents = [
{
'type': '$formSubmitted',
'email': 'user@example.com',
'details': {
'formName': 'Newsletter Signup',
'source': 'Homepage'
}
}
]
bento.batch_create_events(events)Managing subscribers
You can still create, tag, or unsubscribe subscribers directly when you need to correct data or build admin tooling. Keep these calls reserved for operational work—events remain the preferred entry point.
Create a new subscriber
pythonsubscriber = bento.create_subscriber(email='new@example.com')
print(f"New subscriber: {subscriber}")Tag a subscriber
pythoncommands = [
{
'command': 'add_tag',
'email': 'user@example.com',
'query': 'Newsletter'
}
]
result = bento.execute_commands(commands)
print(f"Command result: {result}")Unsubscribe A subscriber
pythoncommands = [
{
'command': 'unsubscribe',
'email': 'user@example.com',
'query': None
}
]
result = bento.execute_commands(commands)
print(f"Unsubscribe result: {result}")Common use cases
- Track onboarding, purchase, and lifecycle events so automations fire with one API call.
- Tag subscribers in response to events rather than separate API loops.
- Store contextual data (cart contents, plan tier) for segmentation and personalization.
Tracking a user login "Event"
pythonevents = [
{
'type': '$login',
'email': 'user@example.com',
'details': {
'method': 'password',
'device': 'mobile'
}
}
]
bento.batch_create_events(events)Updating user information
pythonevents = [
{
'type': '$activation',
'email': 'user@example.com',
'details': {
'account': 'active',
'device': 'mobile'
}
}
]
bento.batch_create_events(events)Adding multiple tags to a subscriber
pythonsubscribers = [
{
'email': 'user@example.com',
'tags': ['Premium', 'Annual Plan', 'Early Adopter']
}
]
result = bento.batch_create_subscribers(subscribers)
print(f"Batch create result: {result}")Intermediate Guide
Custom fields and tags
Combine fields for rich profile data with namespaced tags for segmentation. Tags keep audiences organized (`plan:pro`, `status:vip`) while fields store free-form values like locale or favorite product.
Namespaced tag ideas
Apply via automations whenever possible so marketing and data teams stay in sync.
Create a new custom field definition
pythonfield = bento.create_field(key='membershipLevel')
print(f"New field: {field}")Get all existing fields
pythonfields = bento.get_fields()
print(f"Fields: {fields}")Create a new tag
pythontag = bento.create_tag(name='Power User')
print(f"New tag: {tag}")Get all existing tags
pythontags = bento.get_tags()
print(f"Tags: {tags}")Tracking purchase events
Always include a `unique` key (order ID, cart token, etc.) so Bento dedupes purchases. Capture cart contents and totals to power LTV reporting and Liquid personalization.
Track a purchase event to monitor customer lifetime value
pythonevents = [
{
'type': '$purchase',
'email': 'customer@example.com',
'details': {
'unique': {
'key': 'order-123' # Unique order identifier
},
'value': {
'amount': 9999, # Amount in cents
'currency': 'USD'
},
'cart': {
'items': [
{
'product_id': 'prod-456',
'product_name': 'Premium Widget',
'product_price': 9999,
'quantity': 1,
'product_sku': 'SKU-456'
}
]
}
}
}
]
bento.batch_create_events(events)Framework quickstarts
Reuse the same BentoAPI client across Flask, Django, or FastAPI routes so every request shares logging and credentials.
Flask
flask_support
pythonfrom flask import Flask, request
from bento_api import BentoAPI
app = Flask(__name__)
bento = BentoAPI(site_uuid="...", username="...", password="...")
@app.route('/signup', methods=['POST'])
def signup():
# Process signup
bento.batch_create_events([{
'type': '$subscribe',
'email': request.form['email'],
'fields': {'source': 'website'}
}])
return "Signup successful!"Django
django_support
python# In your views.py
from django.http import HttpResponse
from bento_api import BentoAPI
bento = BentoAPI(site_uuid="...", username="...", password="...")
def track_purchase(request):
# After processing order
bento.batch_create_events([{
'type': '$purchase',
'email': request.user.email,
'details': {'order_id': '123', 'amount': 9999}
}])
return HttpResponse("Order complete!")FastAPI
fast_api_support
pythonfrom fastapi import FastAPI, Depends
from bento_api import BentoAPI
app = FastAPI()
bento = BentoAPI(site_uuid="...", username="...", password="...")
@app.post("/events/")
async def create_event(event_data: dict):
result = bento.batch_create_events([event_data])
return {"status": "success", "result": result}Namespaced tags
Reserve tags for segmentation keys (e.g., `product:analytics`). Use fields when you need arbitrary values or timestamps. This keeps your marketing taxonomy predictable.
Advanced Guide
Batch operations
Batch endpoints cover more than 80% of API work. Keep payloads lean, batch 200–300 records, and retry only failed chunks.
Import multiple subscribers at once
pythonsubscribers = [
{
'email': 'user1@example.com',
'first_name': 'Alice',
'fields': {'company': 'Acme Inc'}
},
{
'email': 'user2@example.com',
'first_name': 'Bob',
'fields': {'company': 'Beta Corp'}
}
# ... up to 1,000 subscribers
]
result = bento.batch_create_subscribers(subscribers)
print(f"Batch create subscribers result: {result}")Import multiple events at once
pythonevents = [
{
'type': '$login',
'email': 'user@example.com',
'fields': {'date': '2023-01-01'}
},
{
'type': '$purchase',
'email': 'user@example.com',
'details': {
'unique': {'key': 'order-123'},
'value': {'currency': 'USD', 'amount': 9999}
}
}
# ... up to 1,000 events
]
bento.batch_create_events(events)Transactional emails
Great candidates
- Onboarding confirmation or welcome sequences
- Password reset and login verification links
- Payment, receipt, or fulfillment notices
- Account or compliance notifications
Avoid using transactional for
- CC / BCC workflows or multi-recipient fan-out
- Attachments (link to files instead)
- Marketing, promotional, or newsletter content
- Bulk announcements where unsubscribes must be honored
Send a transactional email
pythonemails = [
{
'to': 'recipient@example.com',
'from': {
'name': 'Your Name',
'email': 'sender@example.com'
},
'subject': 'Your order #123 has shipped!',
'html': '<h1>Your order has shipped!</h1><p>We're happy to let you know that your order is on its way.</p>',
'text': 'Your order has shipped! We're happy to let you know that your order is on its way.'
}
]
result = bento.batch_create_emails(emails)
print(f"Email send result: {result}")Subscriber updates
Events remain the preferred way to create and update subscribers—they trigger flows and can mutate tags or fields downstream. Use direct endpoints when workflows demand determinism.
Events-first creation
Create a subscriber when they sign up
pythonevents = [
{
'type': '$subscribe',
'email': 'new-user@example.com',
'fields': {
'firstName': 'Jane',
'lastName': 'Doe',
'signupSource': 'website'
}
}
]
bento.batch_create_events(events)Create a subscriber when they make a purchase
pythonevents = [
{
'type': '$purchase',
'email': 'customer@example.com',
'details': {
'unique': {'key': 'order-123'},
'value': {'amount': 9999, 'currency': 'USD'}
},
'fields': {
'firstName': 'John',
'lastName': 'Smith',
'customerType': 'new'
}
}
]
bento.batch_create_events(events)Direct creation options
Create a single subscriber (email only)
pythonsubscriber = bento.create_subscriber(email='user@example.com')
print(f"New subscriber: {subscriber}")Import multiple subscribers
pythonsubscribers = [
{
'email': 'user1@example.com',
'fields': {
'membershipTier': 'gold',
'accountStatus': 'active',
'lastRenewalDate': '2023-01-01T00:00:00'
}
},
{
'email': 'user2@example.com',
'fields': {
'membershipTier': 'silver',
'accountStatus': 'pending',
'trialEndsAt': '2023-02-01T00:00:00'
}
}
]
result = bento.batch_create_subscribers(subscribers)
print(f"Batch create subscribers result: {result}")Event-driven profile updates
Update subscriber when they update their profile
pythonevents = [
{
'type': '$subscription_change',
'email': 'user@example.com',
'fields': {
'subscriptionTier': 'premium'
}
}
]
bento.batch_create_events(events)Single attribute tweaks
Add or update a single field
pythonsubscribers = [
{
'email': 'user2@example.com',
'first_name': 'Jesse',
'last_name': 'Bento',
'tags': ['membership:premium'],
'fields': {
'membershipTier': 'premium',
'accountStatus': 'pending',
'trialEndsAt': '2023-02-01T00:00:00'
}
}
]
result = bento.batch_create_subscribers(subscribers)
print(f"Update result: {result}")Add a tag
pythonsubscribers = [
{
'email': 'user2@example.com',
'first_name': 'Jesse',
'last_name': 'Bento',
'tags': ['example:tag']
}
]
result = bento.batch_create_subscribers(subscribers)
print(f"Add tag result: {result}")Remove a field
pythoncommands = [
{
'command': 'remove_field',
'email': 'user@example.com',
'query': 'temporaryStatus'
}
]
result = bento.execute_commands(commands)
print(f"Remove field result: {result}")Batch updates
Update multiple subscribers
pythonsubscribers = [
{
'email': 'user1@example.com',
'fields': {
'membershipTier': 'gold',
'accountStatus': 'active',
'lastRenewalDate': '2023-01-01T00:00:00'
}
},
{
'email': 'user2@example.com',
'fields': {
'membershipTier': 'silver',
'accountStatus': 'pending',
'trialEndsAt': '2023-02-01T00:00:00'
}
}
]
result = bento.batch_create_subscribers(subscribers)
print(f"Batch update result: {result}")Specialized operations
Change a subscribers email address
pythoncommands = [
{
'command': 'change_email',
'email': 'old@example.com',
'query': 'new@example.com'
}
]
result = bento.execute_commands(commands)
print(f"Change email result: {result}")Update all fields at once
pythonsubscribers = [
{
'email': 'user@example.com',
'first_name': 'Updated',
'last_name': 'Name',
'fields': {
'address': {
'street': '123 Main St',
'city': 'New York',
'state': 'NY',
'zip': '10001'
},
'preferences': {
'theme': 'dark',
'notifications': True
}
}
}
]
result = bento.batch_create_subscribers(subscribers)
print(f"Update all fields result: {result}")Unsubscribe a user
pythoncommands = [
{
'command': 'unsubscribe',
'email': 'user@example.com',
'query': None
}
]
result = bento.execute_commands(commands)
print(f"Unsubscribe result: {result}")Utility features
Validate addresses, guess gender, geolocate IPs, or check blacklists without integrating third-party APIs. These helpers are rate-limited—cache responses when you can.
Validate Email
pythonvalidation_result = bento.validate_email(
email='user@example.com',
name=None,
user_agent='Mozilla/5.0...',
ip='192.168.1.1'
)
print(f"Email validation result: {validation_result}")Guess Prediction (for personalization)
pythongender_info = bento.guess_gender(name='Alex')
print(f"Gender prediction result: {gender_info}")Geolocate IP Address
pythonlocation = bento.geolocate_ip(ip='208.67.222.222')
print(f"IP geolocation result: {location}")Check Domain/IP blacklist
pythondomain_blacklist_info = bento.check_blacklist(domain='example.com')
print(f"Domain blacklist check result: {domain_blacklist_info}")
# Or check an IP instead
ip_blacklist_info = bento.check_blacklist(ip='192.168.1.1')
print(f"IP blacklist check result: {ip_blacklist_info}")API Reference
The Python SDK mirrors Bento's REST resources—most services expose async methods for batch operations plus convenience helpers for validation.
Core operations
-
Create subscriber
bento.create_subscriber(email="user@example.com")
Create subscriber -
Run subscriber commands
bento.execute_commands(commands)
Subscriber command -
Track events
bento.batch_create_events(events)
Events API
Batch helpers
-
Import subscribers
bento.batch_create_subscribers(subscribers)
Import subscribers -
Send transactional email
bento.batch_create_emails(emails)
Emails API -
Update tags or fields
bento.execute_commands([{ 'command': 'add_tag', ... }])
Subscriber command
Utility + experimental
-
Validate email syntax/domain
bento.validate_email(email=email)
Validate email -
Guess gender
bento.guess_gender(name)
Guess gender -
Check blacklist status
bento.check_blacklist(domain="example.com")
Blacklist search
Troubleshooting
Not Authorized
Verify publishable + secret keys and ensure the team member still has access.
Rate Limited
Implement exponential backoff for batch operations and honor Retry-After headers.
Network Errors
Confirm outbound traffic can reach app.bentonow.com and retry transient socket resets.
Payload Exceptions
Double-check Author emails and ensure payloads match the documented schema.
Debugging tips
- Store Site UUID and API keys in environment variables or a secrets manager, then inject them when instantiating BentoAPI.
- Catch `BentoAPIError` (or generic exceptions) to log response payloads and retry transient errors with libraries like `tenacity`.
- Break bulk imports into 200–300 record batches and dispatch them through Celery/RQ workers so retries stay isolated.
FAQ
Can I use this SDK in a frontend environment?
No. The Python SDK requires your publishable key, secret key, and Site UUID, so it must stay on the server. Use the JavaScript SDK or proxy calls through your backend for browser tracking.
How do I handle rate limiting?
Wrap calls with a retry policy (e.g., `tenacity`) that honors `Retry-After` headers so hot jobs back off automatically instead of failing.
rate_limiting
pythonfrom tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10))
def perform_bento_api_call():
# Your Bento API calls here
bento.batch_create_subscribers(subscribers)What's the maximum batch size for importing subscribers or events?
Up to 1,000 subscribers or events per payload are supported, but 200–300 keeps payloads fast to retry. Use queues like Celery for large backfills.
max_batch_size
python# Split large subscriber lists into smaller chunks
def chunk_list(data, chunk_size=300):
for i in range(0, len(data), chunk_size):
yield data[i:i + chunk_size]
# Process each chunk as a separate task
for chunk in chunk_list(all_subscribers, 300):
process_subscriber_chunk.delay(chunk)
# In your Celery task
@app.task
def process_subscriber_chunk(subscribers):
bento.batch_create_subscribers(subscribers)How do I track anonymous users?
Every event currently needs an email address. Capture emails early (waitlists, checkout, gated content) and hydrate events once the email is known.
Which Python versions are supported?
Python 3.6+ works, though we recommend 3.10 or newer for better async performance and TLS defaults. The SDK depends on `requests` under the hood.
How can I contribute to the SDK?
Open issues or pull requests on github.com/bentonow/bento-python-sdk and share feedback in the Bento community Discord.
Can I use the SDK with AWS Lambda or other serverless environments?
Yes. Instantiate BentoAPI outside the handler so sockets are reused, and keep payloads lean to avoid cold-start penalties.
serverless_support
python# Initialize the client outside the handler function
bento = BentoAPI(
site_uuid=os.environ['BENTO_SITE_UUID'],
username=os.environ['BENTO_PUBLISHABLE_KEY'],
password=os.environ['BENTO_SECRET_KEY']
)
def lambda_handler(event, context):
# Use the existing client for better performance
result = bento.batch_create_events(event['events'])
return {"statusCode": 200, "body": json.dumps(result)}How does the Python SDK integrate with common web frameworks?
Initialize BentoAPI once (app factory, ASGI lifespan, etc.) and reuse it inside Flask blueprints, Django views, or FastAPI routers—see the framework quickstarts below for drop-in snippets.
Contribute or debug further
The Python SDK is open source. Report bugs, request features, or open pull requests at https://github.com/bentonow/bento-python-sdk. Keep your local tooling on Python 3.8+ (requests-based client) to match production builds.
Need the original Markdown? Open raw file