> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livingsecurity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 요청 제한

> Living Security Public API의 요청 한도, 429 응답 규약, 그리고 Retry-After를 사용한 재시도 방법을 안내합니다.

`/v1` API는 서로 독립적인 두 가지 요청 한도를 적용합니다. 둘 중 하나라도
소진되면 요청이 거부됩니다.

| 한도 | 적용 대상 |
| - | - |
| API 키별 | 해당 키를 제시하는 모든 요청(요청 발신 주소와 무관) |
| 클라이언트 IP별 | 해당 주소에서 발생하는 모든 요청(인증 여부와 무관) |

API 키별 한도는 키가 사용되는 위치와 관계없이 해당 키를 따라갑니다. IP별 한도는
인증되지 않은 트래픽에 적용되는 유일한 한도이며, 인증된 트래픽에는 두 번째 상한으로
적용됩니다.

<Note>
  제한은 인증 이전에 적용되므로, 유효하지 않거나 누락된 자격 증명으로 보낸 요청도
  요청 발신 측의 한도에 포함됩니다. 구체적인 한도 수치는 운영자가 설정하며 변경될 수 있습니다.
  아래에 설명된 응답 규약이 안정적인 인터페이스이므로, 이를 기준으로 구현하시기 바랍니다.
</Note>

두 가지 Basic 자격 증명 형식 중 하나로 인증합니다. 자세한 내용은
[빠른 시작](/ko/developers/v1/quickstart)(`apikey:secret` 또는 `secret:`)을 참조하십시오.
`BASE`를 조직의 리전 호스트로 설정합니다. 자세한 내용은
[리전](/ko/developers/v1/regions)을 참조하십시오.

## 429 응답

한도가 소진되면 API는 `429 Too Many Requests` 응답과 함께 `Retry-After` 헤더(초 단위)
및 표준 problem-details 본문을 반환합니다.

```json 429 Too Many Requests theme={null}
{
  "type": "https://docs.livingsecurity.com/developers/v1/errors/rate_limited",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Retry after the Retry-After interval.",
  "code": "rate_limited",
  "request_id": "req_...",
  "doc_url": "https://docs.livingsecurity.com/developers/v1/errors/rate_limited",
  "retryable": true
}
```

응답에는 어느 한도가 소진되었는지가 명시되지 않습니다. 모든 `429` 응답을
동일하게 처리하십시오. 즉, 대기한 후 재시도하십시오.

## 429 처리

<Steps>
  <Step title="Retry-After 준수">
    재시도하기 전에 `Retry-After` 헤더에 명시된 시간(초) 이상 대기하십시오.
    더 일찍 재시도하면 어차피 거부될 요청으로 한도만 다시 소모하게 됩니다.
  </Step>

  <Step title="반복 시 지수 백오프 적용">
    재시도가 다시 거부되면 백오프 시간을 늘리고 무작위 지터(jitter)를 추가하여
    병렬 작업자들이 동시에 재시도하지 않도록 하십시오. 로컬 상한에 맞추기 위해 서버가 지정한 `Retry-After` 값을 줄여서는 안 됩니다. 서버가
    요청한 대기 시간이 재시도 허용 범위를 초과하는 경우에는 재시도를 중단하고
    나중에 작업을 다시 예약하십시오.
  </Step>

  <Step title="요청 속도 평탄화">
    요청이 몰리는 버스트보다는 일정한 요청 속도를 유지하십시오. 대량 조회 시에는
    최대 페이지 크기(`limit=500`)를 사용하여 더 적은 요청으로 컬렉션 전체를 조회하십시오.
    자세한 내용은 [페이지 매김](/ko/developers/v1/pagination)을 참조하십시오. 여러 목록 순회를
    병렬로 실행하는 경우(예: Power BI 테이블) 동시성은 적절한 수준으로 유지하고
    `Retry-After`를 준수하여, 일시적인 429 응답으로 전체 새로 고침이 실패하지 않도록 하십시오.
  </Step>
</Steps>

이 예제는 **GET** 요청에 대해 `429` 응답만 재시도하며, 최대 5회까지 시도하고
시도 간 대기 시간은 최대 60초입니다. 그 외의 오류가 발생하면 즉시 중단합니다.
네트워크 타임아웃은 호출자에게 전파됩니다. 타임아웃과 재시도 한도는 사용자의 워크로드에 맞게
구성하십시오. `requests`는 읽기 타임아웃을 전체 제한 시간이 아니라 데이터를 수신하는 동안의 최대 유휴 시간으로
해석합니다.

`LIVING_SECURITY_API_KEY`는 시크릿 관리자에서 불러오십시오. 이 예제를 실행하기 전에 `requests`를
설치하십시오. 사용하려면 `get_with_retry("/organization")`를 호출하십시오.

```python Python theme={null}
import os
import random
import time
import requests

# US: BASE = "https://api.us-east-1.livingsecurity.ai/v1"
BASE = "https://api.eu-west-1.livingsecurity.ai/v1"
AUTH = ("apikey", os.environ["LIVING_SECURITY_API_KEY"])

def get_with_retry(path, params=None, max_attempts=5):
    if not 1 <= max_attempts <= 5:
        raise ValueError("max_attempts must be between 1 and 5")
    for attempt in range(max_attempts):
        response = requests.get(
            f"{BASE}{path}", auth=AUTH, params=params,
            headers={"Living-Security-Version": "2026-07-01"},
            timeout=(5, 30), allow_redirects=False,
        )
        if response.status_code != 429:
            response.raise_for_status()
            if not 200 <= response.status_code < 300:
                raise RuntimeError("Unexpected redirect; check your regional API URL")
            return response

        # The API returns Retry-After as a whole number of seconds.
        raw_wait = response.headers.get("Retry-After", "")
        request_id = response.headers.get("Request-Id", "unknown")
        response.close()
        if attempt == max_attempts - 1:
            raise RuntimeError(f"Retry budget exhausted; request_id={request_id}")
        if not raw_wait.isascii() or not raw_wait.isdigit() or len(raw_wait) > 6:
            raise RuntimeError(f"Invalid Retry-After; request_id={request_id}")

        wait = max(int(raw_wait), min(2 ** attempt, 30))
        delay = wait + random.uniform(0, 1)
        if delay > 60:
            raise RuntimeError(f"Retry-After exceeds wait budget; request_id={request_id}")
        time.sleep(delay)
```

이 루프를 쓰기 작업에 그대로 복사하여 사용하지 마십시오. 쓰기 이후에 발생한 타임아웃은
연결이 끊어지기 전에 서버가 해당 작업을 이미 커밋했을 수 있음을 의미할 수 있습니다. 해당 작업의 참조 문서에
멱등성이 명시적으로 기재되어 있는 경우에 한하여, 동일한 키와 페이로드를 유지한 채 쓰기를 반복하십시오.
그렇지 않은 경우에는 다시 시도하기 전에 리소스 상태를 확인하여 정합성을 맞추십시오.

## 권장 새로 고침 일정

키별 한도와 페이지 크기를 기준으로 새로 고침 주기를 정하십시오. 대규모 컬렉션의
전체 이력 로드는 한 번만 발생하는 비용입니다. 정상 상태의 새로 고침에는 증분 구간을
사용하여 각 실행이 한도를 충분히 밑돌도록 하십시오. `limit=500`는 항상 명시적으로
설정하십시오. 기본값인 25를 사용하면 요청 수가 크게 늘어납니다.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.