크레딧·요청 한도 V5 (beta)
수집에 크레딧이 언제, 얼마나 쓰이는지와 1분에 보낼 수 있는 요청 수입니다.
API 로 수집해도 화면에서 수집할 때와 같은 규칙으로 크레딧이 쓰입니다. 봇·설정·주기·데이터셋을 읽거나 바꾸는 v5 요청에는 크레딧이 들지 않고, 수집이 데이터를 저장할 때만 쓰입니다.
크레딧이 쓰이는 방식
- 기본은 데이터 한 줄마다 봇의 건당 크레딧. 수집이 저장한 데이터 한 줄마다 그 봇의 건당 크레딧이 빠집니다. 기본 계산으로는 건당 5 크레딧인 봇이 1,280줄을 모으면 6,400 크레딧입니다. 다만 한 번에 많은 줄을 묶어 저장하는 일부 수집 방식에는 배수가 붙을 수 있고, 계약으로 단가를 따로 정한 계정은 그 단가를 씁니다.
- 0건이면 0 크레딧. 데이터를 하나도 저장하지 못한 수집에는 크레딧이 들지 않습니다. 이유는 데이터셋의
empty_reason에 있습니다. - 중복으로 버린 줄은 0. 중복으로 판정돼 저장하지 않은 줄에는 크레딧이 들지 않습니다. 무엇을 중복으로 보는지는 봇의 수집 방식에 따라 다릅니다 — 새 수집에서 같은 내용을 다시 저장하면 다시 쓰일 수 있습니다.
- 시작할 때 잔액을 봅니다. 크레딧이 모자라면 수집이 시작되지 않고
402 CREDIT_EXHAUSTED, 이용권이 끝났으면402 TICKET_EXPIRED입니다. 주기 수집은 그 회차를 건너뛰고 주기 실행 결과에 이유를 남깁니다 — 크레딧 부족은insufficient_credit, 이용권 문제는ticket_expired등. - 실제로 쓴 양은 데이터셋에. 데이터셋에 쓴 크레딧은
credits_used(정수)가 정답입니다. 결과를 이어 쌓는 설정(union)으로 앞 데이터셋에 이어 저장했다면, 이 값은 이번 수집만이 아니라 그 데이터셋에 쌓인 수집 전체의 합입니다.
요청 예시
curl 'https://www.hashscraper.com/v5/api/datasets/239758028' \
-H 'Authorization: Bearer YOUR_API_KEY'
응답 예시 200
{
"result": "success",
"version": "v5",
"request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21",
"elapsed_sec": 0.0123,
"data": {
"id": 239758028,
"bot": {
"id": "8f3a7c1e9b5d24f6",
"name": "Sample shopping bot"
},
"status": "done",
"data_count": 1280,
"union": false,
"trigger": "api",
"credits_used": 6400,
"created_at": "2026-10-04T09:00:02+09:00",
"updated_at": "2026-10-04T09:00:02+09:00",
"plan": null,
"inputs": {
"param1": {
"name": "Keyword",
"value": "sneakers"
}
},
"started_at": "2026-10-04T09:00:02+09:00",
"elapsed_sec": 94
}
}
응답 예시 402
{
"result": "error",
"version": "v5",
"request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21",
"code": "CREDIT_EXHAUSTED",
"message": "Credits exhausted.",
"elapsed_sec": 0.0011
}봇마다 다른 건당 크레딧
건당 크레딧은 봇마다 다릅니다. 목록·댓글처럼 가벼운 데이터는 적게, 상세 정보나 AI 분석이 들어가는 데이터는 많이 듭니다.
가격은 바뀔 수 있어 이 문서에 숫자를 적어 두지 않습니다. 아래 두 곳이 늘 지금 값을 보여 줍니다.
- 봇 소개 — 가입하지 않아도 공개 봇마다 건당 크레딧을 볼 수 있습니다.
GET /v5/api/bots·GET /v5/api/bots/{bot_id}— 응답의credit_per_item(정수)입니다.
요청 예시
curl 'https://www.hashscraper.com/v5/api/bots/8f3a7c1e9b5d24f6' \
-H 'Authorization: Bearer YOUR_API_KEY'
응답 예시 200
{
"result": "success",
"version": "v5",
"request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21",
"elapsed_sec": 0.0123,
"data": {
"id": "8f3a7c1e9b5d24f6",
"name": "Sample shopping bot",
"description": "Collects product lists by keyword.",
"schedule_group": null,
"union": false,
"created_at": "2026-08-12T10:21:05+09:00",
"updated_at": "2026-10-01T18:02:40+09:00",
"worker_source": {
"name": "Shopping list",
"version": 12,
"updated_at": "2026-09-20T11:00:00+09:00",
"price": 5
},
"credit_per_item": 5,
"plans_count": 2,
"active_plans": [
{
"id": "512",
"name": "주기 2",
"label": "매주 월요일 09:00 (KST)"
}
],
"period_summary": "주기 2 · 매주 월요일 09:00 (KST)",
"next_run_at": "2026-10-05T09:00:00+09:00",
"latest_dataset": {
"id": 239758028,
"status": "done",
"data_count": 1280,
"trigger": "period",
"created_at": "2026-09-28T09:00:03+09:00"
}
}
}요청 한도
요청은 IP 주소 하나당 1분에 150번을 기준으로 받습니다. API 키가 아니라 요청을 보내는 곳의 IP 주소로 셉니다 — 같은 서버의 여러 프로그램이 부르면 합쳐서 셉니다.
넘으면 HTTP 429가 옵니다. 본문은 다른 오류와 같은 JSON 이고 code 는 RATE_LIMITED 입니다. 응답 머리 Retry-After 에 다시 보내도 되기까지 남은 초가 들어 있으니, 그만큼 기다렸다 다시 보내세요. 수집 시작 요청을 다시 보낼 때는 같은 재시도 중복 방지 키를 씁니다.
이 숫자는 보장이 아니라 기준입니다. 조금 더 받아질 때도 있지만 거기에 기대지 마세요. 수집 상태 확인은 10~30초 간격이면 충분하고, 많은 봇을 한꺼번에 돌릴 때는 하나씩 부르지 말고 묶음 수집을 쓰세요.
응답 예시 429
{
"result": "error",
"version": "v5",
"request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21",
"code": "RATE_LIMITED",
"message": "Rate limit exceeded.",
"elapsed_sec": 0.0011
}
Python 예시
import time
import uuid
import requests
def request_with_backoff(method, url, headers=None, **kwargs):
headers = dict(headers or {})
if method != "GET":
# one key per request — every retry of the same request reuses it
headers.setdefault("Idempotency-Key", str(uuid.uuid4()))
for attempt in range(8):
res = requests.request(method, url, headers=headers, timeout=30, **kwargs)
if res.status_code != 429 or attempt == 7:
return res
# a 429 carries Retry-After: the seconds left until the one-minute window resets
retry_after = res.headers.get("Retry-After", "")
time.sleep(int(retry_after) if retry_after.isdigit() else min(2 ** attempt, 30))
Node.js 예시
// Node.js 18+ (built-in fetch)
const { randomUUID } = require("node:crypto");
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function requestWithBackoff(method, url, { headers = {}, body } = {}) {
const h = { ...headers };
// one key per request — every retry of the same request reuses it
if (method !== "GET") h["Idempotency-Key"] ??= randomUUID();
if (body) h["Content-Type"] = "application/json";
for (let attempt = 0; ; attempt++) {
const res = await fetch(url, { method, headers: h, body: body ? JSON.stringify(body) : undefined });
if (res.status !== 429 || attempt === 7) return res;
// a 429 carries Retry-After: the seconds left until the one-minute window resets
const wait = Number(res.headers.get("Retry-After")) || Math.min(2 ** attempt, 30);
await sleep(wait * 1000);
}
}