Quickstart V5 (beta)
From finding a bot to downloading data. Finish your first collection in five minutes.
v5 splits resources using the same words as the product screens: bots, settings, plans and datasets. A bot can have several plans, and a dataset tells you whether it was collected manually or by which plan, and with which inputs. Replace only YOUR_API_KEY and the commands below work as they are.
Using v4 today? Start with Moving from v4 to v5. v4 keeps working as it is.
A machine-readable spec is available as OpenAPI (JSON).
Want to try the paths before signing up? Import this OpenAPI address into an API tool such as Postman or Insomnia. Every path is filled in, so after you sign up you only add your key to start calling.
-
Copy your API key
Sign up free and get an API keyCopy the key from My info → API key. It is the same key as v4. Send it in the
Authorizationheader of every request asBearer YOUR_API_KEY.Treat the key like a password. Never expose it in a code repository or a shared screen.
After signing up, your key is under My info → API key. Already have an account? Log in.
-
Check the connection
GETGET /v5/api/healthis a status check that needs no key.GET /v5/api/mereturns the account the key belongs to.Example requestcurl 'https://www.hashscraper.com/v5/api/health'Example response 200{ "result": "success", "version": "v5", "request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21", "elapsed_sec": 0.0123, "data": { "status": "ok", "time": "2026-10-04T09:00:00+09:00" } }Example requestcurl 'https://www.hashscraper.com/v5/api/me' \ -H 'Authorization: Bearer YOUR_API_KEY'Example response 200{ "result": "success", "version": "v5", "request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21", "elapsed_sec": 0.0123, "data": { "email": "dev@example.com", "name": "Sample User" } }If you see
"result": "success"and your email, you are ready. On401, copy the key again. -
Pick a bot
GETList your bots and pick the
idof the one to collect with. You can also copy it from the bot detail screen with More → Copy bot ID.Example requestcurl 'https://www.hashscraper.com/v5/api/bots' \ -H 'Authorization: Bearer YOUR_API_KEY'Example response 200{ "result": "success", "version": "v5", "request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21", "elapsed_sec": 0.0123, "count": 1, "next_cursor": null, "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" } } ] }data[].idis the bot ID. Use it in the path of the next steps. -
Check the inputs
GETSee which inputs the bot takes (keyword, sort and so on). The
key(oralias) of each entry ininputsis the name to use ininputswhen you collect, and for fields withoptionspick one of the listedvalues.Example requestcurl 'https://www.hashscraper.com/v5/api/bots/8f3a7c1e9b5d24f6/inputs' \ -H 'Authorization: Bearer YOUR_API_KEY'Example response 200{ "result": "success", "version": "v5", "request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21", "elapsed_sec": 0.0123, "data": { "bot_id": "8f3a7c1e9b5d24f6", "excel_supported": true, "inputs": [ { "key": "param1", "name": "Keyword", "name_en": "Keyword", "alias": "keyword", "type": "text", "required": true, "options": [], "excel_supported": true } ] } }Each field the bot uses shows its
key,nameandoptions. -
Start a collection
POSTStart a collection on the bot you picked. Starting a collection requires an idempotency key (the
Idempotency-Keyheader). Create a new key for every request (the$(uuidgen)in the example above) and reuse the same key only when you resend after a dropped connection. See Idempotency key for the rules.save_to_settingsdefaults totrue: theinputsyou send are saved to the bot settings and used for this collection. Sendfalseto use them for this one collection only.Example requestcurl -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 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": [] } }data.idis the number of this collection (dataset). Credits are used for each row the collection saves (Credits & rate limits). -
Wait until it finishes
GETCollections run in the background. Check the status every 10 to 30 seconds. Depending on the site and volume it takes from tens of seconds to tens of minutes.
Example requestcurl 'https://www.hashscraper.com/v5/api/datasets/239758028' \ -H 'Authorization: Bearer YOUR_API_KEY'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": "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 } }When
statusisdoneit has finished.waiting,runningandretryingare in progress.failedandcanceledhave ended without collecting everything.attentionneeds a look (finished with a notice, or stalled). -
Download the data
GETDownload the data of the finished dataset. It arrives
limitrows at a time. Pass the responsenext_cursoras thecursorof the next request to continue.Example requestcurl 'https://www.hashscraper.com/v5/api/datasets/239758028/results?limit=1000' \ -H 'Authorization: Bearer YOUR_API_KEY'Example response 200{ "result": "success", "version": "v5", "request_id": "req_5a8c1f1c-9b2d-4e7c-9abf-3f7e0a4d1b21", "elapsed_sec": 0.0123, "count": 1, "next_cursor": null, "data": [ { "id": 91000001, "created_at": "2026-10-04T09:00:40+09:00", "updated_at": "2026-10-04T09:00:40+09:00", "title": "Sneakers A", "price": "59000" } ] }When
next_cursorisnullyou have the last page.
Common rules
- Address — every path lives under
https://www.hashscraper.com/v5/api. - Authentication —
Authorization: Bearer <API key>. Onlyhealthworks without a key. - Time — ISO 8601 in the Korea time zone (
+09:00). - Credits — integers.
- Plan id — always a string (
"512"). Do not compare it as a number. - Request ID — send
X-Request-ID: req_...and it is echoed back, otherwise the server makes one. Include it when you contact us.
| Response field | Description |
|---|---|
result | success or error. Bulk collection, which handles several items at once, can also return partial_success and failure. |
version | Always v5. |
request_id | The request ID. |
elapsed_sec | Seconds taken to build the response. |
data | The result: an array for lists, an object for a single item. Fields always have the same shape; missing values arrive as null or an empty array instead of being left out. |
count, next_cursor | List responses only: how many items arrived and the marker for the next page (null when there is none). |
Paging through lists
Lists arrive limit items at a time (default 100, maximum 1000). Pass the response next_cursor unchanged as the cursor of the next request. null means you are done. Do not change conditions such as sorting while paging.
The whole flow at once (Python · Node.js)
Steps 5–7 above (start a collection → wait until it ends → download the data) joined into one script. Replace YOUR_API_KEY and the bot ID, and change inputs to the input fields of that bot from step 4 (GET /bots/{bot_id}/inputs) — the example param1: "sneakers" fits a bot with one keyword field. This example sends save_to_settings: false, so the bot settings stay as they are. A bot that cannot run with one-off inputs answers 422 PLAN_CONDITION_BLOCKED; in that case send without that field.
For when and how many credits are used, and how many calls you can make per minute, see Credits & rate limits.
import time
import uuid
import requests
BASE = "https://www.hashscraper.com/v5/api"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
BOT_ID = "8f3a7c1e9b5d24f6"
# 1. start a collection (a new Idempotency-Key for every new request)
res = requests.post(
f"{BASE}/bots/{BOT_ID}/collections",
headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
json={"inputs": {"param1": "sneakers"}, "save_to_settings": False}, # this run only
timeout=30,
)
res.raise_for_status()
dataset_id = res.json()["data"]["id"]
# 2. wait until it ends (check every 20 seconds)
while True:
dataset = requests.get(f"{BASE}/datasets/{dataset_id}", headers=HEADERS, timeout=30).json()["data"]
if dataset["status"] in ("done", "attention", "failed", "canceled"): # attention = ended with a notice, or stalled
break
time.sleep(20)
print(dataset["status"], dataset["data_count"], "rows", dataset["credits_used"], "credits")
# 3. download every row (until next_cursor is null)
rows, cursor = [], None
while True:
params = {"limit": 1000, **({"cursor": cursor} if cursor else {})}
page = requests.get(f"{BASE}/datasets/{dataset_id}/results", headers=HEADERS, params=params, timeout=60).json()
rows.extend(page["data"])
cursor = page["next_cursor"]
if cursor is None:
break
print(len(rows), "rows downloaded")
// Node.js 18+ (built-in fetch)
const { randomUUID } = require("node:crypto");
const BASE = "https://www.hashscraper.com/v5/api";
const HEADERS = { Authorization: "Bearer YOUR_API_KEY" };
const BOT_ID = "8f3a7c1e9b5d24f6";
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function call(method, path, { body, query } = {}) {
const url = new URL(BASE + path);
for (const [k, v] of Object.entries(query ?? {})) if (v != null) url.searchParams.set(k, v);
const headers = { ...HEADERS };
if (method !== "GET") headers["Idempotency-Key"] = randomUUID(); // a new key for every new request
if (body) headers["Content-Type"] = "application/json";
const res = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : undefined });
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
return res.json();
}
async function main() {
// 1. start a collection
const started = await call("POST", `/bots/${BOT_ID}/collections`, {
body: { inputs: { param1: "sneakers" }, save_to_settings: false }, // this run only
});
const datasetId = started.data.id;
// 2. wait until it ends (check every 20 seconds)
let dataset;
for (;;) {
dataset = (await call("GET", `/datasets/${datasetId}`)).data;
if (["done", "attention", "failed", "canceled"].includes(dataset.status)) break; // attention = ended with a notice, or stalled
await sleep(20000);
}
console.log(dataset.status, dataset.data_count, "rows", dataset.credits_used, "credits");
// 3. download every row (until next_cursor is null)
const rows = [];
let cursor = null;
do {
const page = await call("GET", `/datasets/${datasetId}/results`, { query: { limit: 1000, cursor } });
rows.push(...page.data);
cursor = page.next_cursor;
} while (cursor !== null);
console.log(rows.length, "rows downloaded");
}
main().catch((err) => {
console.error(err.message);
process.exitCode = 1;
});