get_performance_report
Retrieve period-specific performance reports for Kakao Moment ads, including metrics like impressions, clicks, cost, and ROAS. Supports breakdown by day, hour, device, or placement for detailed analysis.
Instructions
기간별 성과 리포트(노출·클릭·비용·전환·CTR·CPC·ROAS).
이런 질문에 사용하세요 (예시 → 인자): • "어제 성과 요약해줘" → target="account", date_from/date_to=어제 • "어제 캠페인별 성과" → target="campaign", date_from/date_to=어제 • "지난주 광고그룹별 성과" → target="adgroup", date_from/date_to=지난주 월~일 • "최근 7일 ROAS 좋은 광고그룹" → target="adgroup", 최근 7일 • "캠페인 X 의 어제 성과" → target="campaign", target_id="X", 어제 • "어제 클릭률 낮은 소재" → target="creative", date_from/date_to=어제 • "지난주 대비 이번주 성과 변화" → 두 번 호출 후 비교 • "오늘 시간대별 페이스" → 단발이면 get_today_status 가 더 적합. • "디바이스별 / 지면별 성과" → breakdown="device" / "placement"
Args: target: account | campaign | adgroup | creative date_from: YYYY-MM-DD (오늘 = today.isoformat()) date_to: YYYY-MM-DD target_id: campaign/adgroup/creative 일 때 특정 ID (선택). 전체 대상이면 비움. breakdown: day | hour | device | placement (선택)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| target | Yes | ||
| date_from | Yes | ||
| date_to | Yes | ||
| target_id | No | ||
| breakdown | No |
Implementation Reference
- Core handler function `get_performance_report` - fetches performance report data from Kakao Moment API, normalizes rows, computes derived metrics (CTR, CPC, ROAS), calculates totals, and returns a structured report with summary text.
async def get_performance_report( client: KakaoMomentClient, target: Target, date_from: str, date_to: str, target_id: str | None = None, breakdown: Breakdown | None = None, ) -> dict[str, Any]: """성과 리포트. Args: target: account | campaign | adgroup | creative date_from: YYYY-MM-DD date_to: YYYY-MM-DD target_id: campaign/adgroup/creative 일 때 특정 ID 필터 (선택) breakdown: day | hour | device | placement (선택) """ if target not in _TARGET_TO_PATH: raise ValueError( f"target 은 account|campaign|adgroup|creative 중 하나여야 합니다. (입력: {target})" ) df = validate_date(date_from, field="date_from") dt = validate_date(date_to, field="date_to") if dt < df: raise ValueError("date_to 가 date_from 보다 앞설 수 없습니다.") params: dict[str, Any] = { "dateFrom": to_yyyymmdd(df), "dateTo": to_yyyymmdd(dt), } if breakdown: # 카카오 모먼트는 `metricsGroups` (복수형) 파라미터를 사용한다. # 단수 `metricsGroup` 은 400 (detailCode 90001) 을 반환한다. params["metricsGroups"] = breakdown.upper() if target_id and target in _TARGET_TO_ID_PARAM: params[_TARGET_TO_ID_PARAM[target]] = target_id # None 값 제거 params = {k: v for k, v in params.items() if v is not None} path = _TARGET_TO_PATH[target] data = await client.get(path, params=params) rows = data.get("data") if isinstance(data, dict) and "data" in data else data def normalize(r: dict[str, Any]) -> dict[str, Any]: flat = _unwrap_metrics(r) return { "date": flat.get("start") or flat.get("date") or flat.get("hour"), "dimension_id": flat.get("dimensionId") or flat.get("id"), "dimension_name": flat.get("name"), **_compute_derived(r), } if isinstance(rows, list): normalized = [normalize(r) for r in rows] elif isinstance(rows, dict): normalized = [normalize(rows)] else: normalized = [] # 합계 요약 totals = { "impressions": sum(r["impressions"] for r in normalized), "clicks": sum(r["clicks"] for r in normalized), "cost": round(sum(r["cost"] for r in normalized), 2), "conversions": sum(r["conversions"] for r in normalized), "revenue": round(sum(r["revenue"] for r in normalized), 2), } total_cost = totals["cost"] total_clicks = totals["clicks"] total_imp = totals["impressions"] totals["ctr_pct"] = round((total_clicks / total_imp * 100) if total_imp else 0.0, 2) totals["cpc"] = round((total_cost / total_clicks) if total_clicks else 0.0, 2) totals["roas_pct"] = round( (totals["revenue"] / total_cost * 100) if total_cost else 0.0, 2 ) summary = ( f"{target} 리포트 {date_from} ~ {date_to}" + (f" (그룹: {breakdown})" if breakdown else "") + f": 노출 {totals['impressions']:,} · 클릭 {totals['clicks']:,}" f" · 비용 {int(totals['cost']):,}원 · CTR {totals['ctr_pct']}% " f"· CPC {int(totals['cpc']):,}원 · ROAS {totals['roas_pct']}%" ) return { "target": target, "date_from": date_from, "date_to": date_to, "breakdown": breakdown, "totals": totals, "rows": normalized, "summary": summary, } - Type aliases `Target` (account|campaign|adgroup|creative) and `Breakdown` (day|hour|device|placement) used as input validation.
Target = Literal["account", "campaign", "adgroup", "creative"] Breakdown = Literal["day", "hour", "device", "placement"] - Helper function `_compute_derived` - computes CTR, CPC, ROAS from raw metrics and returns a flat dict of derived values.
def _compute_derived(row: dict[str, Any]) -> dict[str, Any]: row = _unwrap_metrics(row) imp = float(row.get("imp", 0) or 0) click = float(row.get("click", 0) or 0) cost = float(row.get("cost", 0) or 0) conv = float(row.get("conv", 0) or row.get("conversion", 0) or 0) revenue = float(row.get("conversionAmount", 0) or row.get("revenue", 0) or 0) ctr = (click / imp * 100) if imp else 0.0 cpc = (cost / click) if click else 0.0 roas = (revenue / cost * 100) if cost else 0.0 return { "impressions": int(imp), "clicks": int(click), "cost": cost, "conversions": conv, "revenue": revenue, "ctr_pct": round(ctr, 2), "cpc": round(cpc, 2), "roas_pct": round(roas, 2), } - Helper function `_unwrap_metrics` - normalizes nested API response format (new-style with metrics/dimensions dicts) into flat dict for uniform handling.
def _unwrap_metrics(row: dict[str, Any]) -> dict[str, Any]: """카카오 리포트는 응답 포맷이 두 가지. - 신형: {start, end, dimensions: {...}, metrics: {imp, click, cost, ...}} - 구형: {start, imp, click, cost, ...} (flat) 어느 쪽이든 flat dict 처럼 다루도록 변환. """ metrics = row.get("metrics") if isinstance(metrics, dict): merged = {**row, **metrics} dims = row.get("dimensions") if isinstance(dims, dict): merged.update(dims) return merged return row