Naver Open API (Search, Papago, Clova)

raw JSON →
N/A (REST API — no versioned Python package) verified Tue May 12 auth: no python install: verified quickstart: stale

Naver's consumer-facing Open API suite — provides programmatic access to South Korea's dominant search engine (70%+ market share), Papago translation, Clova OCR/AI services, Naver Maps, and Naver Login (OAuth). No official Python SDK exists — integration is done via direct HTTP requests using the requests library. Auth uses Client ID and Client Secret passed as HTTP headers. All APIs are registered and managed via the Naver Cloud Platform console. IMPORTANT: Naver Open API is Korea-first — registration UI, documentation, and error messages are primarily in Korean. Daily quota resets at 00:00 KST (UTC+9), not UTC.

pip install requests
error {"errorMessage":"Authentication failed. (인증에 실패했습니다.)","errorCode":"024"}
cause The Client ID or Client Secret provided in the HTTP headers is incorrect, missing, or the application is not correctly registered in the Naver Developer Center for the specific API you are trying to use.
fix
Double-check the X-Naver-Client-Id and X-Naver-Client-Secret headers against the values obtained from the Naver Cloud Platform console. Ensure the registered application has access permissions for the specific API being called.
error {"errorCode":"429","errorMessage":"API call quota exceeded."}
cause The daily API call limit (e.g., 25,000 requests per day for many Naver APIs) has been exceeded for your Client ID. The quota resets at 00:00 KST (UTC+9).
fix
Wait until 00:00 KST for the daily quota to reset. If higher limits are consistently needed, refer to Naver Cloud Platform documentation for information on increasing quotas or paid service plans.
error {"errorMessage": "Incorrect query request (잘못된 쿼리요청입니다.)", "errorCode": "SE01"}
cause One or more of the request parameters (e.g., query string, 'display', 'start', or 'sort' values) are invalid, malformed, or fall outside the allowed range as specified in the API documentation.
fix
Review the Naver Open API documentation for the specific endpoint to ensure all parameters are correctly formatted, within valid ranges, and properly URL-encoded (e.g., using urllib.parse.quote in Python).
error ModuleNotFoundError: No module named 'requests'
cause The `requests` library, which is commonly used for making HTTP requests in Python and is necessary for integrating with the Naver Open API, is not installed in your Python environment.
fix
Install the requests library using pip: pip install requests.
gotcha Two different base URLs serve different API categories. Search and Login APIs use 'https://openapi.naver.com'. AI/ML services (Papago translation, Clova OCR, CLOVA Speech) use 'https://naveropenapi.apigw.ntruss.com'. Using the wrong base URL returns a 404 or auth error with no indication of the actual problem.
fix Search/Blog/News/Login → openapi.naver.com. Papago/Clova/OCR → naveropenapi.apigw.ntruss.com. Check each service's API guide for the exact endpoint.
gotcha Daily quota resets at 00:00 KST (Korea Standard Time, UTC+9), not UTC. If you're in UTC or UTC-8, your quota resets at 15:00 or 08:00 UTC respectively — not midnight local time. Rate limit errors return HTTP 429.
fix Plan quota-sensitive workloads around KST midnight. Monitor usage via the Naver Cloud Platform console which displays usage in KST.
gotcha Registering an application in the console does not automatically enable all APIs. Each service must be explicitly selected/enabled per application via the console Edit Application flow. Calling an API that hasn't been enabled for the application returns HTTP 429 (Quota Exceed) — not a 403 or 401 — which makes the error appear to be a rate limit issue rather than a misconfiguration.
fix After registering an app, go to Edit Application and explicitly select each API service you intend to use. If you receive 429 immediately on a fresh app with no prior calls, this is the cause.
gotcha No official Python SDK exists. Community packages on PyPI (napit, NaverTTS) are unmaintained and target old API versions. LLM-generated code often invents a 'naver' or 'naver-sdk' package that does not exist.
fix Use raw requests. The API is simple enough that a direct HTTP wrapper is 10-20 lines. Reference the official API guide at api.ncloud-docs.com for exact endpoint paths and parameters.
gotcha Search API pagination is limited: maximum 1000 results total (start + display ≤ 1001), maximum 100 results per page. Attempting to paginate beyond this returns an error. There is no cursor-based pagination.
fix For large result sets, use multiple queries with different sort orders (sim/date) or date range filters to work around the 1000-result cap.
gotcha Naver Open API registration, console UI, and official documentation are primarily in Korean. The English documentation at api.ncloud-docs.com is a translation that lags behind the Korean source and may be incomplete for newer services.
fix Use the Korean documentation at developers.naver.com as the source of truth for the most current API specs. Use DeepL or Papago itself to translate if needed.
gotcha Naver Search API returns Korean-language content by default. The API does not filter by language — all results are from Naver's Korean index. Non-Korean queries return results but content will be predominantly Korean.
fix For Korean market research and Korean-language content, this is the correct tool. For multilingual or global search, use a different API.
breaking API client credentials (e.g., NAVER_CLIENT_ID, NAVER_CLIENT_SECRET) must be provided as environment variables for authentication. Failure to set these variables will result in a KeyError during application startup.
fix Set the required API client credentials (e.g., `NAVER_CLIENT_ID` and `NAVER_CLIENT_SECRET`) as environment variables in your deployment environment or shell before running the application.
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.54s 70.4M
3.10 slim (glibc) - - 0.38s 142M
3.11 alpine (musl) - - 0.74s 77.1M
3.11 slim (glibc) - - 0.58s 148M
3.12 alpine (musl) - - 0.64s 67.6M
3.12 slim (glibc) - - 0.65s 139M
3.13 alpine (musl) - - 0.63s 64.1M
3.13 slim (glibc) - - 0.61s 137M
3.9 alpine (musl) - - 0.50s 69.3M
3.9 slim (glibc) - - 0.40s 141M

Two different base URLs are used depending on the service: 'openapi.naver.com' for Search and Login APIs, 'naveropenapi.apigw.ntruss.com' for AI services (Papago, Clova). Using the wrong base URL returns a 404 with no helpful error message. Daily quota resets at midnight KST (UTC+9), not UTC.

import os
import requests

CLIENT_ID = os.environ['NAVER_CLIENT_ID']        # X-NCP-APIGW-API-KEY-ID
CLIENT_SECRET = os.environ['NAVER_CLIENT_SECRET']  # X-NCP-APIGW-API-KEY

headers = {
    'X-NCP-APIGW-API-KEY-ID': CLIENT_ID,
    'X-NCP-APIGW-API-KEY': CLIENT_SECRET,
}

# --- Search API (blog, news, web, image, shopping, book) ---
# Base URL: https://openapi.naver.com/v1/search/{type}.json
def search_blog(query: str, display: int = 10, start: int = 1):
    url = 'https://openapi.naver.com/v1/search/blog.json'
    params = {
        'query': query,
        'display': display,  # 1-100, default 10
        'start': start,      # 1-1000
        'sort': 'sim',       # 'sim' (relevance) or 'date'
    }
    response = requests.get(url, headers=headers, params=params)
    response.raise_for_status()
    return response.json()

def search_news(query: str):
    url = 'https://openapi.naver.com/v1/search/news.json'
    params = {'query': query, 'display': 10, 'sort': 'date'}
    response = requests.get(url, headers=headers, params=params)
    response.raise_for_status()
    return response.json()

# --- Papago Translation ---
# Supported language pairs: ko<->en, ko<->zh-CN, ko<->zh-TW, ko<->ja, ko<->es, ko<->fr, etc.
def translate(text: str, source: str = 'ko', target: str = 'en'):
    url = 'https://naveropenapi.apigw.ntruss.com/nmt/v1/translation'
    # NOTE: Papago uses different base URL than Search API
    data = {
        'source': source,
        'target': target,
        'text': text,
    }
    response = requests.post(url, headers=headers, data=data)
    response.raise_for_status()
    return response.json()['message']['result']['translatedText']

# --- Clova OCR ---
def ocr_image(image_url: str):
    url = 'https://naveropenapi.apigw.ntruss.com/ocr/v1/recognize'
    data = {
        'image': image_url,
        'lang': 'ko',
        'type': 'url',
    }
    response = requests.post(url, headers=headers, data=data)
    response.raise_for_status()
    return response.json()

# --- Naver Login (OAuth 2.0) ---
# Step 1: Redirect user to Naver auth URL
import urllib.parse

def get_naver_login_url(state: str) -> str:
    params = {
        'response_type': 'code',
        'client_id': CLIENT_ID,
        'redirect_uri': 'https://yourdomain.com/callback',
        'state': state,
    }
    return 'https://nid.naver.com/oauth2.0/authorize?' + urllib.parse.urlencode(params)

# Step 2: Exchange code for token
def get_naver_token(code: str, state: str) -> dict:
    url = 'https://nid.naver.com/oauth2.0/token'
    params = {
        'grant_type': 'authorization_code',
        'client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'code': code,
        'state': state,
    }
    response = requests.get(url, params=params)
    return response.json()

# Step 3: Get user profile
def get_naver_profile(access_token: str) -> dict:
    url = 'https://openapi.naver.com/v1/nid/me'
    headers_auth = {'Authorization': f'Bearer {access_token}'}
    response = requests.get(url, headers=headers_auth)
    return response.json()