본문 바로가기
빌링플러스개발자센터
홈 로그인 도입 상담

API 규약

목록 · 레이트리밋

커서 페이지네이션과 호출 한도(429), 응답 헤더 읽는 법.

커서 페이지네이션

목록 API 는 페이지 번호(offset) 대신 커서로 다음 페이지를 가져옵니다. 목록을 읽는 사이 새 결제가 들어와도 건너뛰거나 겹치는 항목이 생기지 않습니다.

쿼리뜻
limit한 번에 받을 건수. 기본 20, 최대 100(100보다 크게 보내면 100건만 옵니다)
cursor이전 응답의 page.next_cursor 를 그대로. 첫 페이지는 비웁니다.
GET /api/v1/payments?limit=2
{
  "data": [
    {"id": 8812, "payment_no": "P-2026-1028-0007", "status": "APPROVED", "amount": 29900, "...": "..."},
    {"id": 8809, "payment_no": "P-2026-1028-0004", "status": "FAILED", "amount": 15000, "...": "..."}
  ],
  "page": {"next_cursor": "MTc5MzE0NTYwMC4xMjM0NTZ8ODgwOQ", "has_more": true}
}
  • 정렬은 최신 등록순으로 고정입니다. 정렬을 바꾸는 파라미터는 없습니다.
  • has_more 가 false 면 마지막 페이지입니다. 이때 next_cursor 는 null 입니다.
  • 커서 값의 모양은 바뀔 수 있으니 해석하거나 직접 만들지 마세요. 형식이 깨진 커서는 400 INVALID_REQUEST 입니다.
  • 다음 페이지를 읽을 때도 필터 쿼리는 같게 보냅니다. 쿼리스트링 전체가 서명에 들어갑니다.
Python — 끝까지 읽기
import time
from urllib.parse import urlencode


def all_payments(call, **filters):
    """call 은 '인증 · 요청 서명' 문서의 함수입니다. 최신 결제부터 끝까지 읽습니다."""
    cursor = None
    while True:
        query = {"limit": 100, **filters}
        if cursor:
            query["cursor"] = cursor
        res = call("GET", "/payments?" + urlencode(query))  # 서명도 이 주소(쿼리 포함)로 합니다
        if res.status_code == 429:
            wait = int(res.headers.get("Retry-After", "60"))
            time.sleep(wait)
            continue
        res.raise_for_status()
        body = res.json()
        yield from body["data"]
        if not body["page"]["has_more"]:
            break
        cursor = body["page"]["next_cursor"]

목록별 필터

목록필터 쿼리
GET /api/v1/customersstatus · q(검색어) · external_ref(고객사 회원 ID, 정확히 일치)
GET /api/v1/billing-keyscustomer_id · active_only(기본 true)
GET /api/v1/invoicesstatus · customer_id
GET /api/v1/paymentsstatus · customer_id · invoice_id
GET /api/v1/subscriptionsstatus · customer_id
GET /api/v1/register-windowsstatus(OPEN · USED · EXPIRED) · merchant_ref
GET /api/v1/charge-consentscustomer_id · status(ACTIVE · REVOKED)

계약의 월별 청구 금액 목록(GET /api/v1/subscriptions/{id}/charges)은 회차 수가 정해져 있어 페이지네이션이 없습니다.

레이트리밋(호출 한도)

호출 한도는 가맹점별 분당 횟수입니다. 매 분 정각에 새로 셉니다.

  • 기본 한도는 분당 60회입니다. 가맹점마다 다를 수 있으니 실제 값은 응답 헤더 X-RateLimit-Limit 으로 확인하세요.
  • 결제·계약·등록창·수시결제 동의 경로(/payments · /subscriptions · /register-windows · /charge-consents 로 시작하는 경로, 조회 포함)는 결제사 처리량을 지키기 위해 따로 세는 낮은 한도를 씁니다 — 기본 한도의 4분의 1(최소 5회). 두 한도는 서로의 횟수를 쓰지 않습니다.
  • 빌링플러스가 직접 돌리는 매달 자동결제는 이 한도와 관계없습니다. API 호출이 많아도 정기결제가 밀리지 않습니다.
응답 헤더뜻
X-RateLimit-Limit이 요청에 적용된 분당 한도
X-RateLimit-Remaining이번 분에 남은 횟수
X-RateLimit-Reset한도가 다시 채워지기까지 남은 초
Retry-After429 일 때만. 다시 보내도 되는 시간(초) — X-RateLimit-Reset 과 같은 값
429
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "요청이 너무 많습니다. 분당 15회까지 호출할 수 있습니다.",
    "detail": {"limit": 15, "window_seconds": 60, "retry_after": 23},
    "request_id": "req_0b1c2d3e4f5a6b7c8d9e0f1a"
  }
}

429 를 받은 POST 는 실행되지 않았습니다. Retry-After 초 뒤 같은 멱등키로 다시 보내면 됩니다(서명·nonce 는 새로). 여러 건을 한꺼번에 보낼 때는 X-RateLimit-Remaining 을 보고 속도를 조절하세요.