교육 문의커뮤니티 입장하기

비개발자를 위한 네이버 검색광고 API: 하루 성과 조회와 단위 확인

가상 데이터로 캠페인 이름과 통계를 연결한 뒤 네이버 검색광고 API로 캠페인 하나의 하루 성과를 조회합니다. 인증 서명, 계정 ID, 일별 응답과 비용 및 ROAS 단위를 차례로 확인합니다.

지금까지 500명 넘게 읽었어요, 35%가 끝까지 읽었어요
Share
비개발자를 위한 네이버 검색광고 API: 하루 성과 조회와 단위 확인 대표 이미지
목차
  1. 이름 정보와 성과 정보를 연결합니다
  2. 실제 조회 전에 준비할 것
  3. HMAC 서명은 무엇인가요?
  4. 단일 ID 조회와 여러 ID 조회를 구분합니다
  5. 실제 조회 코드: 캠페인 하나와 날짜 하루
  6. 비용과 ROAS를 합치기 전에 확인합니다
  7. 기간을 늘릴 때와 오류가 날 때
  8. AI에게 구현을 요청한다면
  9. 3줄 요약

세 줄로 먼저 읽기

이번 방문에서 한 편은 바로 볼 수 있습니다.

네이버 광고 보고서를 자동으로 만들고 싶다면 캠페인 하나의 하루 비용을 조회해 광고 관리자와 맞추는 것부터 시작합니다. 처음부터 모든 계정과 매체를 합치면 어떤 단계에서 숫자가 달라졌는지 찾기 어렵습니다.

API는 프로그램이 광고 시스템에 자료를 요청하는 기능입니다. 여기서는 광고 설정을 바꾸지 않고 조회한 정보를 CSV, 즉 표 형태의 텍스트 파일로 저장합니다. 계정 연결이 없다면 먼저 가상 자료 연습을 할 수 있습니다.

이름 정보와 성과 정보를 연결합니다

캠페인 목록에는 ID와 이름 같은 기본 정보가 있고, 통계에는 노출과 클릭, 비용이 있습니다. ID를 기준으로 두 정보를 연결하는 작업을 조인이라고 합니다. 이름은 바뀌거나 중복될 수 있어 연결 기준으로 적합하지 않습니다.

자료조회 경로먼저 확인할 필드
캠페인 기본 정보/ncc/campaigns 또는 특정 캠페인 경로nccCampaignId, name
성과 통계/statsimpCnt, clkCnt, salesAmt

name이 캠페인 이름입니다. 응답에 없는 campaignName을 읽으려 하면 이름을 가져오지 못합니다. 통계 응답도 최상위 자료 자체가 행 목록이라고 가정하지 않고 data 안의 행을 확인합니다. 공식 API 문서

계정 없이 연결과 계산을 연습합니다

Python 3가 있는 환경에서 아래 내용을 naver_practice.py로 저장하고 python3 naver_practice.py로 실행합니다. 외부 패키지와 실제 인증값은 필요하지 않습니다.

from decimal import Decimal campaigns = [{"nccCampaignId": "DEMO_1", "name": "가상 캠페인"}] stats = {"data": [{"id": "DEMO_1", "impCnt": 1000, "clkCnt": 20, "salesAmt": 10000, "ror": 350}]} names = {c["nccCampaignId"]: c["name"] for c in campaigns} for row in stats["data"]: cid = row["id"] roas = None if row.get("ror") is None else Decimal(str(row["ror"])) / 100 print(cid, names.get(cid, "이름 미확인"), row.get("salesAmt"), roas)

정상 출력은 DEMO_1 가상 캠페인 10000 3.5입니다. 이 자료는 여러 대상의 합계 응답 구조를 단순화한 가상 예시입니다. 아래 실제 조회 코드는 단일 캠페인의 일별 응답을 사용합니다.

ror 항목을 지우고 다시 실행하면 마지막 값이 None인지 확인합니다. None은 여기서 값이 확인되지 않았다는 뜻이며 0과 다릅니다. 실제 0이 반환된 경우와 응답에 필드가 없는 경우를 구분하는 연습입니다.

실제 조회 전에 준비할 것

네이버 공식 시작 안내는 광고 시스템의 ‘도구 > API 사용 관리’에서 서비스 신청과 키 발급을 진행하도록 설명합니다. 현재 계정의 메뉴와 사용 권한을 확인합니다. 다른 매체보다 무조건 간단하거나 모든 계정이 즉시 이용 가능하다고 가정하지 않습니다.

위 이미지는 기존 화면 예시입니다. 통합 광고주센터를 사용하는 경우 화면 이름과 이동 경로가 달라질 수 있습니다.

필요한 값역할
검색광고 CUSTOMER_ID조회할 광고 계정을 구분
API Key 또는 액세스 라이선스요청자의 API 이용 정보
Secret Key 또는 비밀키요청의 인증 서명을 생성
캠페인 ID이번에 조회할 캠페인 하나를 지정
조회 날짜광고 관리자와 대조할 하루를 지정

통합광고 ID인 adAccountNo와 검색광고 CUSTOMER_ID를 혼동하지 않습니다. 네이버는 통합 플랫폼 전환 후에도 검색광고 Open API에는 기존 CUSTOMER_ID를 사용해야 한다고 안내합니다. ID를 모르면 공식 ID 구분 안내와 접근 계정 목록 안내에서 확인합니다.

대행사가 조회할 때도 관리 계정 연결과 실제 접근 권한을 확인해야 합니다. 다른 광고주의 ID를 안다고 자료를 읽을 수 있는 것은 아닙니다. 키와 비밀키는 코드, 대화와 공개 저장소에 넣지 않습니다.

HMAC 서명은 무엇인가요?

HMAC-SHA256은 비밀키와 요청 정보를 이용해 인증용 서명을 만드는 방식입니다. 비밀키 자체를 전송하는 대신 요청 시각, HTTP 방식과 경로로 만든 서명을 보냅니다.

서명할 내용: 밀리초 시각.GET./stats 요청에 넣을 정보: X-Timestamp, X-API-KEY, X-Customer, X-Signature

GET은 자료를 조회하는 HTTP 요청 방식입니다. 아래 코드는 캠페인을 만들거나 예산을 변경하는 요청을 보내지 않습니다.

실제 주소는 https://api.searchad.naver.com입니다. 서명에는 /stats 같은 경로를 사용하며, 전체 주소나 뒤의 검색 조건 문자열을 붙이지 않습니다. 요청을 다시 보낼 때는 새 시각으로 서명을 다시 만듭니다. 공식 서명 예제

단일 ID 조회와 여러 ID 조회를 구분합니다

목적요청 조건
캠페인 하나의 일별 자료id 하나와 timeIncrement=1
여러 대상의 기간 합계ids와 timeIncrement=allDays

공식 통계 문서에서 단일 ID와 여러 ID의 지원 조건이 다릅니다. 여러 ID 조회에 일별 옵션을 그대로 붙이지 않습니다. fields와 timeRange는 JSON 문자열로 만들어 보냅니다. JSON은 항목 이름과 값을 정해진 형식으로 적는 데이터 표현입니다. 통계 요청 명세

통계 날짜는 KST, 즉 한국 표준시 기준입니다. 처음에는 오늘처럼 집계가 진행 중인 날 대신 최근 완료된 하루를 고릅니다. 과거 날짜도 전환 등의 집계가 갱신될 수 있으므로 수집 시점을 남깁니다.

실제 조회 코드: 캠페인 하나와 날짜 하루

아래는 Python 3 표준 라이브러리만 사용하는 조회 예시입니다. naver_one_day.py로 저장합니다. 실행 환경에 다음 환경 변수가 준비돼 있어야 합니다. 환경 변수는 프로그램이 읽는 설정값입니다.

NAVER_SEARCHAD_API_KEY NAVER_SEARCHAD_SECRET_KEY NAVER_SEARCHAD_CUSTOMER_ID NAVER_SEARCHAD_CAMPAIGN_ID NAVER_SEARCHAD_DATE (YYYY-MM-DD 형식)

회사에서 정한 비밀값 관리 방식으로 설정한 뒤 실행합니다. 터미널에서 키를 출력해 확인하거나 화면을 그대로 공유하지 않습니다. 계정 ID와 캠페인 ID, 날짜가 맞는지 먼저 대조합니다.

import os, json, time, hmac, hashlib, base64, csv from datetime import date from pathlib import Path from urllib.parse import urlencode, quote from urllib.request import Request, urlopen from urllib.error import HTTPError BASE = "https://api.searchad.naver.com" API_KEY = os.environ["NAVER_SEARCHAD_API_KEY"] SECRET = os.environ["NAVER_SEARCHAD_SECRET_KEY"] CUSTOMER = os.environ["NAVER_SEARCHAD_CUSTOMER_ID"] CID = os.environ["NAVER_SEARCHAD_CAMPAIGN_ID"] DAY = date.fromisoformat(os.environ["NAVER_SEARCHAD_DATE"]).isoformat() OUT = Path("naver_one_day.csv") if OUT.exists(): raise FileExistsError("기존 CSV를 확인하고 다른 이름이나 폴더를 사용하세요.") def get_json(path, params=None): for attempt in range(3): stamp = str(int(time.time() * 1000)) message = f"{stamp}.GET.{path}".encode() signature = base64.b64encode( hmac.new(SECRET.encode(), message, hashlib.sha256).digest() ).decode() url = BASE + path + ("?" + urlencode(params) if params else "") req = Request(url, headers={ "X-Timestamp": stamp, "X-API-KEY": API_KEY, "X-Customer": CUSTOMER, "X-Signature": signature, }, method="GET") try: with urlopen(req, timeout=30) as response: return json.load(response) except HTTPError as error: if error.code == 429 and attempt < 2: time.sleep(2 ** (attempt + 1)) continue raise RuntimeError(f"조회 실패: HTTP {error.code}") from None campaign = get_json("/ncc/campaigns/" + quote(CID, safe="")) if not isinstance(campaign, dict) or campaign.get("nccCampaignId") != CID: raise ValueError("요청한 캠페인과 응답 ID가 다릅니다.") report = get_json("/stats", { "id": CID, "fields": json.dumps(["impCnt", "clkCnt", "salesAmt"]), "timeRange": json.dumps({"since": DAY, "until": DAY}), "timeIncrement": "1", }) if not isinstance(report, dict): raise ValueError("통계 응답이 예상한 객체 형식이 아닙니다.") rows = report.get("data") if not isinstance(rows, list): raise ValueError("통계 응답의 data 목록을 확인하세요.") output = [] for row in rows: if not isinstance(row, dict): raise ValueError("통계 행이 예상한 객체 형식이 아닙니다.") if row.get("dateStart") != DAY or row.get("dateEnd") != DAY: raise ValueError("응답 날짜가 요청한 하루와 다릅니다.") output.append({ "date": DAY, "customer_id": CUSTOMER, "campaign_id": CID, "campaign_name": campaign.get("name"), "impressions": row.get("impCnt"), "clicks": row.get("clkCnt"), "cost_vat_included": row.get("salesAmt"), "collected_at_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), }) if output: with OUT.open("x", newline="", encoding="utf-8-sig") as handle: writer = csv.DictWriter(handle, fieldnames=list(output[0])) writer.writeheader() writer.writerows(output) print(f"{len(output)}행 저장: {OUT}") else: print("조회 결과가 비어 있습니다. 날짜와 계정 상태를 확인하세요.")

python3 naver_one_day.py로 실행한 뒤 CSV의 계정 ID, 캠페인 ID와 날짜를 봅니다. 비용과 클릭수를 광고 관리자에서 같은 조건으로 대조합니다. 비어 있는 필드는 CSV에서도 빈 칸으로 남고, 정상적으로 반환된 0은 0으로 저장됩니다. 출력의 date는 한국 시간 기준 조회 날짜이고, collected_at_utc는 세계 표준시인 UTC로 기록한 수집 시각입니다. 한국 시각은 UTC보다 9시간 빠릅니다.

코드는 429 응답일 때만 최대 세 번 시도합니다. 일정 간격을 두는 것만으로 호출 제한을 항상 피할 수 있는 것은 아닙니다. 계속 실패하면 반복 실행을 멈추고 공식 제한 안내를 확인합니다. 인증, 네트워크나 형식 오류를 무한히 재시도하지 않습니다.

비용과 ROAS를 합치기 전에 확인합니다

네이버 필드뜻보고서에서 확인할 점
impCnt노출수노출 횟수와 도달 인원은 다름
clkCnt클릭수방문 세션이나 구매수가 아님
salesAmt총비용공식 통계 명세의 부가세 포함 기준
ctr클릭률퍼센트 표시와 소수 비율 구분
ccnt, convAmt전환수와 전환매출액구매만인지 다른 전환을 포함하는지 확인
ror광고수익률퍼센트를 배수로 쓸 때 100으로 나눔
avgRnk평균 노출순위지원하는 광고와 지면 범위 확인

salesAmt는 이름에 sales가 있지만 매출액이 아니라 비용입니다. 공식 통계 명세는 부가세 포함 비용으로 설명합니다. 다른 매체 비용과 비교할 때는 통화와 세금 포함 기준을 함께 맞춥니다. 구글 비용의 마이크로 단위를 100만으로 나누는 것은 금액 단위 변환일 뿐 원화 환산이 아닙니다. 네이버 통계 필드 명세

가상 예시로 같은 기준의 전환매출액이 35,000원, 비용이 10,000원이면 광고수익률은 350%, 배수로는 3.5배입니다. 비용이 0이거나 전환매출액을 확인하지 못했다면 정상 ROAS를 계산했다고 적지 않습니다. 여러 캠페인의 ROAS도 단순 평균하지 말고 같은 기준의 매출 합계를 비용 합계로 나눕니다. 네이버 성과 지표와 계산식

Meta의 링크 클릭도 네이버 클릭과 완전히 같은 행동 범위라고 볼 수 없습니다. 전환수 역시 각 매체의 추적 설정과 기여 기간에 따라 달라 같은 구매가 중복 집계될 수 있습니다. 단위만 바꾼 뒤 세 매체의 전환수를 실제 전체 주문수라고 합산하지 않습니다.

기간을 늘릴 때와 오류가 날 때

공식 통계 FAQ는 세분화 없는 일반 보고서의 제공 기간을 2년, 한 번의 조회 범위를 92일로 안내합니다. 세분화한 보고서나 다운로드 보고서는 종류별 조건이 다르므로 모든 데이터의 보관 기간을 365일로 단정하면 안 됩니다. 먼저 하루 조회를 검증하고 기간을 늘립니다. 통계 기간 FAQ

증상먼저 확인할 것
환경 변수 이름이 나오는 KeyError해당 실행 환경에 필요한 설정이 있는지
URLError, 시간 초과인터넷 연결과 서비스 상태를 확인한 뒤 다시 시도
인증 또는 권한 오류라이선스와 비밀키, 검색광고 CUSTOMER_ID, 계정 권한
서명 오류PC 시각, 밀리초 값, GET과 서명한 경로
이름이 비어 있음응답의 name과 캠페인 ID
빈 결과 또는 누락 지표날짜, 실제 집행, 요청 필드와 응답 구조
기존 CSV 오류이전 결과를 확인하고 새 작업 폴더나 출력 이름 사용

첫 실행에서 KeyError가 나오면 코드에 키를 붙여 넣는 대신 오류에 나온 환경 변수 이름을 확인합니다. 회사의 설정 담당자에게 필요한 변수 이름만 전달하고, 키가 보이는 화면이나 전체 설정 목록은 공유하지 않습니다.

확인 질문입니다. 비용 필드가 없는 행과 비용이 0인 행을 모두 0원으로 저장해도 될까요? 그렇지 않습니다. 앞의 행은 비용을 확인하지 못한 상태이며, 뒤의 행만 응답에서 0이 확인된 것입니다.

첫날의 값이 맞으면 날짜만 바꿔 새 폴더에서 다시 실행하고 같은 기준으로 대조합니다. 여러 캠페인으로 확장할 때는 목록의 페이지 처리와 캠페인별 오류 기록도 추가합니다. 여기의 단일 캠페인 예시를 전체 계정 수집 코드라고 사용하지 않습니다.

AI에게 구현을 요청한다면

네이버 검색광고 API의 읽기 전용 보고서 코드를 검토해 주세요. 실제 키는 제공하지 않겠습니다. 공식 주소와 검색광고 CUSTOMER_ID를 사용하고, 단일 캠페인의 id와 timeIncrement=1로 날짜 하루를 조회합니다. 캠페인 이름은 name, 통계 행은 data에서 읽습니다. 누락값을 0으로 채우지 않고, 재시도 횟수와 요청 시간을 제한해 주세요. CSV는 기존 파일을 덮어쓰지 않아야 합니다. 실제 API 호출 전에 가상 응답으로 정상, 빈 결과와 오류를 검사해 주세요.

AI가 만든 코드에서도 주소와 필드, 날짜, 단위를 직접 확인합니다. 구글 Ads API와 Meta API를 합치는 단계는 각 매체의 하루 조회가 맞은 다음 진행합니다.

3줄 요약

  1. 네이버 검색광고 API는 검색광고 계정 ID와 발급받은 키를 사용하고 요청마다 인증 서명을 생성합니다.

  2. 처음에는 캠페인 하나의 하루 통계를 조회해 이름과 ID를 연결하고 응답의 날짜 및 비용을 광고 관리자와 맞춥니다.

  3. ROAS의 퍼센트를 배수로 바꾸는 것과 비용의 부가세 및 전환 기준을 맞추는 것은 별도로 확인해야 합니다.

Share

제대로 이해했는지 한 문제로 확인해 볼까요?

답을 고르면 바로 풀이가 나와요.

퍼센트 단위의 광고수익률 ror가 350일 때 보고서의 배수 단위로 바꾸면 얼마인가요?

이 글이 도움이 되었나요?

이 글 다음 배우기AI 업무 자동화 입문입문 코스 · 10편

메일, 회의록, 리포트처럼 반복되는 일을 AI로 자동화하는 순서와 안전장치를 다룹니다

  1. 1AI 업무 자동화 뜻과 규칙 자동화와 다른 점
  2. 2자동화할 일 고르기: 반복, 규칙, 되돌리기
  3. 3n8n 이해하기, 노드로 잇는 자동화와 셀프호스팅
코스 전체 보기 →
이어서 읽기 좋은 글비개발자를 위한 카카오모먼트 API: 권한 확인과 하루 보고서 →

카카오모먼트 API의 사용 권한과 비즈니스 토큰을 구분하고 가상 응답으로 보고서 구조를 익힙니다. 캠페인 하나의 하루 성과를 조회한 뒤 날짜, 지표 그룹과 누락값을 확인합니다.