Skip to content
Docs
Download Markdown
In this article

Retrieve delivered records

Read a page of records authorized by an active subscription.

Download Markdown

The same endpoint supports incremental delivery and replay of an already-readable range.

GET /v1/live/pull
Authorization: Bearer <API_KEY>

For a guided first request, see Get your first authorized data.

Hosts and authentication

EnvironmentAPI origin
Staginghttps://api.staging.humanbased.ai
Productionhttps://api.humanbased.ai

Use a full organizationThe Developer-side unit that holds Campaigns, data access, and funds API key issued for the selected environment. Pass it in the Authorization header with the exact Bearer prefix. Do not include credentials in the query string.

The key must be active, unexpired, permit data delivery, and cover the requested subscription. The subscription must belong to the key's organization and be active. A browser session, CLI sign-in session, masked key, or CampaignA developer's data-production project and its settings ID is not a replacement.

Authentication does not expand the data's usage terms. Confirm permitted use separately, including restrictions on sharing, training, retention, or redistribution that apply to the data.

Reading modes and recovery

Choose the mode explicitly before making a request.

ReplayIncremental delivery
RequestInclude from=beginning.Omit from.
PurposeRead or reconstruct the subscription's readable range.Obtain records beyond the shared reading position.
PaginationReturn the latest next_cursor as cursor; preserve mode and filters.Repeat the request while has_more is true; the server holds the position.
Shared positionDoes not move it.Advances it for the whole subscription, including other keys and consumers.
MeteringCreates no new metered pull charge; receipt is null.May charge for newly delivered records according to the subscription.
FiltersOptional task and campaign filters narrow the permitted range.Task and campaign filters are rejected.

For zero-priced subscriptions, replay can include eligible delivered records beyond the incremental reading position. For priced subscriptions, replay stays within the previously acquired range. A full pull means this readable scope; it does not bypass payment or grant access to all campaign submissionsThe record created when work is submitted.

Replay is suitable for recovering a lost local copy. Retrying it does not advance the feed. The endpoint does not promise a frozen snapshot: new deliveries or corrections may affect what is readable over time.

Incremental delivery is stateful even though it uses HTTP GET. Save each complete response before requesting more. If a response is lost, the server may already have advanced the shared position. Another incremental request can move on to the next page. Recover by replaying the acquired range and reconciling records against your local copy. For campaign submissions, use submission_id to identify repeat records; do not use timestamps as cursors.

Query parameters

ParameterTypeRequiredDescription
subscription_idstringYesThe active subscription to read. It is not a Campaign ID.
limitintegerNoMaximum records per page: 1–200. Default: 50.
fromstringNoSet to beginning for replay. Omit for incremental delivery. No other value is supported.
cursorstringNoFor replay, use the previous response's opaque next_cursor. Omit on the first page.
task_idsstringNoComma-separated task IDs; replay only. Narrows the subscription scope.
campaign_idsstringNoComma-separated campaign IDs; replay only. Narrows the subscription scope.

The two filters are applied together when both are present. Each list accepts up to 200 IDs; additional entries are truncated by the current implementation, so split larger requests yourself. Whitespace is trimmed and empty entries are ignored. An entirely empty filter means no filter, not “return nothing”. Validate IDs before sending them.

A filter outside the subscription's scope normally returns an empty page; it never expands authorization. Preserve the same filters on every replay page. Treat cursors as opaque, and never generate one from a TaskA specific item a contributor claims or enters ID, Submission ID, or timestamp.

Example: replay one page

Set HB_API_KEY securely and set HB_SUBSCRIPTION_ID to your own production subscription before running this request:

curl --silent --show-error --get \
  'https://api.humanbased.ai/v1/live/pull' \
  --header "Authorization: Bearer $HB_API_KEY" \
  --data-urlencode "subscription_id=$HB_SUBSCRIPTION_ID" \
  --data-urlencode 'from=beginning' \
  --data-urlencode 'limit=5'

The response below illustrates the wire shape. The identifiers, values, and delivered fields are examples, not downloadable sample data. data.items[].data varies with the delivery's output schema.

{
  "data": {
    "items": [
      {
        "submission_id": "784210006031",
        "task_id": "784210005001",
        "campaign_id": "784210001001",
        "template_id": "784210002001",
        "data": {
          "case_id": "returns-014",
          "decision": "escalate",
          "reason": "The supplied evidence does not establish an exception."
        },
        "quality_score": 0.85,
        "quality_grade": "A",
        "unit_price_usd": 0.02,
        "source": "web",
        "created_at": "2026-10-01T09:30:00+00:00"
      }
    ],
    "next_cursor": null,
    "has_more": false,
    "count": 1,
    "receipt": null
  },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

The item can have a nonzero price while replay's receipt is null. The price describes the subscription's resolved per-record price; it is not proof that this request charged that amount.

Success response

The HTTP response uses a standard envelope. Require success: true before treating data as a successful page.

FieldTypeMeaning
successbooleanWhether the operation succeeded.
errorCodeinteger0 on success.
errorMessagestringSUCCESS on success; diagnostic text on failure.
data.itemsarrayThe returned records. An empty array is a valid result.
data.countintegerNumber of records on this page.
data.has_morebooleanWhether another page was available when this page was read.
data.next_cursorstring or nullContinuation marker. In replay mode, pass it back unchanged while has_more is true.
data.receiptobject or nullMetering result for a priced incremental read; null for replay and reads without a charge.

When a receipt is present, it contains usage_event_id, amount_usd, quantity, and balance_available_usd. Save the receipt with its response. Usage and reconciliation shows how to match it against the organization's usage list.

Record fields

FieldTypeMeaning
submission_idstringSource submission identifier; can be empty when unavailable.
task_idstringSource task identifier; can be empty.
campaign_idstringSource campaign identifier; can be empty.
template_idstringTemplate identifier; empty means unknown.
dataJSON valueDelivered content after any configured output mapping. Do not parse it as a JSON string a second time.
quality_scorenumberScore mapped from the recorded grade; 0 when no recognized grade is present.
quality_gradestringS, A, B, C, or D; empty when no recognized grade is present.
unit_price_usdnumberResolved per-record price for this subscription. It is not a payment receipt.
sourcestringSource label; can be empty.
created_atstringOriginal submission timestamp; can be empty.

Known grades map to scores as follows: S → 0.97, A → 0.85, B → 0.70, C → 0.50, D → 0.30. These are grade mappings, not calibrated probabilities or measurements of downstream model performance.

Records are paged in delivery order. An older created_at in a later page does not by itself indicate a duplicate. IDs are strings; preserve them without numeric conversion. Preserve the source precision of arbitrary numeric values inside data when selecting a JSON parser.

Get the field schema for data

The fields inside each record's data depend on its template_id. Two reads return that contract, so you can decode records without guessing:

GET /v1/live/task/templates?subscription_id=<SUBSCRIPTION_ID>
GET /v1/live/task/template?template_id=<TEMPLATE_ID>
Authorization: Bearer <API_KEY>

The plural form lists every template the subscription's Campaign is configured to deliver, so you can cache them before any record arrives. It returns subscription_id, count, and templates; templates is empty when none is configured. The singular form looks up one template_id met in a record and is not limited to one subscription. Both need a Key with data access: a Key made only for Campaign configuration gets 403. On the plural form, a subscription outside the Key's selected scope also gets 403, and one your organization does not have gets 404.

FieldTypeMeaning
template_idstringMatches template_id in pull records
namestringTemplate name
descriptionstringWhat the template collects
kindstringTemplate kind, form for form-based work
fieldsarrayOne entry per field in data: name, type, required, description, and enum when the value comes from a fixed set

A missing template_id or subscription_id is refused by request validation with 422 and a message naming the parameter, without an errorCodeName. A template_id that is present but empty returns 400 with TEMPLATE_ID_REQUIRED, and an unknown template returns 404 with TEMPLATE_NOT_FOUND.

Usage and reconciliation

GET /v1/live/usage
Authorization: Bearer <API_KEY>

This endpoint lists the usage events your organization has been charged for, newest first. Each priced incremental pull that returns records creates one event, and that event's usage_event_id is the one in the pull's receipt. Reading the list charges nothing and moves no reading position.

Use the same host and Key as GET /v1/live/pull; the Key needs data access. The list covers the whole organization in that environment, including subscriptions outside the Key's own scope. It takes no subscription_id.

ParameterTypeRequiredDescription
limitintegerNoNumber of events to return. Default: 50. Maximum: 200; a larger value answers 422.

The endpoint has no cursor. It returns the newest events and nothing older.

FieldTypeMeaning
data.itemsarrayThe usage events, newest first.
data.countintegerNumber of events in items.
usage_event_idstringThe event's identifier. Equal to receipt.usage_event_id of the pull that was charged.
subscription_idstringThe subscription the pull ran under.
dataset_idstring or nullThe dataset the charge is attributed to; null when the price did not come from a dataset.
vertical_idstring or nullThe subscription's vertical; null when it has none.
quantityintegerRecords served by that pull.
unit_price_usdnumberPrice per record at the time of the pull.
amount_usdnumberAmount charged: unit_price_usd times quantity, rounded to six decimal places.
currencystringUSD.
occurred_atstringWhen the charge was recorded.

The values below are illustrations. A pull response saved with this receipt:

{
  "usage_event_id": "784210009001",
  "amount_usd": 0.1,
  "quantity": 5,
  "balance_available_usd": 49.9
}

matches this event in the usage list:

{
  "data": {
    "items": [
      {
        "usage_event_id": "784210009001",
        "subscription_id": "784210003001",
        "dataset_id": null,
        "vertical_id": "784210004001",
        "quantity": 5,
        "unit_price_usd": 0.02,
        "amount_usd": 0.1,
        "currency": "USD",
        "occurred_at": "2026-10-01T09:31:00+00:00"
      }
    ],
    "count": 1
  },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}

To reconcile, look up each saved receipt's usage_event_id in data.items, then compare quantity and amount_usd.

You findWhat it meansWhat to do now
An event with the receipt's usage_event_idThe charge is recorded once.Check that quantity and amount_usd equal the receipt's.
No such event, and count equals your limitThe event may be older than the events you asked for.Request again with a larger limit, up to 200.
No such event among the 200 newestThe event is older than this endpoint returns.Keep the saved receipt as your record, and review the period's charges on the organization's Billing page.
An event with no saved receiptA pull was charged and its response was not saved, for example because it was lost in transit.Recover the records by replay, as described in Reading modes and recovery.
A saved response with receipt: nullThe read was a replay, or it charged nothing.Nothing to reconcile.

Rate limits

Each endpoint allows a set number of requests per API Key, counted separately for each endpoint. The default is 100 requests per minute. Every response says where you stand:

HeaderMeaning
X-RateLimit-LimitRequests allowed in the current window
X-RateLimit-RemainingRequests left in that window
Retry-AfterOn a 429, seconds to wait before the next request

Over the limit, the gateway answers 429 with errorMessage Too many requests and no errorCodeName. Wait Retry-After seconds, then continue more slowly. A paging loop should pause when X-RateLimit-Remaining reaches 0.

Errors

Use the HTTP status and envelope together. errorCode is numeric. errorCodeName may be present for some structured errors but is not present for every failure. Some detailed errors are JSON serialized inside errorMessage; clients must not assume one universal named-error shape.

{
  "data": null,
  "success": false,
  "errorCode": 401,
  "errorMessage": "Missing API key"
}

A data-API refusal that carries detail puts it in errorMessage as a JSON string, and errorCodeName is absent. For example, a priced read with too little balance:

{
  "data": null,
  "success": false,
  "errorCode": 402,
  "errorMessage": "{\"error\": \"insufficient_balance\", \"available_usd\": 0.0, \"required_usd\": 0.02}"
}

When errorMessage starts with {, parse it and branch on its error field: invalid_cursor and filters_need_reread (400), insufficient_balance and entitlement_required (402, the second with dataset_id), or pull_contended and billing_unavailable (503).

HTTP statusCause or exampleRecovery
400Unsupported from, inactive subscription, invalid replay cursor, or filters on incremental mode (filters_need_reread).Correct the parameter or subscription state. Use a cursor returned by this endpoint.
401Missing, malformed, unknown, revoked, or expired key.Correct the full Bearer credential or obtain a replacement through an administrator.
402insufficient_balance on a priced incremental read.Have the responsible person check available billing funds.
402entitlement_required for a price-on-application source.No self-serve purchase exists for these sources yet, and adding funds does not change that. Contact support with the dataset_id from the response.
403No data-delivery access or the subscription is outside the key's scope.Correct the authorized key or subscription scope.
404Subscription not found for the caller.Verify organization, environment, and ID.
422Missing required parameter or an invalid limit.Use the query contract above.
429Too many requests to this endpoint with this Key.Wait Retry-After seconds; see Rate limits.
503Pricing/balance/billing unavailable, or concurrent pull contention (pull_contended).Retry after a short delay; apply the reading mode's recovery rules.
500 or an unfamiliar responseUnexpected service failure or contract mismatch.Preserve a sanitized diagnostic and contact support; do not report success.

Honor server-provided retry guidance, including Retry-After when present. Keep retries bounded and avoid a tight polling loop.

Delivery and completion boundaries

Campaign records must reach the eligible delivery set. An accepted review alone is insufficient: the campaign delivery path also waits for the reward to be awarded and for the delivery record to be produced. Subscription scope and quality filters further limit the result. A campaign's internal sink output is not automatically the same as this endpoint's delivered record.

This is a synchronous response, not an export job. There is no operation ID to poll. A successful page proves only that the included records were returned. Reaching has_more: false completes the current traversal; it does not establish immutable versioning, a license grant, downstream model quality, or permanent availability of every returned record.

For support, send hello@humanbased.ai the environment, request time, HTTP status, and sanitized error text. Exclude API keys and private payloads. For safe agent execution, see Use an agent with Humanbased.