Idempotency key V5 (beta)

Keep a collection from running twice, or credits from being charged twice, when you resend after a dropped connection.

If the connection drops while the response to a start request is on its way, your program sees a failure and sends the same request again. The same collection can then run twice and credits can be charged twice. With the Idempotency-Key header you say "this is a resend of the earlier request", and the server returns the first response instead of starting anything new.

A new key for every request

A key is a string of 1 to 255 characters. A UUID is recommended ($(uuidgen)). Responses are kept for 24 hours per (account, key) pair.

Do not use a fixed key. Create a new key for each request (one collection intent) and reuse a key only when you resend the same request after a dropped connection. If you put the same string in every request, collections from the second one on do not start: you either get the first response again (same content) or 422 IDEMPOTENCY_KEY_REUSED (different content).

The six places where it is required

A POST that creates something or uses credits requires the key. Without it you get 400 IDEMPOTENCY_KEY_REQUIRED.

Other requests (changing settings or plans, deleting, reading, preview, copy_settings, cancel, notifications/test) give the same result when sent twice or create nothing, so the key is not required. If you send one, the same rules apply. Every write example in this guide includes a key.

PathWhy
POST /v5/api/bots/{bot_id}/collectionsStarting a collection: credits
POST /v5/api/plans/{plan_id}/runRunning a plan now: credits
POST /v5/api/collections/bulkCollecting several bots: credits
POST /v5/api/collections/bulk_jobsAccepting a bulk job: twice means two jobs, credits
POST /v5/api/bots/{bot_id}/plansCreating a plan: sending twice makes two plans
POST /v5/api/datasets/{dataset_id}/retryRetry: runs the collection again
Example request
curl -X POST 'https://www.hashscraper.com/v5/api/bots/8f3a7c1e9b5d24f6/collections' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  -d '{
  "inputs": {
    "param1": "sneakers"
  }
}'
Example response 400
{
  "result": "error",
  "version": "v5",
  "request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21",
  "code": "IDEMPOTENCY_KEY_REQUIRED",
  "message": "Idempotency-Key header is required.",
  "elapsed_sec": 0.0011,
  "details": {
    "hint": "Send a unique `Idempotency-Key` header (1-255 characters, e.g. a UUID) per request; reuse it only to retry the same request."
  }
}

Behavior

CaseResponse
A key we have not seenProcessed normally, and the response is kept for 24 hours.
Same key, same content (path and body)Nothing new starts; the kept first response is returned unchanged with the header Idempotent-Replayed: true.
Same key, different content422 IDEMPOTENCY_KEY_REUSED: this catches using a fixed key by mistake.
The first request is still being processed409 REQUEST_IN_PROGRESS: send again with the same key shortly.
The first request ended with a 4xx (bad input, not enough credits and so on)The error response is kept too, and the same key gets the same error. After fixing the input or topping up credits, send with a new key.
The first request ended with a 5xx (server error)It is not kept. Sending the same key again is processed anew. If the error happened after the collection was already handed off, that response is kept so the collection is not started again.
Example response 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": "running",
    "data_count": 0,
    "union": false,
    "trigger": "api",
    "credits_used": 0,
    "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": 3,
    "saved_to_settings": true,
    "plans_affected": []
  }
}
Example response 422
{
  "result": "error",
  "version": "v5",
  "request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21",
  "code": "IDEMPOTENCY_KEY_REUSED",
  "message": "Idempotency-Key was already used with a different request.",
  "elapsed_sec": 0.0011
}
Example response 409
{
  "result": "error",
  "version": "v5",
  "request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21",
  "code": "REQUEST_IN_PROGRESS",
  "message": "The first request with this Idempotency-Key is still in progress.",
  "elapsed_sec": 0.0011
}

Retry code example

Create the key once inside the function and reuse that same key inside the retry loop.

Python example
import time
import uuid
import requests

BASE = "https://www.hashscraper.com/v5/api"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}


def start_collection(bot_id, inputs):
    # 이 수집 요청 한 번에 쓸 키 — 요청마다 새로 만들고, 재시도에서만 같은 키를 다시 쓴다
    key = str(uuid.uuid4())
    for attempt in range(5):
        try:
            res = requests.post(
                f"{BASE}/bots/{bot_id}/collections",
                headers={**HEADERS, "Idempotency-Key": key},
                json={"inputs": inputs},
                timeout=30,
            )
        except requests.exceptions.RequestException:
            time.sleep(2 ** attempt)  # 응답을 못 받았다 — 같은 키로 다시 보낸다
            continue
        body = res.json()
        if res.status_code == 409 and body.get("code") == "REQUEST_IN_PROGRESS":
            time.sleep(2)  # 첫 요청이 아직 처리 중 — 잠시 뒤 같은 키로
            continue
        return body
    raise RuntimeError("could not confirm the request")

Related guides