> ## 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.

# Publisher usage analytics (all my apps, filterable)

> How my apps are being called over a period: range totals plus a
breakdown by day (or by hour for short ranges), by app or by calling
channel (or two of these crossed), or — for diagnosis — by final
state, by app version or by error code. The publisher-side
counterpart of GET /billing — same vocabulary (`created_from` /
`created_to` are ISO-8601 absolute timestamps, inclusive start /
exclusive end, attributed by run start time; `tz_offset` only moves
day boundaries; `data_app` / `triggered_by` narrow the set),
ownership flips from "runs I started" to "apps I own" (zero-Release
apps included).

Day / hour / app / channel breakdowns come from hourly pre-aggregated
buckets (accumulated at terminal states), so any range and any
whole-hour offset is exact and cheap. State / version / error
breakdowns are computed from run details on demand and therefore
require both bounds at most 92 days apart (400 range-too-wide);
error groups only cover runs that carry an error and each one brings
the most recent error message as `sample_message`. Debug traffic
(run_kind=test) is excluded by default; platform health probes never
count. Success counts partial successes; cancellations leave the
success-rate denominator; metrics with no finished run are null
rather than 0. `amount` is what callers paid for these runs (data
fees), not the publisher's earnings.

Day / hour groups are ascending (`hour` keys are local wall-clock
`YYYY-MM-DDTHH:00` under `tz_offset`, no zone suffix), other groups by
run count descending. Unknown or other people's apps in `data_app`
are 404 (no existence leak). `group_by` outside the vocabulary, or
combining `day` with `hour`, is 400 invalid-group-by.

Callers: `totals.callers` (and `previous_totals.callers`) is the
number of distinct users who called in the range — it comes from run
details, so it is null unless the range is bounded and at most 92
days. `group_by=caller` breaks a **single private or shared app**
down by caller: the publisher's own runs (`caller_kind=owner`) and
users currently on the grant list (`grantee`) are named by their
current username; everyone else (former grantees, callers from a
public period) is merged into one `other` group. It is 400
caller-group-unavailable for a public app or for more than one app
— a public app's callers are anonymous and only counted.



## OpenAPI

````yaml /jp/datahub/api/openapi.json get /v1/publisher/usage
openapi: 3.1.0
info:
  title: DataHub API
  version: 0.2.0
  description: Public /v1 contract of Octoparse DataHub. Internal channels are excluded.
servers:
  - url: https://api-datahub.octoparse.com
security:
  - bearerAuth: []
tags:
  - name: Discovery
    description: >-
      Anonymous access: platform metadata (currency), data app search, detail
      and input contract, machine-readable manifest spec.
  - name: Publishing
    description: >-
      Self-service publishing channel (API Key auth), two-phase: validate first
      (validate / package, nothing persisted), then publish (POST /data-apps,
      the submission goes live as a new Release). api kind (declarative
      integration) becomes visible on publish; code kind (code package) returns
      202 and enters an async image build; once ready it is promoted to a
      Release, visible and runnable. Poll progress via GET
      /data-apps/{ref}/builds.
  - name: Runs & Results
    description: >-
      Start / query / cancel runs and read result records with pagination.
      Business inputs go in the request body; platform run options (wait,
      max_records) go in query parameters.
  - name: Datasets
    description: >-
      Metadata and record access for platform datasets (including seeded stock
      data).
  - name: Account
    description: Account info and cumulative usage for the current API Key.
  - name: Secrets
    description: >-
      CRUD for upstream credentials (environment entries referenced by manifest
      `runtime.env`). Plaintext is write-only: every read returns a mask.
paths:
  /v1/publisher/usage:
    get:
      tags:
        - Publishing
      summary: Publisher usage analytics (all my apps, filterable)
      description: |-
        How my apps are being called over a period: range totals plus a
        breakdown by day (or by hour for short ranges), by app or by calling
        channel (or two of these crossed), or — for diagnosis — by final
        state, by app version or by error code. The publisher-side
        counterpart of GET /billing — same vocabulary (`created_from` /
        `created_to` are ISO-8601 absolute timestamps, inclusive start /
        exclusive end, attributed by run start time; `tz_offset` only moves
        day boundaries; `data_app` / `triggered_by` narrow the set),
        ownership flips from "runs I started" to "apps I own" (zero-Release
        apps included).

        Day / hour / app / channel breakdowns come from hourly pre-aggregated
        buckets (accumulated at terminal states), so any range and any
        whole-hour offset is exact and cheap. State / version / error
        breakdowns are computed from run details on demand and therefore
        require both bounds at most 92 days apart (400 range-too-wide);
        error groups only cover runs that carry an error and each one brings
        the most recent error message as `sample_message`. Debug traffic
        (run_kind=test) is excluded by default; platform health probes never
        count. Success counts partial successes; cancellations leave the
        success-rate denominator; metrics with no finished run are null
        rather than 0. `amount` is what callers paid for these runs (data
        fees), not the publisher's earnings.

        Day / hour groups are ascending (`hour` keys are local wall-clock
        `YYYY-MM-DDTHH:00` under `tz_offset`, no zone suffix), other groups by
        run count descending. Unknown or other people's apps in `data_app`
        are 404 (no existence leak). `group_by` outside the vocabulary, or
        combining `day` with `hour`, is 400 invalid-group-by.

        Callers: `totals.callers` (and `previous_totals.callers`) is the
        number of distinct users who called in the range — it comes from run
        details, so it is null unless the range is bounded and at most 92
        days. `group_by=caller` breaks a **single private or shared app**
        down by caller: the publisher's own runs (`caller_kind=owner`) and
        users currently on the grant list (`grantee`) are named by their
        current username; everyone else (former grantees, callers from a
        public period) is merged into one `other` group. It is 400
        caller-group-unavailable for a public app or for more than one app
        — a public app's callers are anonymous and only counted.
      operationId: publisher_usage_v1_publisher_usage_get
      parameters:
        - name: group_by
          in: query
          required: false
          schema:
            type: string
            description: >-
              one or two comma-separated dimensions from day / hour / data_app /
              triggered_by / state / version / error / caller (e.g.
              `day,data_app`); `day` and `hour` are two granularities of the
              same time axis and cannot be combined; state / version / error /
              caller are computed from run details and need a bounded range of
              at most 92 days; `caller` additionally needs exactly one private
              or shared `data_app`; default `day`
            default: day
            title: Group By
          description: >-
            one or two comma-separated dimensions from day / hour / data_app /
            triggered_by / state / version / error / caller (e.g.
            `day,data_app`); `day` and `hour` are two granularities of the same
            time axis and cannot be combined; state / version / error / caller
            are computed from run details and need a bounded range of at most 92
            days; `caller` additionally needs exactly one private or shared
            `data_app`; default `day`
        - name: data_app
          in: query
          required: false
          schema:
            anyOf:
              - items:
                  type: string
                type: array
              - type: 'null'
            description: >-
              restrict to these apps (`<username>/<app_name>`, repeatable);
              default = all my apps
            title: Data App
          description: >-
            restrict to these apps (`<username>/<app_name>`, repeatable);
            default = all my apps
        - name: created_from
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            title: Created From
        - name: created_to
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            title: Created To
        - name: tz_offset
          in: query
          required: false
          schema:
            type: integer
            maximum: 840
            minimum: -720
            description: >-
              timezone offset for day / hour buckets (minutes relative to UTC,
              e.g. 480 for UTC+8); default 0 means UTC. Fixed offset, no DST;
              buckets are hourly so half-hour offsets are approximated to the
              hour
            default: 0
            title: Tz Offset
          description: >-
            timezone offset for day / hour buckets (minutes relative to UTC,
            e.g. 480 for UTC+8); default 0 means UTC. Fixed offset, no DST;
            buckets are hourly so half-hour offsets are approximated to the hour
        - name: triggered_by
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              restrict to one calling channel (api / sdk / mcp / web …, the same
              value as the runs list filter)
            title: Triggered By
          description: >-
            restrict to one calling channel (api / sdk / mcp / web …, the same
            value as the runs list filter)
        - name: include_test
          in: query
          required: false
          schema:
            type: boolean
            default: false
            title: Include Test
        - name: compare
          in: query
          required: false
          schema:
            type: boolean
            description: >-
              also return `previous_totals` for the equal-length range
              immediately before `created_from` (requires both bounds)
            default: false
            title: Compare
          description: >-
            also return `previous_totals` for the equal-length range immediately
            before `created_from` (requires both bounds)
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataEnvelope_PublisherUsage_'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    DataEnvelope_PublisherUsage_:
      properties:
        data:
          $ref: '#/components/schemas/PublisherUsage'
      type: object
      required:
        - data
      title: DataEnvelope[PublisherUsage]
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    PublisherUsage:
      properties:
        range:
          $ref: '#/components/schemas/UsageRange'
        currency:
          type: string
          title: Currency
        totals:
          $ref: '#/components/schemas/UsageTotals'
        previous_totals:
          anyOf:
            - $ref: '#/components/schemas/UsageTotals'
            - type: 'null'
        group_by:
          type: string
          title: Group By
          default: day
        groups:
          items:
            $ref: '#/components/schemas/UsageGroup'
          type: array
          title: Groups
      type: object
      required:
        - range
        - totals
      title: PublisherUsage
      description: |-
        Publisher-side usage analytics: totals over the range (optionally the
        preceding equal-length range for period-over-period comparison) and a
        breakdown by up to two dimensions.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    UsageRange:
      properties:
        created_from:
          anyOf:
            - type: string
            - type: 'null'
          title: Created From
        created_to:
          anyOf:
            - type: string
            - type: 'null'
          title: Created To
        tz_offset:
          type: integer
          title: Tz Offset
          default: 0
      type: object
      title: UsageRange
      description: Echo of the effective range (normalised UTC instants) and offset.
    UsageTotals:
      properties:
        runs:
          type: integer
          title: Runs
          default: 0
        succeeded:
          type: integer
          title: Succeeded
          default: 0
        partial:
          type: integer
          title: Partial
          default: 0
        failed:
          type: integer
          title: Failed
          default: 0
        cancelled:
          type: integer
          title: Cancelled
          default: 0
        success_rate:
          anyOf:
            - type: number
            - type: 'null'
          title: Success Rate
        records:
          type: integer
          title: Records
          default: 0
        avg_duration_ms:
          anyOf:
            - type: integer
            - type: 'null'
          title: Avg Duration Ms
        amount:
          type: number
          title: Amount
          default: 0
        apps_active:
          type: integer
          title: Apps Active
          default: 0
        callers:
          anyOf:
            - type: integer
            - type: 'null'
          title: Callers
          description: >-
            distinct callers (users) in the range; computed from run details, so
            null unless both bounds are given at most 92 days apart
      type: object
      title: UsageTotals
      description: |-
        Range totals plus the number of distinct apps that had runs and, for a
        bounded range of at most 92 days, the number of distinct callers.
    UsageGroup:
      properties:
        runs:
          type: integer
          title: Runs
          default: 0
        succeeded:
          type: integer
          title: Succeeded
          default: 0
        partial:
          type: integer
          title: Partial
          default: 0
        failed:
          type: integer
          title: Failed
          default: 0
        cancelled:
          type: integer
          title: Cancelled
          default: 0
        success_rate:
          anyOf:
            - type: number
            - type: 'null'
          title: Success Rate
        records:
          type: integer
          title: Records
          default: 0
        avg_duration_ms:
          anyOf:
            - type: integer
            - type: 'null'
          title: Avg Duration Ms
        amount:
          type: number
          title: Amount
          default: 0
        day:
          anyOf:
            - type: string
            - type: 'null'
          title: Day
        hour:
          anyOf:
            - type: string
            - type: 'null'
          title: Hour
        namespace:
          anyOf:
            - type: string
            - type: 'null'
          title: Namespace
        app_name:
          anyOf:
            - type: string
            - type: 'null'
          title: App Name
        channel:
          anyOf:
            - type: string
            - type: 'null'
          title: Channel
        state:
          anyOf:
            - type: string
            - type: 'null'
          title: State
        version:
          anyOf:
            - type: string
            - type: 'null'
          title: Version
          description: >-
            version dimension: the Release version the run was pinned to; null
            for debug runs (they pin a Build snapshot)
        error_code:
          anyOf:
            - type: string
            - type: 'null'
          title: Error Code
        error_category:
          anyOf:
            - type: string
            - type: 'null'
          title: Error Category
        sample_message:
          anyOf:
            - type: string
            - type: 'null'
          title: Sample Message
          description: 'error dimension: message of the most recent run in this group'
        last_seen_at:
          anyOf:
            - type: string
            - type: 'null'
          title: Last Seen At
        username:
          anyOf:
            - type: string
            - type: 'null'
          title: Username
          description: >-
            caller dimension: the caller's current username; null for the merged
            `other` group or when the user has no username
        caller_kind:
          anyOf:
            - type: string
            - type: 'null'
          title: Caller Kind
          description: >-
            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)
      type: object
      title: UsageGroup
      description: |-
        One group of the usage aggregate: only the keys of the requested
        dimensions are set — `day` for day, `hour` for hour, `namespace` +
        `app_name` for data_app, `channel` for triggered_by, `state` for state,
        `namespace` + `app_name` + `version` for version, `error_code` +
        `error_category` (plus `sample_message` / `last_seen_at`) for error,
        `username` + `caller_kind` for caller.
        Error groups only cover runs that carry an error object, so they do not
        add up to the totals.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: DataHub API Key

````