Python sample
A minimal, copy-pasteable Python client for the BottleCRM API. Uses only the standard library plus requests.
Setup
pip install requests
A tiny client
import os
import requests
BASE = os.environ["BOTTLECRM_URL"].rstrip("/") # e.g. https://crm.acme.com
TOKEN = os.environ["BOTTLECRM_TOKEN"] # JWT access token
session = requests.Session()
session.headers.update({
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
})
def list_leads(**filters):
"""Yield every lead matching the filters, paginating automatically."""
url = f"{BASE}/api/leads/"
params = filters
while url:
r = session.get(url, params=params)
r.raise_for_status()
body = r.json()
yield from body["results"]
url = body["next"]
params = None # next URL already has the params encoded
def create_lead(**fields):
r = session.post(f"{BASE}/api/leads/", json=fields)
r.raise_for_status()
return r.json()
def update_lead(lead_id, **fields):
r = session.patch(f"{BASE}/api/leads/{lead_id}/", json=fields)
r.raise_for_status()
return r.json()
Example: sync website signups into BottleCRM
def on_signup(form_data):
create_lead(
title=f"Website signup: {form_data['company']}",
first_name=form_data["first_name"],
last_name=form_data["last_name"],
email=form_data["email"],
source="website_form",
status="new",
custom_fields={
"industry": form_data.get("industry"),
"company_size": form_data.get("company_size"),
},
)
Example: weekly hot-leads report
from datetime import datetime, timedelta, timezone
since = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()
hot = list(list_leads(
status="qualified",
cf_bant_score__gte=70,
created_at__gte=since,
))
print(f"{len(hot)} qualified leads in the last 7 days")
for lead in hot[:20]:
print(f" {lead['title']:40s} {lead['email']}")
Refreshing the token
Once TOKEN expires you'll get a 401. Wrap the calls in a small retry helper:
def refresh():
r = requests.post(
f"{BASE}/api/auth/refresh/",
json={"refresh": os.environ["BOTTLECRM_REFRESH_TOKEN"]},
)
r.raise_for_status()
return r.json()["access"]
In a long-running process, refresh proactively a minute before expiry rather than waiting for the 401.