HumanbasedDocs
Developer GuidesData and integrations

API Reference

Reference for the Humanbased data API endpoints currently documented for external integrations.

Response format

Every endpoint wraps its payload in a StandardResponse envelope:

FieldTypeDescription
dataobject | array | nullThe actual response payload (shape varies per endpoint). null on error.
successbooltrue on 2xx, false on any 4xx/5xx.
errorCodeint0 on success, otherwise an HTTP-derived code.
errorMessagestring"SUCCESS" on success, otherwise a short failure reason.

Below, every endpoint shows the complete response body — envelope and all — so you can paste it straight into a client mock or contract test.

Paginated endpoints (e.g. /v1/live/pull) put their array inside data.items and the pagination cursors at data.next_cursor, data.has_more, data.count. Single-list endpoints (e.g. /v1/frontiers) put their array straight at data — no inner key, no data.data double-wrap.


Public endpoints

No authentication required.

List Frontiers

GET /v1/frontiers?status=online

Browse available data sources from production. Returns frontiers with task and submission counts.

ParamTypeRequiredDefaultDescription
statusstringnoonlineonline for live sources, all to include historical

Response:

{
  "data": [
    {
      "domain_id": "8114254168500106625",
      "title": "CEX Hot Wallet",
      "status": "ONLINE",
      "task_count": 31,
      "total_submissions": 812878
    }
  ],
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

domain_id is the frontier's snowflake — pass it as the domain_id query param on /v1/frontiers/tasks to drill into the tasks.

List Frontier Tasks

GET /v1/frontiers/tasks?domain_id=<id>

List tasks under a frontier with submission counts.

ParamTypeRequiredDescription
domain_idstringyesFrontier snowflake from /v1/frontiers

Response:

{
  "data": [
    {
      "task_id": "8495394906500108169",
      "domain_id": "8114254168500106625",
      "name": "Collect deposit address images from exchanges for week10.",
      "task_type": "submission",
      "status": "PAUSE",
      "submission_count": 70944
    }
  ],
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

status is one of COLLECTING / PAUSE / COMPLETED. task_type reflects the contributor flow — typically submission for new data collection.

List Verticals (Simulated)

GET /v1/data/verticals

Returns all available simulated data verticals.

Response:

{
  "data": [
    {
      "id": "afbb8950-4937-4549-a982-907b9c839baf",
      "slug": "crypto_account_annotation",
      "name": "Crypto Account Annotation",
      "description": "Blockchain address entity classification across 35+ networks.",
      "base_price_usd": 10.0
    }
  ],
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

base_price_usd is per-record pricing and varies per vertical (e.g. fashion annotation is 0.025, crypto account annotation is 10.0). Treat the example as illustrative — read the live value from this endpoint before billing.


Portal-authenticated endpoints

Preview Task Submissions

GET /v1/frontiers/tasks/preview?domain_id=<id>&task_id=<id>&limit=5

Preview sample adopted submissions for a task. Requires JWT authentication (dashboard).

ParamTypeRequiredDefaultDescription
domain_idstringyesFrontier snowflake
task_idstringyesTask snowflake
limitintno5Max items (max 20)

Response:

{
  "data": [
    {
      "submission_id": "123456789",
      "data": { "address": "0x...", "chain": "ethereum" },
      "quality_score": 0.97,
      "quality_grade": "S",
      "source": "codatta-app",
      "created_at": "2026-03-28T10:00:00",
      "audit_rating": 5,
      "audit_reason": null
    }
  ],
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

Note: preview mirrors /v1/live/pull's shape — data is a structured object on both endpoints (the pre-2026-07-16 hotfix /v1/live/pull returned data as a JSON string; that was changed at the same time this section was written and both endpoints now behave identically here).


Production data endpoints

All require Authorization: Bearer hb_live_sk_... header. These endpoints read from the production database via frontier-based subscriptions.

Pull Live Data

GET /v1/live/pull?subscription_id=<id>&limit=50
GET /v1/live/pull?subscription_id=<id>&limit=50&from=beginning&cursor=<next_cursor>&task_ids=<csv>&campaign_ids=<csv>

Read the submissions delivered to a frontier subscription, at most 200 per call. The endpoint has two modes.

Incremental pull (default — no from). The server keeps a read position per subscription. Each call returns the records not yet delivered to you and advances that position. Send no cursor; call again while has_more is true. When has_more is false you are caught up — call again later to pick up new records. An incremental page may carry a next_cursor; you never need to send it back.

Full pull (from=beginning). Returns every delivered record from the first, including records above the read position. The read position does not move, so a full pull never disturbs your incremental pulls. Page with cursor=<next_cursor> — an opaque hbrp_… token. Stop when has_more is false. Do not stop on next_cursor alone — has_more is the authoritative signal.

Both modes return at most limit records per call (default 50, maximum 200).

# Incremental pull — no cursor; repeat while has_more is true
curl -G https://api.humanbased.ai/v1/live/pull \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d subscription_id=YOUR_SUBSCRIPTION_ID \
  -d limit=200

# Full pull — first page
curl -G https://api.humanbased.ai/v1/live/pull \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d subscription_id=YOUR_SUBSCRIPTION_ID \
  -d from=beginning \
  -d limit=200

# Full pull — next page: pass the previous response's next_cursor
curl -G https://api.humanbased.ai/v1/live/pull \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d subscription_id=YOUR_SUBSCRIPTION_ID \
  -d from=beginning \
  -d cursor=NEXT_CURSOR_FROM_PREVIOUS_PAGE \
  -d limit=200
ParamTypeRequiredDefaultDescription
subscription_idstringyesFrontier subscription ID
limitintno50Records per page. Maximum 200; a larger value is rejected with 422.
fromstringnoThe only accepted value is beginning, which selects a full pull. Omit it for an incremental pull. Any other value is rejected with 400.
cursorstringnoOpaque paging token copied from the previous page's next_cursor (hbrp_…). Only meaningful with from=beginning; an incremental pull needs no cursor.
task_idsstring (CSV)noComma-separated task IDs. Narrows the result to submissions whose task_id is in the list. Full pull only — on an incremental pull the request is rejected with 400 filters_need_reread. Empty value (task_ids=) or all-empty tokens (,,,) is treated as "no filter". Capped at 200 IDs.
campaign_idsstring (CSV)noComma-separated campaign IDs. Narrows the result to submissions whose campaign_id is in the list. Same full-pull-only, empty-value and 200-cap rules as task_ids.

Both task_ids and campaign_ids intersect with the subscription's own scope — they narrow, never widen. A filter value that isn't part of your subscription returns an empty page rather than a 403.

Note for integrations written before 2026-09-08: cursor=0 used to mean "from the start". It is now an ordinary incremental pull — it returns the records after the subscription's read position, not the whole history. Use from=beginning for a full pull.

Response (a full-pull page is shown; an incremental page has the same shape):

{
  "data": {
    "items": [
      {
        "submission_id": "2026050121475400102454",
        "task_id": "9573999127600106018",
        "campaign_id": "8114254168500106625",
        "template_id": "AIRDROP_CEX_HOT_WALLET_WITHDRAW",
        "data": {
          "coin": "BSC-USDT",
          "type": "withdrawal",
          "amount": "29",
          "address": "0xe94e…",
          "network": "BNB",
          "tx_hash": "0x52c5…",
          "exchange_name": "Binance",
          "transaction_date": "2026-04-03"
        },
        "quality_score": 0.97,
        "quality_grade": "S",
        "unit_price_usd": 0.0,
        "source": "mobile",
        "created_at": "2026-05-01T21:47:55+00:00"
      }
    ],
    "next_cursor": "hbrp_MTIzNDU2Nzg5",
    "has_more": true,
    "count": 50
  },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}
FieldTypeNotes
submission_idstringSnowflake. Unique per submission — the key to dedupe on.
task_idstringSnowflake. Same task can appear across many submissions.
campaign_idstringSnowflake. The campaign (a.k.a. frontier — same identifier space) this submission belongs to. Renamed from frontier_id in the 2026-07-16 hotfix so the response field name matches the DB column.
template_idstringThe template_id of the form this submission was collected against. Empty string for rows collected before templateId was tracked; use List Subscription Templates to enumerate what your subscription can deliver.
dataobjectStructured payload — the fields the annotator filled in. Shape is template-specific (see below). Prior to the 2026-07-16 hotfix this was a JSON-encoded string and included taskId / templateId / device / channel / repeat_attrs as sibling keys; those siblings have been lifted to top-level fields or dropped.
quality_scorefloat0.0 – 1.0. Adoption threshold is set per-subscription.
quality_gradestringS / A / B / C. Coarse band derived from quality_score.
unit_price_usdfloatUnit value recorded on the delivery row; 0.0 when none is set.
sourcestringweb / mobile / import / codatta-app. How the contributor submitted.
created_atstringISO-8601 (UTC). The contributor's submission time. Pages are ordered by internal delivery order, not by created_at, so an incremental pull can return records whose created_at is older than ones you already hold. Dedupe on submission_id, not on time.

The data object's shape is template-specific — its fields follow whichever template_id the task is bound to. Common templates include AIRDROP_CEX_HOT_WALLET_WITHDRAW, AIRDROP_CEX_HOT_WALLET_DEPOSIT, CEX_HOT_WALLET. Treat unknown fields as opaque; new fields can appear without breaking the envelope. Fetch the field contract for a given template_id via GET /v1/live/task/template?template_id=….

Migration note (2026-07-16): if you were calling this endpoint before this date:

  • data used to be a JSON-encoded string; it's now an object. Drop the JSON.parse() step.
  • template_id used to live inside that string (as templateId); it's now a top-level field.
  • frontier_id was renamed to campaign_id (same value, name matches the DB column now). The frontier_ids query param — added in an earlier draft of this hotfix — is likewise campaign_ids.
  • The device, channel, and repeat_attrs sibling fields inside the old string are no longer surfaced by this endpoint; use GET /v1/live/task/template for the template's repeat_attrs, and treat source as the modern equivalent of device.

Adopt Live Submission

POST /v1/live/items/adopt

Record adopt feedback for a production submission.

Request body:

{ "submission_id": "123456789", "subscription_id": "uuid" }

Response:

{
  "data": { "ok": true, "submission_id": "123456789", "feedback": "adopt" },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

Dispute Live Submission

POST /v1/live/items/dispute

Record dispute feedback with optional reason.

Request body:

{ "submission_id": "123456789", "subscription_id": "uuid", "reason": "Incorrect label" }

Response:

{
  "data": { "ok": true, "submission_id": "123456789", "feedback": "dispute" },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

Sandbox data endpoints

All require Authorization: Bearer hb_live_sk_... header. These endpoints work with simulated vertical-based subscriptions.

Pull Data

GET /v1/data/pull?subscription_id=<id>&limit=50

Pull pending delivery items for a simulated subscription.

ParamTypeRequiredDefaultDescription
subscription_idstringyesThe subscription to pull from
limitintno50Max items (max 200)

Response:

{
  "data": {
    "items": [
      {
        "id": "uuid",
        "vertical_id": "uuid",
        "topic_id": "uuid",
        "payload": { "address": "0x...", "chain": "ethereum", "label": "Uniswap V3" },
        "quality_score": 0.94,
        "quality_method": "consensus",
        "validator_count": 5,
        "consensus_ratio": 0.92,
        "unit_price_usd": 0.032,
        "created_at": "2026-03-25T10:00:00Z"
      }
    ],
    "count": 1,
    "next_cursor": 50
  },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

next_cursor is an integer offset (not a snowflake like /v1/live/pull). Pass it back as the cursor query param to fetch the next page; null means no more items.

Adopt Item

POST /v1/data/items/adopt

Mark a delivery item as adopted (accepted). You are charged unit_price_usd.

Request body:

{ "item_id": "uuid" }

Response:

{
  "data": { "ok": true, "item_id": "uuid", "status": "adopted" },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

Dispute Item

POST /v1/data/items/dispute

Mark a delivery item as disputed (rejected). No charge — item is re-reviewed.

Request body:

{ "item_id": "uuid" }

Response:

{
  "data": { "ok": true, "item_id": "uuid", "status": "disputed" },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

Verify API Key

GET /v1/auth/verifyKey

Validate an API key without burning a pull-quota call. Returns the key's metadata (org binding, name, allowed subscription IDs).

Response:

{
  "data": {
    "key_id": "uuid",
    "org_id": "snowflake",
    "key_name": "humanbased-production-ingestion",
    "subscription_ids": ["uuid", "uuid"]
  },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

On this page