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

# Python 클라이언트 라이브러리

> 공식 Python SDK octoparse-client.

`octoparse-client`는 Data Hub 공식 Python 클라이언트. 공개 `/v1` REST만 감싸며 의존성은 `httpx`뿐. 메서드 1:1, 파라미터명 REST, 반환은 응답 `data`.

## 설치

```bash theme={null}
pip install octoparse-client
```

## 초기화

```python theme={null}
from octoparse_client import Client

client = Client(api_key="<your API key>")
```

| 매개변수       | 설명                                                                                       |
| ---------- | ---------------------------------------------------------------------------------------- |
| `api_key`  | Data Hub API 키. 생략 시 `OCTOPARSE_API_KEY`. 그래도 없으면 익명으로 익명 엔드포인트만.                        |
| `base_url` | 서비스 URL. 생략 시 `OCTOPARSE_BASE_URL`, 기본 `https://api-datahub.octoparse.com`. 로컬/스테이징은 명시. |
| `timeout`  | 요청당 HTTP 타임아웃(초). 기본 90.                                                                 |

`Client`는 `with`를 지원하며 종료 시 연결을 닫습니다. `client.close()`도 가능. SDK는 `.env`를 읽지 않습니다. 필요하면 직접.

## 메서드-엔드포인트 대응

| 방식                                                                     | 엔드포인트                                     | 비고                           |
| ---------------------------------------------------------------------- | ----------------------------------------- | ---------------------------- |
| `meta()`                                                               | `GET /v1/meta`                            | 플랫폼 상수                       |
| `search(q, *, limit, offset, ...)`                                     | `GET /v1/data-apps`                       | 카드 한 페이지(`pagination` 포함)    |
| `get_app(app_id)`                                                      | `GET /v1/data-apps/{app_id}`              | 전체 App 상세                    |
| `run(app_id, inputs, *, wait, max_records, version, build)`            | `POST /v1/data-apps/{app_id}/runs`        | 실행 시작                        |
| `call(app_id, inputs, *, max_records, timeout, raise_on_failure, ...)` | `POST` + 폴링                               | 블로킹 헬퍼: 최종 상태 또는 타임아웃까지 대기   |
| `get_run(run_id, *, wait)`                                             | `GET /v1/runs/{run_id}`                   | 상태와 과금 스냅샷                   |
| `list_runs_page(filters)`                                              | `GET /v1/runs`                            | 실행 한 페이지(`pagination` 포함)    |
| `list_runs(filters)`                                                   | `GET /v1/runs`                            | 실행 1페이지(items만)              |
| `iterate_runs(filters)`                                                | `GET /v1/runs`                            | 자동 페이지 이터레이터                 |
| `cancel(run_id)`                                                       | `POST /v1/runs/{run_id}/cancel`           | 실행 취소                        |
| `get_records(run_id, *, offset, limit, fields)`                        | `GET /v1/runs/{run_id}/records`           | 결과 레코드 한 페이지                 |
| `iterate_records(run_id, *, batch, fields)`                            | `GET /v1/runs/{run_id}/records`           | 자동 페이지 이터레이터                 |
| `export_records(run_id, format)`                                       | `GET /v1/runs/{run_id}/records`           | `jsonl` / `csv` 텍스트로 전체 내보내기 |
| `list_datasets(*, offset, limit)`                                      | `GET /v1/datasets`                        | 데이터셋 목록                      |
| `set_dataset_retention(dataset_id, retained)`                          | `PUT /v1/datasets/{dataset_id}/retention` | 보존 플래그 설정                    |
| `get_dataset_records(dataset_id, *, offset, limit, fields)`            | `GET /v1/datasets/{dataset_id}/records`   | 데이터셋 레코드 한 페이지               |
| `iterate_dataset_records(dataset_id, *, batch, fields)`                | `GET /v1/datasets/{dataset_id}/records`   | 자동 페이지 이터레이터                 |
| `account()`                                                            | `GET /v1/account`                         | 계정 정보와 누적 지출                 |
| `billing(*, group_by, created_from, created_to, tz_offset)`            | `GET /v1/billing`                         | 청구 집계                        |

실행 목록 `filters`는 `status`, `data_app`, `triggered_by`, `run_kind`, `credential`, `created_from`, `created_to`와 `offset`/`limit`. 게시·운영·시크릿 미래핑 — REST 직접.

## 일반적인 사용

```python theme={null}
from octoparse_client import Client, ApiError, RunFailed

with Client(api_key="<your API key>") as client:
    # Discover
    page = client.search("reviews", limit=5)
    for card in page["items"]:
        print(card["app_id"], card["namespace"], card["app_name"])
    detail = client.get_app("carol/reviews-query")

    # Run and consume. Timeouts are seconds. TimeoutError does not cancel the server-side run.
    run = client.call(
        "carol/reviews-query",
        {"product": "p-9001"},
        max_records=100,
        timeout=120,
        raise_on_failure=True,
    )
    for record in client.iterate_records(run["run_id"]):
        print(record)

    # Non-blocking: start, then poll yourself
    started = client.run("carol/reviews-query", {"product": "p-9002"}, wait=0)
    polled = client.get_run(started["run_id"], wait=60)

    # Reconcile spend
    print(client.billing(group_by="data_app", tz_offset=480))
    for r in client.iterate_runs(created_from="2026-09-01T00:00:00+08:00"):
        print(r["run_id"], r["status"], r["billing"]["total"])

    # Results are kept 90 days by default; mark datasets you need longer
    client.set_dataset_retention(run["dataset_id"], True)
```

## 오류 처리

API 오류는 `ApiError`를 발생. 필드는 REST `error`와 같음:

| 속성            | 의미                                |
| ------------- | --------------------------------- |
| `status_code` | HTTP 상태                           |
| `code`        | 분기용 안정 오류 id                      |
| `category`    | 대분류                               |
| `message`     | 영문 설명                             |
| `retryable`   | 동일 요청 재시도 가능 여부                   |
| `details`     | 입력 검증 실패 시 필드 단위 문제, 그 외에는 `None` |

```python theme={null}
from octoparse_client import ApiError

try:
    client.run("carol/reviews-query", {})
except ApiError as e:
    if e.code == "invalid-input":
        for d in e.details or []:
            print(d["path"], d["message"])
    elif e.retryable:
        # back off and retry
        ...
    else:
        raise
```

`call(..., raise_on_failure=True)`가 `FAILED` / `CANCELLED` / `EXPIRED`로 끝나면 `RunFailed`. `run` 속성에 실행 객체 전체.

## App 참조

`get_app()`, `run()`, `call()`, 실행 목록 `data_app` 필터는 두 형식 허용: 2단 `<namespace>/<app_name>`(가독, 이름 변경 시 깨짐)과 안정 `app_id`(`app_<hex>`, 이름 변경 생존). 장기 연동은 `app_id` 권장.
