커서가 빌 때까지 관측값 엔드포인트를 페이지별로 순회하세요. 2025년 11월 3일부터 2026년 10월 23일까지의 USD/IDR 일별 환율은 249일치이며, 페이지당 100행이면 요청 3번으로 가져올 수 있습니다. 모든 계정이 시계열의 시작점까지 거슬러 올라갈 수 있습니다.
API로 일괄 수집하기
관측값 엔드포인트는 날짜 범위, 페이지 한도, 커서를 받습니다. 환율은 /v1/fx/rates/{base}/observations, CPI는 /v1/economics/{country}/cpi/observations, 원자재 바는 /v1/commodities/spot/{commodity}/{currency}/bars를 사용합니다. 날짜 파라미터는 엔드포인트에 따라 start와 end 또는 start_date와 end_date입니다. 각 응답에는 next_cursor가 있으며, 마지막 페이지에서는 null입니다.
- API 키 설정 무료 API 키를 받아 환경 변수
NARWHAL_API_KEY에 저장하세요. 스크립트는 이 변수를 읽고 키를Authorization: Bearer YOUR_KEY로 지정해https://api.narwhalapi.com으로 보냅니다. - 시계열과 날짜 범위 지정하기 이 예시는
limit을 100으로 설정해 2025년 11월 1일부터 2026년 10월 23일까지의 USD/IDR을 요청합니다. 직전 응답에서 받은 커서를 보낼 때도 모든 페이지에서 같은 필터를 유지하세요. - 각 페이지를 파일에 저장하기 스크립트로 각 관측값을 JSON 한 줄로
usd_idr.jsonl에 추가하세요.next_cursor에 값이 있으면 다음 요청을 보내고, 값이 비면 멈춥니다.import json, os import requests H = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"} params = { "currencies": "IDR", "start_date": "2025-11-01", "end_date": "2026-10-23", "limit": 100, } rows, pages, cursor = 0, 0, None with open("usd_idr.jsonl", "a") as out: while True: if cursor: params["cursor"] = ( cursor # the cursor carries the filters; resend them unchanged ) r = requests.get( "https://api.narwhalapi.com/v1/fx/rates/USD/observations", params=params, headers=H, timeout=15, ) r.raise_for_status() body = r.json() for day in body["observations"]: out.write(json.dumps(day) + "\n") rows += 1 pages += 1 cursor = body.get("next_cursor") if not cursor: break print(pages, "pages,", rows, "days") - 반환된 날짜 확인하기 2025년 11월 1일부터 2026년 10월 23일까지의 범위에서는 스크립트가
3 pages, 249 days를 출력합니다. 응답이 요청한 시작일보다 늦게 시작할 수 있으므로 마지막 날짜뿐 아니라 처음 저장된 날짜도 확인하세요.
스크립트는 페이지 수와 일수를 따로 셉니다. 다음 페이지를 요청하기 전에 각 관측값을 먼저 기록하고, 커서 루프가 끝난 뒤 합계를 출력합니다. 파일은 추가 모드로 열리며 저장된 각 줄에는 관측값이 하나씩 들어 있습니다. 요청은 USD를 기준 통화로 하는 IDR 시계열을 읽습니다.
각 페이지는 크레딧에서 호출 1회로 계산되며, 무료 크레딧 계정은 분당 50건의 요청 한도가 있습니다. 여러 시계열을 일괄 수집할 때는 각 시계열이 몇 페이지만 필요하더라도 분당 한도를 넘지 않도록 페이지 사이에 여유를 두어야 합니다.
응답에는 X-RateLimit-Remaining이 포함됩니다. 페이지 요청이 429 rate_limit_exceeded를 반환하면 다음 페이지를 요청하기 전에 Retry-After 값만큼 기다리세요. 402 insufficient_credits 응답은 잔액을 모두 썼다는 뜻이므로 계속하기 전에 크레딧을 충전하세요. 오류는 application/problem+json 형식이며 status, code, detail, request_id를 포함하므로, 일괄 수집을 계속할 수 있는지는 오류 코드를 읽고 판단하세요.
시트나 노코드 워크플로 사용하기
수백 행 정도라면 Sheets로 가져와도 충분합니다. 한 번만 하는 일괄 수집이라면 페이지를 순회하는 워크플로보다 스크립트가 더 간단합니다. 관측값을 도착하는 대로 기록하고 싶다면 파일 방식을 쓰세요. Python 예시는 일괄 수집 내내 행을 추가합니다.
n8n, Make, Zapier도 커서로 페이지를 순회할 수 있습니다. 워크플로는 커서를 보낼 때마다 같은 날짜와 시계열 필터를 함께 보내고, next_cursor가 null이 되면 멈춰야 합니다. 지속적인 피드는 일괄 수집으로 시작해 매일 데이터 가져오기로 이어 갈 수 있습니다.
일괄 수집 방식 선택하기
| 상황 | 적합한 방법 | 이유 |
|---|---|---|
| 한 시계열의 1년치를 한 번만 수집 | 스크립트 | 범위를 페이지별로 순회하며 행을 파일에 추가합니다 |
| 여러 시계열 | 시계열을 순회하고 페이지 사이에 대기 시간 두기 | 대기 시간 덕분에 요청이 분당 한도 안에 머뭅니다 |
| 지속적인 피드 | 한 번 일괄 수집한 뒤 매일 가져오기 | 일괄 수집이 매일 가져오기 시작 전의 과거 데이터를 채워 줍니다 |
일괄 수집이 중간에 멈추면 어떻게 하나요?
마지막으로 저장한 날짜부터 다시 시작하세요. 스크립트는 관측값을 받을 때마다 JSON 줄을 추가하므로, 작업이 멈춰도 이미 기록된 행은 파일에 남습니다. 그 저장된 날짜를 범위를 재개할 지점으로 사용하세요.
저장해 둔 커서는 만료될 수 있습니다. 요청이 409 cursor_expired를 반환하면 커서 없이 다시 시작하고 마지막으로 저장한 날짜부터 이어서 진행하세요. 커서에는 해당 범위의 필터가 담겨 있으므로, 그 범위를 페이지별로 순회하는 동안 필터를 바꾸지 마세요.
한 페이지에 몇 행까지 요청할 수 있나요?
limit은 엔드포인트의 최대값까지 설정할 수 있습니다. 환율 관측값은 366, 경제 지표 관측값은 1,000, 원자재 바는 500, 경제 캘린더는 100입니다. 기본값은 100입니다. limit은 페이지 크기만 조절하므로, 더 이상 다음 페이지가 없을 때까지 next_cursor를 계속 따라가야 합니다.
일괄 수집이 요청한 날짜보다 늦게 시작되는 이유는 무엇인가요?
모든 계정이 전체 이력을 조회할 수 있습니다. 반환된 첫 날짜가 시계열이 실제로 시작하는 시점을 알려 주므로, 시작일을 더 이르게 요청해도 더 이른 행이 반환된다는 보장은 없습니다.
계산 예시: 2025년 11월 1일부터 2026년 10월 23일까지의 USD/IDR
시작일을 2025년 11월 1일, 종료일을 2026년 10월 23일, 페이지 한도를 100으로 하여 USD/IDR을 요청합니다. 반환된 커서를 같은 필터로 계속 따라가세요. 이 범위에서 응답은 3페이지에 걸쳐 249행이므로 일괄 수집에는 요청이 3번 듭니다.
첫 행은 2025년 11월 3일 월요일입니다. 2025년 11월 1일과 2일이 주말이기 때문입니다. 2025년 11월 3일의 USD/IDR 환율은 16,710.3이고, 2026년 10월 23일은 18,107.8입니다.
| 항목 | 결과 |
|---|---|
| 요청한 범위 | 2025년 11월 1일부터 2026년 10월 23일 |
| 페이지 한도 | 100행 |
| 이 범위의 페이지 수와 요청 수 | 3페이지, 요청 3회 |
| 이 범위의 행 수 | 249 |
| 첫 관측값 | 2025년 11월 3일: 1 USD당 16,710.3 IDR |
| 마지막 관측값 | 2026년 10월 23일: 1 USD당 18,107.8 IDR |
에 게시되었습니다.