# 외부 API 원가 **사이트 1건을 만드는 데 나가는 외부 API 비용**만 다룬다. 상한은 **$1(1,400원)** 이고, 문서가 아니라 코드가 강제한다(3절). 제품 제약으로서의 근거는 [PRODUCT.md 8절](PRODUCT.md). > ⚠️ 이 상한에 **개발비를 섞지 않는다.** 코드를 짜는 데 든 LLM 요금은 일회성 지출이고, > 사이트를 만들 때마다 반복해서 나가는 원가와는 성격이 다르다. --- ## 1. 공급자별 상태 (2026-08-31) | API | 용도 | 키 | 붙은 자리 | 과금 | |---|---|---|---|---| | **TourAPI** (`apis.data.go.kr`) | fact 주력 공급원 — 숙박·음식점 | ✅ | `collector/tour_api_adapter.py` | **무료** (개발계정 1,000건/일) | | **네이버 지역검색** (`openapi.naver.com`) | 동일 업소 검증 후보 | ✅ | `external/naver.py` | 무료 쿼터 | | **Perplexity Sonar** | 채널 URL 발견 | ✅ | `external/perplexity.py` | 요청 + 토큰 | | **Gemini** (AI Studio) | 사진 분류 · 소개문/FAQ · 붙여넣기 추출 | ✅ | `external/gemini.py` `gemini_text.py` `gemini_extract.py` | 토큰 | | **Kakao Local** (`dapi.kakao.com`) | 주변 정보 | ⬜ 미발급 | `external/kakao.py` | 건당 2원 | | **Open-Meteo** | 날씨 | 불필요 | `external/open_meteo.py` | 무료 | `.env` 의 키가 비어 있으면 **해당 어댑터만 비활성되고 서버는 그대로 뜬다** — 부팅이 외부 계약에 묶이면 안 되기 때문이다. 수집 어댑터는 `COLLECT_ADAPTERS` 로 배포 없이 개별로 끌 수 있다. 무엇을 어디서 가져오는지(필드 단위 카탈로그와 실측 충전율)는 [DATA_SOURCE_RESEARCH.md 8-3](DATA_SOURCE_RESEARCH.md). ## 2. 단가표 코드의 단일 출처는 `solution/backend/common/cost.py` 의 `RATES` 다. 아래는 그 요약이다. | API | 단가 | 확정 | |---|---|---| | Kakao Local | 키워드/카테고리 검색 **2원**, 좌표 변환 **0.5원** | ✅ `.env.example` | | Perplexity Sonar | 요청 7원 + 1K 토큰당 1.4원 (low context, USD_KRW 1,400) | ✅ 공식 단가표 | | Gemini | 이미지 1원 · 1K 토큰 0.5원 | ❌ **추정치** | | TourAPI · Open-Meteo | 0원 | ✅ | ★ **좌표 변환이 키워드 검색보다 4배 싸다.** 같은 공급자인데 단가가 다르므로 호출측이 `kakao_coord` 로 구분해 넘긴다. 지역 정보를 `place_id` 가 아니라 **행정구역 코드로 캐싱**하는 이유도 이것이다 — 같은 지역에 사이트 50개가 생겨도 외부 조회는 1회다. ⚠️ **Kakao 무료 쿼터 함정** — 무료 쿼터는 개발자 계정의 "첫 번째 활성 앱" 에만 붙는다. dev/stage/prod 앱을 따로 파면 **하나만 무료**다. 앱을 나누기 전에 확인할 것. ## 3. 상한을 코드가 강제한다 ```python meter = CostMeter(place_id=42) meter.guard(Provider.PERPLEXITY, searches=2, tokens=3000) # 호출 "전" → 넘으면 BudgetExceeded ...실제 호출... meter.charge(Provider.PERPLEXITY, searches=2, tokens=3100) # 호출 "후" 실측 기록 ``` - `guard()` 가 막으면 **그 호출을 하지 않는다.** 이미 쓴 돈은 못 돌려받지만 손실이 선형으로 늘어나는 걸 끊는다. - 예산 70% 소진 시 경고 로그, 100% 초과 시 예외. **미터 1개 = 사이트 1건.** - `solution/backend/common/cost.py` · 테스트 `tests/test_cost.py` 8건. ⚠️ **Gemini 단가가 아직 추정치다.** 그래서 `assert_rates_confirmed()` 가 실배치를 막는다 — 추정치 위에 "예산 안에 들어온다" 는 결론을 세우지 않기 위해서다. 공식 단가표로 `RATES` 를 교체하면서 `confirmed=True` 로 바꾼다. 비용이 실제로 붙는 순서: ``` 상호명 → [Perplexity: 채널 URL 발견] ← 요청 + 토큰 → [TourAPI: fact 수집] ← 무료 → 크롤링(무료) → [Gemini: 사진 분류 + 카피] ← 토큰 (사진 수에 비례) ``` **업소당 원가를 먼저 실측한 뒤 배치를 키우는 순서**가 안전하다. ## 4. 아직 없는 것 — 호출 로그 테이블 DB(`solution/backend/common/database/model/models.py`, 17테이블)에 **API 호출·비용을 적재하는 테이블이 없다.** 지금은 애플리케이션 로그로만 남는다(`perplexity.py` 가 토큰과 검색 횟수를 찍는 정도). 파이프라인을 배치로 돌리기 전에 `api_call_logs`(공급자 · 엔드포인트 · 토큰/호출수 · 추정단가 · `place_id` · 시각)를 얹어두면, 이 문서를 손으로 갱신하지 않고 쿼리로 뽑을 수 있다. ## 5. SNS 비용 (2026-09-14) 사용자 결정: X 대신 Threads. [Meta 공식 컬렉션](https://www.postman.com/meta/threads/documentation/dht3nzz/threads-api)에 직접 API 건당 과금·유료 티어가 안내돼 있지 않다. 현재 0원으로 분리 기록하되 영구 무료로 약속하지 않는다. X는 현재 [URL 포함 생성 $0.20/요청](https://docs.x.com/x-api/getting-started/pricing)을 안내하므로 고정비라는 기존 계획을 폐기했다. X 어댑터는 넣지 않는다. | 비용 | 처리 | |---|---| | 사이트당 변동비 | Gemini 초안, 알림톡 발송. 초안 최대 3회 재요청, 버전당 원고 1건. 알림톡 단가 미확정이라 활성화 전 계약 확인 | | 계정/계약당 고정비 | 계약에 있다면 별도 운영비. 사이트 생성 CostMeter에 배분하지 않음 | | 개발비 | 일회성 구현·심사 대응 비용. 사이트 원가와 분리 | `Provider.THREADS`는 0원, 공개 가격 확정 근거가 없어 confirmed=False로 기록한다. 월 고정비라는 잘못된 근거로 confirmed=True를 넣지 않는다. `assert_rates_confirmed()`에 Threads를 포함하는 실배치는 운영 과금 확인 전 차단된다. 현재 SNS 서비스는 생성 호출 횟수 상한만 강제하며 누적 사이트 예산 연동은 별도 보완이 필요하다. Gemini 비용을 무료로 간주하지 않는다.