> ## Documentation Index
> Fetch the complete documentation index at: https://www.octoparse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 発行者使用量

> 期間内の公開 App 呼び出し: 合計と日・時間・App・チャネル等の内訳。

**`GET`** `https://api-datahub.octoparse.com/v1/publisher/usage`

Authentication: API key required (`Authorization: Bearer <API Key>`).

How your apps were called in a period: range totals, plus breakdowns by day (or hour for short ranges), app, and call channel (any two can be crossed), or diagnostic breakdowns by terminal state, version, or error code. This is the publisher-side counterpart of billing aggregation: the same vocabulary (`created_from` / `created_to` keyed by run start time, start inclusive and end exclusive; `tz_offset` only moves day boundaries; `data_app` / `triggered_by` narrow the scope), with ownership flipped from “runs I started” to “apps I own” (including apps with zero releases).

Day / hour / app / channel breakdowns come from hour buckets accumulated at terminal state, so any range and whole-hour offset stay exact and cheap. State / version / error breakdowns are computed on demand from run details, so both bounds are required and the span must be at most 92 days (`400 range-too-wide`). Error groups cover only runs that carry an error; each group includes the latest error message as `sample_message`. Author debug runs are excluded by default; platform health probes never count. Success includes partial success; cancelled runs stay out of the success-rate denominator; metrics with no terminal runs are `null`. `amount` is the data fee callers paid for these runs, not the publisher’s settled revenue.

Day / hour groups ascend (`hour` keys are local clock `YYYY-MM-DDTHH:00` under `tz_offset`, with no timezone suffix). Other groups descend by run count. Unknown or other people’s apps in `data_app` return `404`.

`totals.callers` is the distinct caller-user count in the range, from run details, and is set only when the range is bounded and at most 92 days. `group_by=caller` breaks down a **single private or shared app** by caller: the author (`caller_kind=owner`) and users currently on the grant list (`grantee`) show under their current username; everyone else (former grantees, callers from a public period) merges into one `other` group. Public apps or multiple apps return `400 caller-group-unavailable`: public-app callers stay anonymous and are counted only.

## Request

### Query parameters

<ParamField query="group_by" type="string" default="day">
  One or two dimensions, comma-separated: `day` / `hour` / `data_app` / `triggered_by` / `state` / `version` / `error` / `caller` (for example `day,data_app`). `day` and `hour` cannot combine; `state` / `version` / `error` / `caller` need a bounded range of at most 92 days; `caller` also requires exactly one private or shared app. Default `day`.
</ParamField>

<ParamField query="data_app" type="string[]">
  Limit to these apps (`<username>/<app_name>`, repeatable). Default: all of your apps.
</ParamField>

<ParamField query="created_from" type="string">
  Inclusive start.
</ParamField>

<ParamField query="created_to" type="string">
  Exclusive end.
</ParamField>

<ParamField query="tz_offset" type="integer" default="0">
  Timezone offset in minutes for day / hour buckets. Use `480` for China Standard Time. Buckets are hour-grained; half-hour offsets round to the hour.

  Range -720 to 840.
</ParamField>

<ParamField query="triggered_by" type="string">
  Limit to one call channel (`api` / `sdk` / `mcp` / `web`, and so on).
</ParamField>

<ParamField query="include_test" type="boolean" default="False">
  Whether to include author debug runs.
</ParamField>

<ParamField query="compare" type="boolean" default="False">
  Also return `previous_totals` for the equal-length window immediately before `created_from`. Requires both bounds.
</ParamField>

### Example request

```bash theme={null}
curl \
  -H "Authorization: Bearer $OCTOPARSE_API_KEY" \
  "https://api-datahub.octoparse.com/v1/publisher/usage?group_by=day,data_app&created_from=2026-09-01T00:00:00%2B08:00&created_to=2026-10-01T00:00:00%2B08:00&tz_offset=480"
```

## Response

### 200 success

```json theme={null}
{
  "data": {
    "range": {
      "created_from": null,
      "created_to": null,
      "tz_offset": 480
    },
    "currency": "CNY",
    "totals": {
      "runs": 2,
      "succeeded": 2,
      "partial": 0,
      "failed": 0,
      "cancelled": 0,
      "success_rate": 1.0,
      "records": 40,
      "avg_duration_ms": 422,
      "amount": 0.04,
      "apps_active": 1,
      "callers": null
    },
    "previous_totals": null,
    "group_by": "day",
    "groups": [
      {
        "runs": 2,
        "succeeded": 2,
        "partial": 0,
        "failed": 0,
        "cancelled": 0,
        "success_rate": 1.0,
        "records": 40,
        "avg_duration_ms": 422,
        "amount": 0.04,
        "day": "2026-09-15",
        "hour": null,
        "namespace": null,
        "app_name": null,
        "channel": null,
        "state": null,
        "version": null,
        "error_code": null,
        "error_category": null,
        "sample_message": null,
        "last_seen_at": null,
        "username": null,
        "caller_kind": null
      }
    ]
  }
}
```

The payload is wrapped in `data`. Fields:

<ResponseField name="range" type="object" required>
  The range and offset actually used.

  <Expandable title="fields">
    <ResponseField name="created_from" type="string">
      Inclusive start actually applied.
    </ResponseField>

    <ResponseField name="created_to" type="string">
      Exclusive end actually applied.
    </ResponseField>

    <ResponseField name="tz_offset" type="integer">
      Day / hour bucket offset in minutes.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="currency" type="string">
  Currency.
</ResponseField>

<ResponseField name="totals" type="object" required>
  Range totals.

  <Expandable title="fields">
    <ResponseField name="runs" type="integer">
      Run count.
    </ResponseField>

    <ResponseField name="succeeded" type="integer">
      Success count (includes partial success).
    </ResponseField>

    <ResponseField name="partial" type="integer">
      Partial-success count.
    </ResponseField>

    <ResponseField name="failed" type="integer">
      Failure count.
    </ResponseField>

    <ResponseField name="cancelled" type="integer">
      Cancellation count.
    </ResponseField>

    <ResponseField name="success_rate" type="number">
      Success rate; cancelled runs stay out of the denominator. `null` when there are no terminal runs.
    </ResponseField>

    <ResponseField name="records" type="integer">
      Total records written back.
    </ResponseField>

    <ResponseField name="avg_duration_ms" type="integer">
      Average execution duration in milliseconds.
    </ResponseField>

    <ResponseField name="amount" type="number">
      Total data fee paid by callers.
    </ResponseField>

    <ResponseField name="apps_active" type="integer">
      Number of apps with at least one run.
    </ResponseField>

    <ResponseField name="callers" type="integer">
      Distinct caller users. `null` when the range is unbounded or wider than 92 days.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="previous_totals" type="object">
  Equal-length prior-window totals when `compare=true`.

  <Expandable title="fields">
    <ResponseField name="runs" type="integer">
      Run count.
    </ResponseField>

    <ResponseField name="succeeded" type="integer">
      Success count (includes partial success).
    </ResponseField>

    <ResponseField name="partial" type="integer">
      Partial-success count.
    </ResponseField>

    <ResponseField name="failed" type="integer">
      Failure count.
    </ResponseField>

    <ResponseField name="cancelled" type="integer">
      Cancellation count.
    </ResponseField>

    <ResponseField name="success_rate" type="number">
      Success rate; cancelled runs stay out of the denominator.
    </ResponseField>

    <ResponseField name="records" type="integer">
      Total records written back.
    </ResponseField>

    <ResponseField name="avg_duration_ms" type="integer">
      Average execution duration in milliseconds.
    </ResponseField>

    <ResponseField name="amount" type="number">
      Total data fee paid by callers.
    </ResponseField>

    <ResponseField name="apps_active" type="integer">
      Number of apps with at least one run.
    </ResponseField>

    <ResponseField name="callers" type="integer">
      Distinct callers (users) in the range; computed from run details, so `null` unless both bounds are given and at most 92 days apart.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="group_by" type="string">
  Dimension(s) actually used.
</ResponseField>

<ResponseField name="groups" type="object[]">
  Group rows. Each row carries the same metrics as `totals`, plus dimension keys (`day` / `hour` / `namespace` + `app_name` / `channel` / `state` / `version` / `error_code` + `error_category` + `sample_message` + `last_seen_at` / `username` + `caller_kind`). Unused keys are `null`.

  <Expandable title="fields">
    <ResponseField name="runs" type="integer">
      Run count in the group.
    </ResponseField>

    <ResponseField name="succeeded" type="integer">
      Success count (includes partial success).
    </ResponseField>

    <ResponseField name="partial" type="integer">
      Partial-success count.
    </ResponseField>

    <ResponseField name="failed" type="integer">
      Failure count.
    </ResponseField>

    <ResponseField name="cancelled" type="integer">
      Cancellation count.
    </ResponseField>

    <ResponseField name="success_rate" type="number">
      Success rate for the group.
    </ResponseField>

    <ResponseField name="records" type="integer">
      Records written back in the group.
    </ResponseField>

    <ResponseField name="avg_duration_ms" type="integer">
      Average execution duration in milliseconds.
    </ResponseField>

    <ResponseField name="amount" type="number">
      Data fee paid by callers in the group.
    </ResponseField>

    <ResponseField name="day" type="string">
      Day key when grouping by day, `YYYY-MM-DD`.
    </ResponseField>

    <ResponseField name="hour" type="string">
      Hour key when grouping by hour: local `YYYY-MM-DDTHH:00` under `tz_offset`.
    </ResponseField>

    <ResponseField name="namespace" type="string">
      Publisher username when grouping by app.
    </ResponseField>

    <ResponseField name="app_name" type="string">
      App name when grouping by app.
    </ResponseField>

    <ResponseField name="channel" type="string">
      Call channel when grouping by `triggered_by`.
    </ResponseField>

    <ResponseField name="state" type="string">
      Terminal state when grouping by state.
    </ResponseField>

    <ResponseField name="version" type="string">
      Version dimension: the release version the run was pinned to; `null` for debug runs (they pin a build snapshot).
    </ResponseField>

    <ResponseField name="error_code" type="string">
      Error code when grouping by error.
    </ResponseField>

    <ResponseField name="error_category" type="string">
      Error category when grouping by error.
    </ResponseField>

    <ResponseField name="sample_message" type="string">
      Error dimension: message of the most recent run in this group.
    </ResponseField>

    <ResponseField name="last_seen_at" type="string">
      Timestamp of the most recent run in the group.
    </ResponseField>

    <ResponseField name="username" type="string">
      Caller dimension: the caller’s current username; `null` for the merged `other` group or when the user has no username.
    </ResponseField>

    <ResponseField name="caller_kind" type="string">
      Caller dimension: `owner` (the publisher’s own runs), `grantee` (a user currently on the app’s grant list), or `other` (all remaining callers merged into one group).
    </ResponseField>
  </Expandable>
</ResponseField>

### Errors

| HTTP | `code`                     | `category`      | Description                                                                                                  |
| ---- | -------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------ |
| 401  | `unauthorized`             | `forbidden`     | Missing or invalid API key.                                                                                  |
| 400  | `invalid-group-by`         | `invalid_input` | `group_by` is not allowed, or the combination is illegal.                                                    |
| 400  | `range-too-wide`           | `invalid_input` | This dimension requires both `created_from` and `created_to`, at most 92 days apart.                         |
| 400  | `caller-group-unavailable` | `invalid_input` | `group_by=caller` supports exactly one private or shared app.                                                |
| 404  | `app-not-found`            | `not_found`     | App does not exist, was renamed, or is invisible to the current credential (outside private / shared scope). |

Error responses use `{"error": {code, category, message, retryable}}`. See <a href="/docs/jp/datahub/api/reference/introduction#errors">Errors</a>.

## Client libraries

The Python and JavaScript SDKs do not wrap this endpoint yet. Call REST directly.
