Perplexity Sonar API 개발자 가이드: 애플리케이션에 AI 검색 기능 구축하기
Perplexity Sonar API란 무엇인가
Perplexity Sonar API는 실시간 웹 검색과 대규모 언어 모델(LLM)을 결합하여, 출처가 명시된 정확한 답변을 프로그래밍 방식으로 제공하는 검색 특화 API이다. 기존의 검색 엔진 API가 링크 목록만 반환하는 것과 달리, Sonar API는 검색 결과를 종합하여 자연어로 구성된 답변과 함께 인용(citation)을 제공한다.
Sonar API가 개발자에게 유용한 핵심 이유는 다음과 같다.
- 실시간 웹 검색 통합: 모델이 자체적으로 웹을 검색하여 최신 정보를 반환한다. RAG(Retrieval-Augmented Generation) 파이프라인을 직접 구축할 필요가 없다.
- 인용 기반 응답: 모든 답변에 출처 URL이 포함되어, 사용자에게 신뢰할 수 있는 정보를 제공할 수 있다.
- OpenAI 호환 인터페이스: chat completions 형식을 따르므로, 기존 OpenAI SDK 기반 코드에서 엔드포인트와 API 키만 변경하면 바로 사용할 수 있다.
- 다양한 모델 티어: 빠른 응답이 필요한 경우부터 심층 리서치까지, 용도에 맞는 모델을 선택할 수 있다.
이 가이드에서는 API 키 발급부터 프로덕션 수준의 검색 기능 구축까지 전 과정을 다룬다.
시작하기: API 키 발급과 첫 번째 호출
API 키 발급
Perplexity API를 사용하려면 먼저 API 키를 발급받아야 한다.
- Perplexity API 설정 페이지에 접속한다.
- 결제 정보를 등록하고 크레딧을 충전한다. Sonar API는 사용량 기반 과금 방식이다.
- API 키를 생성하고 안전한 장소에 저장한다. 키는
pplx-접두사로 시작한다.
첫 번째 API 호출 (Python)
Python에서 requests 라이브러리를 사용한 기본 호출 예시이다.
import requests
import json
API_KEY = "pplx-your-api-key-here"
URL = "https://api.perplexity.ai/chat/completions"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "sonar",
"messages": [
{
"role": "system",
"content": "당신은 정확하고 간결한 답변을 제공하는 검색 어시스턴트입니다."
},
{
"role": "user",
"content": "2026년 한국의 AI 규제 정책 현황은?"
}
]
}
response = requests.post(URL, headers=headers, json=payload)
data = response.json()
# 답변 출력
print(data["choices"][0]["message"]["content"])
# 인용 출력
if "citations" in data:
for i, url in enumerate(data["citations"], 1):
print(f"[{i}] {url}")
첫 번째 API 호출 (JavaScript)
Node.js 환경에서 fetch를 사용하는 예시이다.
const API_KEY = "pplx-your-api-key-here";
const URL = "https://api.perplexity.ai/chat/completions";
async function searchWithSonar(query) {
const response = await fetch(URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "sonar",
messages: [
{
role: "system",
content: "정확하고 간결한 답변을 제공하는 검색 어시스턴트입니다."
},
{
role: "user",
content: query
}
]
})
});
const data = await response.json();
return {
answer: data.choices[0].message.content,
citations: data.citations || []
};
}
// 사용 예시
const result = await searchWithSonar("2026년 한국의 AI 규제 정책 현황은?");
console.log("답변:", result.answer);
result.citations.forEach((url, i) => {
console.log(`[${i + 1}] ${url}`);
});
응답 구조 이해
Sonar API의 응답은 OpenAI chat completions 형식을 기반으로 하되, 검색 관련 필드가 추가되어 있다. 주요 필드는 다음과 같다.
{
"id": "chatcmpl-abc123",
"model": "sonar",
"object": "chat.completion",
"created": 1711504800,
"choices": [
{
"index": 0,
"finish_reason": "stop",
"message": {
"role": "assistant",
"content": "2026년 한국 정부는 AI 기본법을 시행하며..."
}
}
],
"citations": [
"https://example.com/ai-regulation-korea-2026",
"https://example.com/ai-basic-act"
],
"usage": {
"prompt_tokens": 42,
"completion_tokens": 256,
"total_tokens": 298
}
}
- choices[0].message.content: 검색 결과를 종합한 자연어 답변이다. 본문 내에서
[1],[2]와 같은 인용 번호가 표시된다. - citations: 답변에서 참조한 웹 소스의 URL 목록이다. 본문의 인용 번호와 이 배열의 인덱스가 대응한다.
- usage: 토큰 사용량 정보로, 비용 추적에 활용한다.
모델 선택: Sonar, Sonar Pro, Sonar Deep Research
Perplexity는 용도에 따라 세 가지 Sonar 모델을 제공한다. 아래 표에서 각 모델의 특성을 비교할 수 있다.
| 항목 | Sonar | Sonar Pro | Sonar Deep Research |
|---|---|---|---|
| 용도 | 빠른 팩트 체크, 간단한 질문 | 복합적인 질문, 다중 소스 종합 | 심층 리서치, 보고서 수준 분석 |
| 응답 속도 | 빠름 (1-3초) | 보통 (3-8초) | 느림 (30초-수 분) |
| 검색 깊이 | 기본 웹 검색 | 심화 웹 검색, 더 많은 소스 참조 | 다단계 검색, 반복 탐색 |
| 인용 수 | 3-5개 | 5-15개 | 10-30개 |
| 모델명 | sonar | sonar-pro | sonar-deep-research |
| 비용 | 낮음 | 중간 | 높음 |
| 적합한 시나리오 | 챗봇, 빠른 Q&A | 비교 분석, 시장 조사 요약 | 경쟁사 분석, 기술 보고서 작성 |
모델 선택 기준을 정리하면 다음과 같다.
- 응답 시간이 중요한 실시간 대화 인터페이스에는
sonar를 사용한다. 사용자가 즉각적인 응답을 기대하는 챗봇이나 검색 바에 적합하다. - 정확성과 깊이가 중요한 리서치 도구에는
sonar-pro를 사용한다. 여러 소스를 교차 검증하여 더 신뢰할 수 있는 답변을 제공한다. - 종합 보고서 수준의 분석이 필요한 경우에는
sonar-deep-research를 사용한다. 비동기 처리가 적합하며, 사용자에게 “분석 중” 상태를 표시하는 것이 좋다.
고급 기능
스트리밍 응답
긴 응답을 실시간으로 표시하려면 스트리밍을 활성화한다. 사용자 경험을 크게 개선할 수 있다.
import requests
import json
def stream_search(query):
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "sonar",
"messages": [
{"role": "user", "content": query}
],
"stream": True
}
response = requests.post(
"https://api.perplexity.ai/chat/completions",
headers=headers,
json=payload,
stream=True
)
full_content = ""
for line in response.iter_lines():
if line:
line_text = line.decode("utf-8")
if line_text.startswith("data: "):
data_str = line_text[6:]
if data_str.strip() == "[DONE]":
break
chunk = json.loads(data_str)
delta = chunk["choices"][0].get("delta", {})
content = delta.get("content", "")
if content:
print(content, end="", flush=True)
full_content += content
print()
return full_content
JavaScript에서는 다음과 같이 구현한다.
async function streamSearch(query) {
const response = await fetch("https://api.perplexity.ai/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "sonar",
messages: [{ role: "user", content: query }],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullContent = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
const lines = text.split("\n").filter(line => line.startsWith("data: "));
for (const line of lines) {
const dataStr = line.slice(6);
if (dataStr.trim() === "[DONE]") return fullContent;
const chunk = JSON.parse(dataStr);
const content = chunk.choices[0]?.delta?.content || "";
if (content) {
process.stdout.write(content);
fullContent += content;
}
}
}
return fullContent;
}
도메인 필터링
특정 도메인의 소스만 포함하거나 제외할 수 있다. 신뢰할 수 있는 소스로 검색 범위를 제한하고 싶을 때 유용하다.
payload = {
"model": "sonar",
"messages": [
{"role": "user", "content": "최근 반도체 산업 동향"}
],
"search_domain_filter": ["reuters.com", "bloomberg.com", "ft.com"]
}
특정 도메인을 제외하려면 도메인명 앞에 - 접두사를 붙인다.
payload = {
"model": "sonar",
"messages": [
{"role": "user", "content": "최근 반도체 산업 동향"}
],
"search_domain_filter": ["-reddit.com", "-quora.com"]
}
최신성 필터 (search_recency_filter)
검색 결과의 시간 범위를 제한하여 최신 정보만 가져올 수 있다. 뉴스나 트렌드 분석에 특히 유용하다.
payload = {
"model": "sonar",
"messages": [
{"role": "user", "content": "이번 주 AI 산업 주요 뉴스"}
],
"search_recency_filter": "week" # "month", "week", "day", "hour" 중 선택
}
사용 가능한 옵션은 다음과 같다.
"month": 최근 한 달 이내 결과"week": 최근 일주일 이내 결과"day": 최근 하루 이내 결과"hour": 최근 한 시간 이내 결과
시스템 프롬프트 활용
시스템 프롬프트를 통해 응답의 형식, 언어, 톤을 제어할 수 있다. 제품에 맞는 일관된 출력을 유지하는 데 핵심적인 역할을 한다.
payload = {
"model": "sonar",
"messages": [
{
"role": "system",
"content": (
"당신은 한국 스타트업 투자자를 위한 시장 조사 어시스턴트입니다. "
"답변은 반드시 한국어로 작성하고, 핵심 수치와 데이터를 먼저 제시합니다. "
"모든 주장에는 출처를 명시하며, 불확실한 정보는 명확히 구분합니다. "
"답변 마지막에 '핵심 요약' 섹션을 3줄 이내로 추가합니다."
)
},
{
"role": "user",
"content": "2026년 한국 SaaS 시장 규모와 성장률"
}
]
}
온도(temperature) 설정
응답의 창의성 수준을 조절할 수 있다. 검색 기반 팩트 확인에는 낮은 값을, 브레인스토밍에는 높은 값을 사용한다.
payload = {
"model": "sonar",
"messages": [
{"role": "user", "content": "양자 컴퓨팅의 실용적 응용 사례"}
],
"temperature": 0.1 # 0.0에서 2.0 사이, 기본값은 0.2
}
프로덕션 기능 구축
리서치 어시스턴트 구현
사용자의 질문을 받아 검색하고, 인용과 함께 구조화된 응답을 제공하는 리서치 어시스턴트를 구축해 보겠다.
import requests
import json
from dataclasses import dataclass
from typing import Optional
@dataclass
class SearchResult:
answer: str
citations: list[str]
model: str
tokens_used: int
class SonarResearchAssistant:
BASE_URL = "https://api.perplexity.ai/chat/completions"
def __init__(self, api_key: str, default_model: str = "sonar"):
self.api_key = api_key
self.default_model = default_model
self.session = requests.Session()
self.session.headers.update({
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
})
def search(
self,
query: str,
model: Optional[str] = None,
system_prompt: Optional[str] = None,
recency: Optional[str] = None,
domain_filter: Optional[list[str]] = None,
temperature: float = 0.2
) -> SearchResult:
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
messages.append({"role": "user", "content": query})
payload = {
"model": model or self.default_model,
"messages": messages,
"temperature": temperature
}
if recency:
payload["search_recency_filter"] = recency
if domain_filter:
payload["search_domain_filter"] = domain_filter
response = self.session.post(self.BASE_URL, json=payload)
response.raise_for_status()
data = response.json()
return SearchResult(
answer=data["choices"][0]["message"]["content"],
citations=data.get("citations", []),
model=data["model"],
tokens_used=data["usage"]["total_tokens"]
)
def deep_research(self, query: str) -> SearchResult:
"""심층 리서치가 필요한 질문에 사용한다."""
return self.search(
query=query,
model="sonar-deep-research",
system_prompt=(
"심층적이고 포괄적인 분석을 제공합니다. "
"여러 소스를 교차 검증하고, 상반되는 관점이 있으면 함께 제시합니다."
),
temperature=0.1
)
def quick_fact_check(self, claim: str) -> SearchResult:
"""빠른 팩트 체크에 사용한다."""
return self.search(
query=f"다음 주장의 사실 여부를 검증해 주세요: {claim}",
model="sonar",
system_prompt=(
"팩트 체커로서 주장의 진위를 판별합니다. "
"'사실', '거짓', '부분적 사실', '확인 불가' 중 하나로 판정하고, "
"근거를 제시합니다."
),
temperature=0.0
)
인용 표시 처리
Sonar API 응답의 인용 번호를 사용자에게 표시하는 로직을 구현한다. 프론트엔드에서 클릭 가능한 링크로 변환하는 것이 일반적이다.
import re
def parse_citations(content: str, citations: list[str]) -> str:
"""본문의 [1], [2] 등을 실제 URL 링크로 변환한다."""
def replace_citation(match):
index = int(match.group(1)) - 1
if 0 <= index < len(citations):
url = citations[index]
return f'[{match.group(1)}]({url})'
return match.group(0)
return re.sub(r'\[(\d+)\]', replace_citation, content)
def format_response_with_sources(content: str, citations: list[str]) -> dict:
"""응답을 본문과 소스 목록으로 분리하여 구조화한다."""
return {
"content": content,
"sources": [
{"index": i + 1, "url": url}
for i, url in enumerate(citations)
],
"content_with_links": parse_citations(content, citations)
}
JavaScript에서 프론트엔드 렌더링 시 인용을 처리하는 예시이다.
function renderCitations(content, citations) {
// [1], [2] 등을 클릭 가능한 링크로 변환
const rendered = content.replace(/\[(\d+)\]/g, (match, num) => {
const index = parseInt(num) - 1;
if (index >= 0 && index < citations.length) {
const url = citations[index];
return `<a href="${url}" target="_blank" rel="noopener noreferrer"
class="citation-link" title="${url}">[${num}]</a>`;
}
return match;
});
// 소스 목록 생성
const sourceList = citations.map((url, i) => {
const domain = new URL(url).hostname.replace("www.", "");
return `<li><a href="${url}" target="_blank">[${i + 1}] ${domain}</a></li>`;
}).join("\n");
return {
html: rendered,
sourceListHtml: `<ol class="citation-sources">${sourceList}</ol>`
};
}
오류 처리와 재시도
프로덕션 환경에서는 네트워크 오류, 레이트 리밋, 서버 오류에 대한 적절한 처리가 필수적이다.
import time
import requests
from requests.exceptions import RequestException
class SonarAPIClient:
def __init__(self, api_key: str, max_retries: int = 3):
self.api_key = api_key
self.max_retries = max_retries
self.base_url = "https://api.perplexity.ai/chat/completions"
def _make_request(self, payload: dict) -> dict:
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
for attempt in range(self.max_retries):
try:
response = requests.post(
self.base_url,
headers=headers,
json=payload,
timeout=60
)
if response.status_code == 200:
return response.json()
if response.status_code == 429:
# 레이트 리밋: Retry-After 헤더를 확인한다
retry_after = int(response.headers.get("Retry-After", 5))
print(f"레이트 리밋 도달. {retry_after}초 후 재시도합니다.")
time.sleep(retry_after)
continue
if response.status_code >= 500:
# 서버 오류: 지수 백오프로 재시도한다
wait_time = (2 ** attempt) + 1
print(f"서버 오류 ({response.status_code}). {wait_time}초 후 재시도합니다.")
time.sleep(wait_time)
continue
# 4xx 클라이언트 오류 (429 제외): 재시도하지 않는다
response.raise_for_status()
except RequestException as e:
if attempt < self.max_retries - 1:
wait_time = (2 ** attempt) + 1
print(f"요청 실패: {e}. {wait_time}초 후 재시도합니다.")
time.sleep(wait_time)
else:
raise
raise Exception(f"{self.max_retries}회 시도 후에도 요청이 실패했습니다.")
캐싱 전략
동일한 쿼리가 반복되는 경우 캐싱을 통해 비용을 절감하고 응답 시간을 단축할 수 있다. 검색 결과의 특성상 TTL(Time To Live)을 적절히 설정하는 것이 중요하다.
import hashlib
import json
import time
from functools import lru_cache
class CachedSonarClient:
def __init__(self, api_key: str, cache_ttl: int = 3600):
self.client = SonarAPIClient(api_key)
self.cache = {}
self.cache_ttl = cache_ttl # 기본 1시간
def _cache_key(self, query: str, model: str, recency: str = None) -> str:
"""쿼리와 설정을 기반으로 캐시 키를 생성한다."""
key_data = json.dumps({
"query": query,
"model": model,
"recency": recency
}, sort_keys=True)
return hashlib.sha256(key_data.encode()).hexdigest()
def search(self, query: str, model: str = "sonar",
recency: str = None, bypass_cache: bool = False) -> dict:
cache_key = self._cache_key(query, model, recency)
# 캐시 확인
if not bypass_cache and cache_key in self.cache:
entry = self.cache[cache_key]
if time.time() - entry["timestamp"] < self.cache_ttl:
return entry["data"]
# API 호출
payload = {
"model": model,
"messages": [{"role": "user", "content": query}]
}
if recency:
payload["search_recency_filter"] = recency
result = self.client._make_request(payload)
# 캐시 저장
self.cache[cache_key] = {
"data": result,
"timestamp": time.time()
}
return result
캐시 TTL 설정 권장 사항은 다음과 같다.
| 쿼리 유형 | 권장 TTL | 이유 |
|---|---|---|
| 속보 / 실시간 뉴스 | 5-15분 | 빠르게 변하는 정보 |
| 일반 뉴스 | 1-4시간 | 적당한 최신성 유지 |
| 기술 문서 / 가이드 | 12-24시간 | 자주 변하지 않는 정보 |
| 역사적 사실 | 24-72시간 | 거의 변하지 않는 정보 |
레이트 리밋 관리
Sonar API는 분당 요청 수에 제한이 있다. 프로덕션 환경에서는 토큰 버킷 방식의 레이트 리미터를 구현하는 것이 좋다.
import threading
import time
class RateLimiter:
"""토큰 버킷 방식의 레이트 리미터."""
def __init__(self, requests_per_minute: int = 50):
self.capacity = requests_per_minute
self.tokens = requests_per_minute
self.refill_rate = requests_per_minute / 60.0 # 초당 토큰 보충량
self.last_refill = time.time()
self.lock = threading.Lock()
def acquire(self, timeout: float = 30.0) -> bool:
"""토큰을 획득한다. 성공하면 True, 타임아웃이면 False를 반환한다."""
deadline = time.time() + timeout
while time.time() < deadline:
with self.lock:
self._refill()
if self.tokens >= 1:
self.tokens -= 1
return True
time.sleep(0.1)
return False
def _refill(self):
now = time.time()
elapsed = now - self.last_refill
self.tokens = min(self.capacity, self.tokens + elapsed * self.refill_rate)
self.last_refill = now
비용 관리
Sonar API는 입력 토큰과 출력 토큰에 따라 과금된다. 비용을 효과적으로 관리하기 위한 방법을 정리한다.
모델별 비용 구조
| 모델 | 입력 토큰 단가 (1M 토큰당) | 출력 토큰 단가 (1M 토큰당) | 검색 요청당 추가 비용 |
|---|---|---|---|
| sonar | $1 | $1 | $5 / 1000 요청 |
| sonar-pro | $3 | $15 | $5 / 1000 요청 |
| sonar-deep-research | $2 | $8 | 요청당 가변 |
위 금액은 변경될 수 있으므로, 최신 요금은 Perplexity 공식 문서를 확인하는 것이 좋다.
비용 최적화 전략
- 모델 티어 분리: 간단한 질문은
sonar, 복잡한 질문만sonar-pro를 사용하도록 라우팅 로직을 구현한다.
def select_model(query: str) -> str:
"""쿼리 복잡도에 따라 적합한 모델을 선택한다."""
complex_keywords = ["비교", "분석", "추세", "전망", "종합", "차이점"]
deep_keywords = ["심층 분석", "보고서", "상세 조사", "전수 조사"]
if any(kw in query for kw in deep_keywords):
return "sonar-deep-research"
elif any(kw in query for kw in complex_keywords) or len(query) > 100:
return "sonar-pro"
else:
return "sonar"
- max_tokens 제한: 필요 이상으로 긴 응답을 방지하여 출력 토큰 비용을 절약한다.
payload = {
"model": "sonar",
"messages": [{"role": "user", "content": query}],
"max_tokens": 500 # 간결한 답변이 필요한 경우 제한을 둔다
}
- 사용량 모니터링: 일일/월간 사용량을 추적하고 임계치를 설정한다.
class UsageTracker:
def __init__(self, daily_budget_usd: float = 10.0):
self.daily_budget = daily_budget_usd
self.daily_tokens = 0
self.daily_requests = 0
self.reset_date = time.strftime("%Y-%m-%d")
def record(self, usage: dict):
today = time.strftime("%Y-%m-%d")
if today != self.reset_date:
self.daily_tokens = 0
self.daily_requests = 0
self.reset_date = today
self.daily_tokens += usage.get("total_tokens", 0)
self.daily_requests += 1
def is_within_budget(self) -> bool:
# 대략적인 비용 추정 (sonar 기준)
estimated_cost = (self.daily_tokens / 1_000_000) * 1.0
estimated_cost += (self.daily_requests / 1000) * 5.0
return estimated_cost < self.daily_budget
자주 묻는 질문 (FAQ)
Sonar API는 OpenAI SDK와 호환되는가?
그렇다. Sonar API는 OpenAI의 chat completions 형식을 따르므로, 기존 OpenAI Python SDK 또는 JavaScript SDK에서 base URL과 API 키만 변경하면 사용할 수 있다.
from openai import OpenAI
client = OpenAI(
api_key="pplx-your-api-key",
base_url="https://api.perplexity.ai"
)
response = client.chat.completions.create(
model="sonar",
messages=[{"role": "user", "content": "오늘 코스피 지수는?"}]
)
다만, citations 필드 등 Sonar 전용 응답 필드는 OpenAI SDK 타입에 포함되어 있지 않으므로, 원시 응답 딕셔너리에서 직접 접근해야 한다.
한국어 쿼리와 응답을 지원하는가?
Sonar API는 다국어를 지원한다. 한국어로 질문하면 한국어로 답변하며, 시스템 프롬프트에서 언어를 명시적으로 지정할 수도 있다. 한국어 웹 소스도 검색 대상에 포함되며, 도메인 필터를 활용하여 한국어 소스의 비중을 높일 수 있다.
스트리밍과 일반 응답 중 어떤 것을 사용해야 하는가?
사용자 인터페이스가 있는 애플리케이션에서는 스트리밍을 권장한다. 특히 sonar-pro나 sonar-deep-research처럼 응답 시간이 긴 모델에서는 스트리밍이 사용자 체감 대기 시간을 크게 줄여 준다. 반면, 백엔드 자동화 파이프라인이나 배치 처리에서는 일반 응답이 코드 구현이 단순하다.
인용이 포함되지 않는 경우가 있는가?
드물지만, 모델이 자체 지식만으로 답변할 수 있다고 판단하거나 검색 결과가 충분하지 않은 경우 인용이 비어 있을 수 있다. 프로덕션 코드에서는 citations 필드가 빈 배열이거나 존재하지 않는 경우를 반드시 처리해야 한다.
API 키 보안은 어떻게 관리해야 하는가?
API 키는 절대 클라이언트 사이드 코드(프론트엔드 JavaScript)에 노출해서는 안 된다. 반드시 서버 사이드에서 API 호출을 수행하고, 환경 변수나 시크릿 매니저를 통해 키를 관리한다. .env 파일에 저장하는 경우 .gitignore에 반드시 추가한다.
import os
API_KEY = os.environ.get("PERPLEXITY_API_KEY")
if not API_KEY:
raise ValueError("PERPLEXITY_API_KEY 환경 변수가 설정되지 않았습니다.")
동시 요청 제한은 얼마인가?
기본 레이트 리밋은 플랜에 따라 다르다. 일반적으로 분당 50회 요청부터 시작하며, 더 높은 처리량이 필요한 경우 Perplexity 팀에 엔터프라이즈 플랜을 문의할 수 있다. 레이트 리밋에 도달하면 HTTP 429 응답이 반환되며, Retry-After 헤더에 권장 대기 시간이 포함된다.
Sonar API를 기존 RAG 파이프라인과 함께 사용할 수 있는가?
가능하다. Sonar API를 외부 검색 도구로 활용하고, 자체 벡터 데이터베이스의 내부 문서 검색 결과와 결합하는 하이브리드 접근이 효과적이다. 예를 들어, 내부 지식 기반에서 관련 문서를 먼저 검색한 뒤, Sonar API로 최신 외부 정보를 보완하는 방식으로 구성할 수 있다.
정리
Perplexity Sonar API는 웹 검색과 LLM을 결합한 강력한 도구로, 인용이 포함된 신뢰할 수 있는 검색 기능을 애플리케이션에 빠르게 통합할 수 있게 해 준다. 핵심 사항을 다시 정리하면 다음과 같다.
- 모델 선택: 속도가 중요하면
sonar, 깊이가 중요하면sonar-pro, 종합 분석이 필요하면sonar-deep-research를 사용한다. - 인용 처리: 모든 응답에 포함된 출처 URL을 사용자에게 표시하여 신뢰성을 확보한다.
- 프로덕션 준비: 레이트 리밋 관리, 지수 백오프 재시도, 응답 캐싱, 비용 모니터링을 구현한다.
- 고급 기능: 도메인 필터, 최신성 필터, 스트리밍을 활용하여 사용자 경험을 최적화한다.
공식 문서는 Perplexity API Documentation에서 확인할 수 있으며, 최신 모델 목록과 요금 정보도 해당 페이지에서 관리된다.