Meta Ads API 입문: 클릭과 전환을 구분해 첫 보고서 만들기
Meta 광고 데이터를 조회하기 전에 클릭 종류와 계정 통화, 전환 배열을 이해합니다. 가상 데이터로 계산을 연습하고 Python 조회 결과를 광고 관리자와 대조하는 순서를 설명합니다.

목차
세 줄로 먼저 읽기
이번 방문에서 한 편은 바로 볼 수 있습니다.
Meta Marketing API는 프로그램이 Facebook과 Instagram 등의 광고 정보를 요청하고 관리하는 연결 방식입니다. 성과 보고를 조회하는 부분을 Insights API라고 부릅니다.
한 번 보고서를 내려받는 일이라면 광고 관리자 내보내기로 충분할 수 있습니다. 계정 여러 개의 자료를 반복해서 합치거나 정기적으로 갱신할 때 API를 검토합니다. 이 글의 첫 목표는 계정 하나의 캠페인 성과를 조회하고 비용과 전환의 뜻이 맞는지 확인하는 것입니다.
먼저 보고서 조건을 적습니다
| 항목 | 처음 정할 내용 |
|---|---|
| 계정 | 조회 권한이 있는 광고 계정 ID |
| 기간 | 예: 2026-09-21부터 2026-09-27까지 |
| 행 단위 | 캠페인 한 개의 기간 합계 |
| 기본 지표 | 노출, 링크 클릭, 비용 |
| 전환 | 웹 구매 등 비교할 유형 한 가지 |
| 확인 대상 | 같은 조건의 광고 관리자 보고서 |
ID는 이름과 별개인 식별 번호입니다. 광고 계정 조회 주소에는 보통 act_가 붙은 계정 ID를 사용합니다. 계정 연결이 없다면 아래 가상 데이터와 계산 코드부터 실행할 수 있습니다.
조회 데이터는 세 종류로 구분합니다
| 종류 | 예시 | 주의 |
|---|---|---|
| 구조 | 캠페인 이름과 상태, 예산 | 성과와 별도 필드 또는 요청이 필요할 수 있음 |
| 성과 | 노출, 클릭, 비용, 전환 | 지표 정의와 기간을 맞춰야 함 |
| 세분화 | 연령, 성별, 게재 위치 | 지원되는 필드 조합만 사용 |
필드는 가져올 항목 이름입니다. Breakdown은 성과를 나누는 기준이며 추가하면 한 캠페인이 여러 행으로 나뉠 수 있습니다. Insights API 개요
계정 접근을 준비합니다
직접 API를 이용하려면 Meta 개발자 앱과 대상 광고 계정에 접근할 인증을 준비합니다. 앱은 API 사용과 권한을 관리하는 단위입니다.
위는 기존 앱 생성 화면입니다. 현재 대시보드의 이용 사례와 요구사항을 확인하고 Marketing API 사용 목적에 맞게 설정합니다. 화면 이름이 다르다고 임의의 다른 제품을 추가하지 않습니다.
| 준비 | 확인 |
|---|---|
| 앱 | Marketing API 이용에 필요한 설정 |
| 권한 | 보고서 읽기에는 ads_read 등 해당 요청에 필요한 권한 |
| 자산 접근 | 인증한 사용자 또는 시스템 사용자에게 대상 광고 계정이 할당됐는지 |
| 접근 수준 | 앱의 사용 대상에 필요한 검수와 비즈니스 인증 여부 |
| API 버전 | 코드와 SDK에서 사용할 지원 버전 |
자체 계정 조회와 다른 사업자의 계정을 위한 앱은 요구사항이 다릅니다. 권한별 Standard/Advanced 접근과 호출량 등에 관한 Marketing API Access Tier도 별개입니다. 현재 문서는 후자의 등급을 Limited/Full로 구분합니다. 용어가 비슷하다고 같은 설정으로 취급하지 말고 앱 대시보드에서 확인합니다. 승인까지 걸리는 시간을 고정하지 않습니다. 권한과 접근 안내
액세스 토큰은 비밀 인증 정보입니다
액세스 토큰은 API 요청을 허용하는 인증 정보입니다. 테스트용 사용자 토큰과 운영용 시스템 사용자 구성은 목적이 다릅니다. 시스템 사용자는 사람이 직접 로그인하는 계정 대신 프로그램에 자산 접근을 부여하는 구성입니다.
만료일 없는 토큰을 발급한 경우라도 권한 회수나 자산 변경, 토큰 무효화 등으로 사용할 수 없게 될 수 있습니다. ‘한 번 발급하면 영구 운영된다’고 보장하지 않습니다. 필요한 역할과 자산만 할당하고 만료 및 오류를 확인할 담당자를 정합니다. 모든 자동화에 관리자 역할을 일괄 부여하지 않습니다.
토큰 값은 코드나 URL, 화면 캡처에 넣어 공유하지 않습니다. 아래 예시는 환경 변수에서 읽습니다. 환경 변수는 실행할 프로그램에 전달하는 설정값입니다. 이미 노출된 토큰은 담당자가 폐기와 재발급 필요성을 확인해야 합니다. Meta 액세스 토큰 안내
클릭수와 웹사이트 방문은 같지 않습니다
| 목적 | 필드 또는 값 | 의미 |
|---|---|---|
| 광고의 전체 클릭 | clicks | 링크 외 다른 광고 상호작용 클릭도 포함 |
| 링크 클릭 | inline_link_clicks | Meta 안팎의 목적지로 가는 링크 클릭 |
| 외부 클릭 | outbound_clicks | Meta 소유 환경 밖으로 이동하는 링크 클릭 |
| 랜딩페이지 조회 | actions의 landing_page_view | 페이지 로딩에 관련된 조회 지표 |
링크 클릭 100회가 웹사이트가 100번 열렸다는 뜻은 아닙니다. 외부 클릭도 페이지 로딩 완료나 GA4 세션 수와 같지 않습니다. outbound_clicks는 배열 형태일 수 있으므로 전체 클릭 숫자처럼 바로 계산하지 않습니다. 다른 매체의 클릭과 비교할 때는 목적지와 정의를 함께 적습니다.
기본 지표와 단위를 맞춥니다
| 지표 | 필드 | 처리 |
|---|---|---|
| 노출 | impressions | 표시된 횟수 |
| 도달 | reach | 해당 범위에서 중복을 제거한 추정 지표 |
| 빈도 | frequency | 노출 ÷ 도달, 기간별 값을 단순 합산하지 않음 |
| 비용 | spend | 계정 통화 기준 금액 |
| 링크 클릭률 | inline_link_click_ctr | 링크 클릭 기준의 백분율 값 |
| 전체 클릭당 비용 | cpc | 전체 클릭 기준 |
| 노출 1,000회당 비용 | cpm | 비용 ÷ 노출 × 1,000 |
spend가 "15000"이고 통화가 KRW면 15,000원입니다. 통화가 USD라면 15,000달러입니다. Meta 비용을 Google의 cost_micros처럼 100만으로 나누지 않습니다. 도달은 날짜나 캠페인별 값을 더하면 같은 사람이 중복돼 전체 도달과 달라질 수 있습니다. 필드 정의
전환 배열에서 필요한 유형을 고릅니다
배열은 여러 항목을 묶은 목록입니다. actions에는 행동 수가, action_values에는 해당 행동의 가치가 들어갑니다. action_type으로 종류를 구분합니다.
{
"spend": "15000",
"actions": [
{"action_type": "link_click", "value": "30"},
{"action_type": "offsite_conversion.fb_pixel_purchase", "value": "3"}
],
"action_values": [
{"action_type": "offsite_conversion.fb_pixel_purchase", "value": "60000"}
]
}이것은 가상 응답입니다. 이 예시의 웹 구매는 3건이고 구매가치는 60,000입니다. 같은 통화로 기록한 매출이라는 조건이면 구매당 비용은 5,000, ROAS는 4배입니다.
웹 구매와 전체 구매, 앱 구매 등 유형이 중복 범위를 가질 수 있습니다. purchase, omni_purchase, offsite_conversion.fb_pixel_purchase처럼 이름이 비슷한 값을 모두 더하지 않습니다. 광고 관리자에서 비교할 열과 실제 응답을 대조해 유형을 하나 고릅니다. 배열에 원하는 항목이 없을 때도 자료 미제공과 실제 0을 구분해야 합니다. 행동 값의 계층과 세분화
광고 관리자의 ‘결과’는 설정한 성과 목표에 따라 다릅니다. 모든 판매 캠페인의 결과가 웹 구매라고 가정하지 않습니다.
계정 없이 먼저 계산해 봅니다
Python 3가 있는 환경에서 아래를 meta_parse_practice.py로 저장하고 python3 meta_parse_practice.py로 실행합니다. 외부 패키지와 토큰 없이 가상 자료만 처리합니다.
from decimal import Decimal
PURCHASE = "offsite_conversion.fb_pixel_purchase"
def action_value(items, action_type):
if items is None:
return None
matches = [x for x in items if x.get("action_type") == action_type]
if len(matches) > 1:
raise ValueError("같은 행동 유형이 여러 개입니다. 세분화 조건을 확인하세요.")
if not matches or matches[0].get("value") is None:
return None
return Decimal(str(matches[0]["value"]))
def ratio(numerator, denominator):
if numerator is None or denominator is None or denominator == 0:
return None
return numerator / denominator
def report_row(row):
spend = Decimal(str(row["spend"])) if row.get("spend") is not None else None
purchases = action_value(row.get("actions"), PURCHASE)
value = action_value(row.get("action_values"), PURCHASE)
return {
"campaign_id": row.get("campaign_id"),
"campaign_name": row.get("campaign_name"),
"currency": row.get("account_currency"),
"date_start": row.get("date_start"),
"date_stop": row.get("date_stop"),
"spend": spend,
"impressions": row.get("impressions"),
"link_clicks": row.get("inline_link_clicks"),
"purchases": purchases,
"purchase_value": value,
"purchase_cpa": ratio(spend, purchases),
"roas": ratio(value, spend),
}
if __name__ == "__main__":
sample = {
"campaign_id": "DEMO_1", "campaign_name": "가상 캠페인",
"account_currency": "KRW",
"date_start": "2026-09-21", "date_stop": "2026-09-27",
"spend": "15000", "impressions": "600", "inline_link_clicks": "30",
"actions": [{"action_type": PURCHASE, "value": "3"}],
"action_values": [{"action_type": PURCHASE, "value": "60000"}],
}
print(report_row(sample))결과의 purchase_cpa는 5000, roas는 4입니다. Decimal은 금액 계산에 쓰는 십진수 형식이고 None은 여기서 미확인 또는 계산 불가를 뜻합니다. 구매 0건이나 비용 0원 때문에 나눌 수 없는 값은 0으로 꾸미지 않습니다. 원시 값이 잘못된 문자열이면 오류를 내므로 해당 응답을 확인합니다. 가상 자료의 spend를 "0"으로 바꾸면 ROAS가 None인지, actions를 빈 목록으로 바꾸면 구매가 미확인으로 남는지 확인한 뒤 원래 값으로 되돌려 실행해 봅니다.
실제 조회 코드는 인증 준비 후 실행합니다
다음은 공식 Python SDK를 사용하는 조회 예시입니다. SDK는 API 호출을 돕는 코드 묶음입니다. 앞의 meta_parse_practice.py와 같은 폴더에 meta_report.py로 저장합니다.
사전에 Python 실행 환경에 facebook_business가 설치되어 있고, 환경 변수 META_APP_ID, META_APP_SECRET, META_ACCESS_TOKEN, META_AD_ACCOUNT_ID, META_API_VERSION이 준비돼야 합니다. 버전은 담당자가 현재 지원 여부와 SDK 호환성을 확인한 값을 사용합니다. META_AD_ACCOUNT_ID에는 act_를 포함합니다. 공식 Python SDK
import csv
import os
from pathlib import Path
from facebook_business.api import FacebookAdsApi
from facebook_business.adobjects.adaccount import AdAccount
from meta_parse_practice import report_row
FacebookAdsApi.init(
app_id=os.environ["META_APP_ID"],
app_secret=os.environ["META_APP_SECRET"],
access_token=os.environ["META_ACCESS_TOKEN"],
api_version=os.environ["META_API_VERSION"],
)
account = AdAccount(os.environ["META_AD_ACCOUNT_ID"])
fields = [
"campaign_id", "campaign_name", "account_currency",
"date_start", "date_stop", "impressions", "inline_link_clicks",
"spend", "actions", "action_values",
]
params = {
"level": "campaign",
"time_range": {"since": "2026-09-21", "until": "2026-09-27"},
"use_unified_attribution_setting": True,
"limit": 100,
}
rows = [report_row(dict(row)) for row in account.get_insights(fields=fields, params=params)]
if not rows:
print("조회 결과가 없습니다. 계정과 기간, 필터를 확인하세요.")
else:
output = Path("meta_report.csv")
with output.open("x", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=list(rows[0]))
writer.writeheader()
writer.writerows(rows)
print(f"{len(rows)}개 행 저장: {output.resolve()}")python3 meta_report.py로 실행하면 현재 폴더에 CSV를 만듭니다. CSV는 표의 행과 열을 저장하는 텍스트 파일입니다. 같은 파일이 있으면 덮어쓰지 않고 멈추므로 새 이름으로 바꾸거나 기존 보고서를 옮긴 뒤 실행합니다. SDK의 조회 결과를 순회하며 다음 페이지를 가져옵니다. limit=100이 전체 보고서를 100행으로 끝내는 조건은 아닙니다.
이 예시는 캠페인 기간 합계의 구매 보고서입니다. API 연결 성공과 특정 계정의 구매 유형 일치까지 보장하는 코드는 아닙니다. 날짜는 params의 time_range에서 바꿉니다. 첫 실행 뒤 통화와 기간, 캠페인 ID 및 선택한 구매 유형을 광고 관리자와 대조합니다. CSV에서는 None이 빈칸으로 저장되며 0과 구분합니다. 노출과 링크 클릭 원시값도 문자로 받을 수 있으므로 추가 계산 전 숫자 형식을 확인합니다.
이 코드는 계정 하나와 짧은 기간을 확인하는 예시입니다. 많은 계정을 운영할 때는 요청 한도, 재시도와 중복 저장 방지, 큰 보고서의 비동기 처리와 실패 알림을 추가해야 합니다. 비동기 처리는 요청 즉시 전체 결과를 받는 대신 보고서 생성이 끝난 뒤 결과를 가져오는 방식입니다.
기여 기간과 보고 날짜를 구분합니다
기여 기간은 광고 상호작용 후 성과를 인정하는 기간입니다. 위의 use_unified_attribution_setting=True는 광고 세트 수준의 기여 설정을 사용하도록 요청합니다. 별도 기간 비교가 목적이면 지원되는 action_attribution_windows를 확인해 그 조건으로 조회하고 보고서에도 표시합니다. 모든 계정을 무조건 7일 클릭과 1일 조회로 바꾸지 않습니다.
action_report_time은 성과를 어느 날짜에 보고할지 정하는 매개변수입니다. 노출 기준과 전환 발생일 기준이 다를 수 있으므로 광고 관리자와 비교할 때 이 조건도 맞춥니다. 1d_click과 7d_click은 기간이 겹치므로 값을 합해 전체 전환을 만들지 않습니다. 기여와 날짜 매개변수
세분화는 하나씩 추가합니다
| 목적 | 예시 |
|---|---|
| 연령과 성별 | age, gender |
| 플랫폼과 게재 위치 | publisher_platform, platform_position |
| 기기 종류 | 필요한 수준에 맞는 device_platform 또는 impression_device 확인 |
모든 조합이 허용되지는 않고 조합별로 전환 지표가 제한될 수 있습니다. 단순 곱셈으로 항상 72행이 나온다고 예상하지 않습니다. 행이 없거나 지표가 0이면 해당 조합이 지원되는지부터 확인합니다. 도달과 고유 사용자 지표는 세분화 값이나 날짜별로 더해 전체값을 만들지 않습니다.
과거 자료의 조회 한도와 지원 버전은 지표 및 요청 조건에 따라 다릅니다. 오래된 글의 일괄 ‘13개월’이나 ‘연 2회 변경’을 운영 규칙으로 쓰지 말고 제한과 권장사항, 현재 변경 안내를 확인합니다.
오류가 나면 어디부터 볼까요?
| 증상 | 다음 확인 |
|---|---|
ModuleNotFoundError | 실행 중인 Python 환경에 SDK가 있는지 |
환경 변수 이름의 KeyError | 필요한 설정이 프로그램에 전달됐는지, 값은 출력하지 않음 |
| 인증 오류 | 토큰 유효 상태와 필요한 권한 |
| 권한 오류 | 앱 접근 수준과 대상 광고 계정 자산 할당 |
| 결과 없음 | 계정, 기간과 필터, 실제 광고 활동 |
| 구매만 비어 있음 | 응답의 행동 유형과 기여 조건, 자료 미제공 여부 |
| 기존 CSV 오류 | 출력 파일 이름 또는 저장 위치 변경 |
AI에게는 비밀값을 지운 오류 코드와 단계, 사용 버전과 요청 필드를 전달합니다. 전체 인증 파일을 붙여넣지 않습니다. 합계가 다를 때는 계정과 통화, 기간과 시간대, 행 단위, 전환 정의 순으로 비교합니다.
혼자 다시 확인하기
가상 USD 계정에서 비용이 "100", 링크 클릭이 20, 외부 클릭이 12이고 구매 항목은 응답에 없습니다. ‘100원으로 20명이 방문했고 구매 0건’이라고 적어도 될까요?
아닙니다. 비용은 100달러이고 클릭은 방문 완료나 사람 수가 아닙니다. 구매 항목 누락도 이 예제에서는 미확인으로 남깁니다. 자료의 단위와 의미가 맞는지 확인한 뒤 다른 매체와 합칩니다. 합칠 때는 매체, 계정 ID와 캠페인 ID, 날짜를 함께 보관합니다.
3줄 요약
-
첫 조회는 계정 하나와 기간, 행 단위를 정하고 광고 관리자와 같은 조건으로 비교합니다.
-
링크 클릭과 외부 클릭, 실제 페이지 조회를 구분하고 비용에는 계정 통화를 표시합니다.
-
전환은 필요한 action_type을 선택하고 누락값과 중복 집계, 기여 기준을 확인합니다.

제대로 이해했는지 한 문제로 확인해 볼까요?
답을 고르면 바로 풀이가 나와요.
Meta API의 inline_link_clicks가 100이면 외부 웹사이트가 100번 열렸다는 뜻인가요?
이 글이 도움이 되었나요?
메일, 회의록, 리포트처럼 반복되는 일을 AI로 자동화하는 순서와 안전장치를 다룹니다
코스 전체 보기 →
새 글과 AI 소식을 메일로 받아 보세요
AI가 바꾸는 일과 도구, 측정 실무 이야기를 매주 한 번 보내 드려요.
