Klipfolio is a SaaS platform for business dashboards, popular among SMB teams as an alternative to Tableau and Power BI. Klipfolio supports a Push API: you send data yourself, and the platform stores and visualizes it. There is no native integration with Kommo - we build a Python ETL script with hourly updates.
Architecture: Push datasource
Klipfolio works in two ways: Pull (Klipfolio fetches data on a schedule) and Push (you send data via API). For Kommo we use Push - Kommo has no public URL that Klipfolio could poll without authorization.
Scheme:
- Cron every hour -> Python script
- Script fetches data from Kommo API
- Transforms it into the required format
- Sends to Klipfolio Push datasource
Klipfolio authentication
Klipfolio API uses an API Key in the apikey header:
import requests
KLIPFOLIO_API_KEY = "your_klipfolio_api_key"
kf_session = requests.Session()
kf_session.headers.update({
"apikey": KLIPFOLIO_API_KEY,
"Content-Type": "application/json",
})
KF_BASE = "https://app.klipfolio.com/api/1.0"
API Key is available in Klipfolio Account -> Profile -> API Keys.
Creating a Push datasource
Created once. The datasource ID is used on every data push.
def create_push_datasource(name: str, columns: list) -> str:
"""Create Klipfolio push datasource. Returns datasource ID."""
payload = {
"data": {
"name": name,
"type": "push",
"data_format": "json",
}
}
r = kf_session.post(f"{KF_BASE}/datasources", json=payload)
r.raise_for_status()
return r.json()["payload"]["id"]
# Example: datasource for daily pipeline
DATASOURCE_ID = create_push_datasource(
name="Kommo Daily Pipeline",
columns=["date", "stage_name", "deals_count", "total_value", "avg_deal_value"],
)
print(f"Datasource ID: {DATASOURCE_ID}") # save to config
Extracting data from Kommo
from datetime import datetime, timedelta
KOMMO_BASE = "https://YOUR.kommo.com/api/v4"
KOMMO_TOKEN = "your_bearer_token"
kommo_session = requests.Session()
kommo_session.headers.update({"Authorization": f"Bearer {KOMMO_TOKEN}"})
def get_pipeline_snapshot() -> list:
"""Get current deals by stage from Kommo."""
r = kommo_session.get(
f"{KOMMO_BASE}/leads",
params={"limit": 250, "page": 1},
)
r.raise_for_status()
deals = r.json().get("_embedded", {}).get("leads", [])
# Group by status
stage_data = {}
for deal in deals:
stage_id = deal.get("status_id")
stage_name = get_stage_name(stage_id) # cache from /pipelines
value = deal.get("price", 0)
if stage_name not in stage_data:
stage_data[stage_name] = {"count": 0, "total_value": 0}
stage_data[stage_name]["count"] += 1
stage_data[stage_name]["total_value"] += value
today = datetime.now().strftime("%Y-%m-%d")
rows = []
for stage_name, data in stage_data.items():
count = data["count"]
total = data["total_value"]
rows.append({
"date": today,
"stage_name": stage_name,
"deals_count": count,
"total_value": total,
"avg_deal_value": round(total / count, 2) if count else 0,
})
return rows
Pushing data to Klipfolio
def push_to_klipfolio(datasource_id: str, rows: list) -> bool:
"""Push data rows to Klipfolio datasource."""
if not rows:
return False
# Klipfolio Push API accepts a JSON array of strings or a JSON array of objects
payload = {"data": rows}
r = kf_session.post(
f"{KF_BASE}/datasources/{datasource_id}/data",
json=payload,
)
if r.status_code not in (200, 201, 204):
print(f"Klipfolio push failed: {r.status_code} {r.text}")
return False
return True
# Main ETL
def run_etl():
print(f"ETL started at {datetime.now().isoformat()}")
rows = get_pipeline_snapshot()
success = push_to_klipfolio(DATASOURCE_ID, rows)
print(f"Pushed {len(rows)} rows: {'OK' if success else 'FAILED'}")
if __name__ == "__main__":
run_etl()
Run via cron: 0 * * * * python /path/to/kommo_klipfolio_etl.py
Advanced metrics: stage conversion rates
def get_conversion_metrics(days: int = 30) -> list:
"""Get conversion rates between stages for last N days."""
since = int((datetime.now() - timedelta(days=days)).timestamp())
# Won deals for the period
r_won = kommo_session.get(
f"{KOMMO_BASE}/leads",
params={"filter[closed_at][from]": since, "filter[statuses][0]": 142}, # 142 = Won
)
won_count = len(r_won.json().get("_embedded", {}).get("leads", []))
# All deals created in the period
r_total = kommo_session.get(
f"{KOMMO_BASE}/leads",
params={"filter[created_at][from]": since},
)
total_count = len(r_total.json().get("_embedded", {}).get("leads", []))
conversion = round(won_count / total_count * 100, 1) if total_count else 0
return [{
"date": datetime.now().strftime("%Y-%m-%d"),
"period_days": days,
"total_deals": total_count,
"won_deals": won_count,
"conversion_rate": conversion,
}]
Sent to a separate datasource Kommo Conversion.
Date formatting for Klipfolio
Important detail: Klipfolio interprets date format strictly. Accepted formats:
YYYY-MM-DD- safe format for datesYYYY-MM-DD HH:MM:SS- for timestamps- Unix timestamp (integer) - also works
Send numbers as numbers (not strings): "total_value": 45000, not "total_value": "45000". Klipfolio applies numeric aggregation functions only to numeric fields.
Real case
A SaaS company with a 5-person sales team was doing manual exports from Kommo once a week into Google Sheets, then building charts by hand. The manager received pipeline status with a 3-7 day delay.
After the Kommo -> Klipfolio ETL:
- Dashboard updates every hour
- TV screen in the office shows current pipeline value and conversion rate
- Manager sees the full picture in real time without asking the team
ETL setup took 2 business days, including dashboard design in Klipfolio.
Who this is for
Sales teams of 3-15 people who need a simple live dashboard without heavy BI infrastructure. Klipfolio is cheaper and simpler than Tableau but more powerful than Google Data Studio for Klip-based visualization.
For more powerful BI solutions - Kommo + Metabase (self-hosted, SQL), Kommo + Tableau (enterprise), Kommo + Grafana (open-source).
Frequently asked questions
How does Klipfolio differ from Tableau or Power BI?
Klipfolio is simpler to set up and cheaper ($99-299/month vs $70+/user). Visualizations are called “Klips” - each Klip connects to a separate datasource. There is no SQL editor like in Metabase. Optimal for predefined KPI metrics, not for ad-hoc analysis.
How to update historical data in a datasource?
Klipfolio Push API replaces data by default on every push (replace mode). For append (adding rows), use "mode": "append" in the POST request parameters. For storing history, append mode with a date field is recommended - this allows Klipfolio to show trends.
How many datasources can be created?
Depends on the pricing plan. On the starter plan - up to 20 datasources. For a Kommo integration, 3-5 are typically enough: pipeline by stage, conversion, average time in deal, revenue by manager.
Are there ready-made Klip templates for CRM data?
Klipfolio Gallery contains ready-made Klip templates for sales metrics. When creating a new Klip, choose Gallery -> Sales -> “Sales Pipeline” or “Conversion Funnel” and connect it to your datasource. Field configuration takes 15-20 minutes.
Summary
Kommo -> Klipfolio ETL is the simplest BI solution for small sales teams:
- Klipfolio API Key in the
apikeyheader - Push datasource: created once, updated every hour
- Data sent as a JSON array of objects
- Numbers as numbers, dates in
YYYY-MM-DDformat - otherwise aggregation won’t work
If your sales team needs a live dashboard and has no resources for BI infrastructure - reach out to Exceltic.dev. We’ll set up the ETL and dashboard for your metrics.