Hyperresearch Usage API

Usage and billing

GitHub OpenAPI

Operations 1

GET /v1/usage Usage for this workspace #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/hyperresearch-ai:hyperresearch-ai-usage-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

hyperresearch-ai-usage-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: hyperresearch Cloud Usage API
  version: 1.0.0
  description: 'REST v1 for hosted hyperresearch runs, vault access, verification, and billing. Times are RFC 3339 UTC; ids are prefixed ULIDs.


    **Authentication.** Send an API key as `Authorization: Bearer hr_live_…`. Each operation lists the scope it needs (`x-hr-scope`, and in its `security` requirement). OAuth 2.1 (authorization code + PKCE, `oauth2` scheme) issues tokens for the hosted MCP server at https://mcp.hyperresearch.ai/mcp; REST calls use an API key.


    **Errors.** Every 4xx/5xx body is the `ErrorEnvelope`: `error.type`, a stable `error.code`, a human `message`, the offending `param` when there is one, and the `request_id` to quote to support. Branch on `code`, never on `message`.


    **Rate limits.** Responses carry `RateLimit-Policy` and `RateLimit` (draft-ietf-httpapi-ratelimit-headers) alongside the older `X-RateLimit-*` headers. A 429 always carries `Retry-After` (seconds) and `error.retry_after`.


    **Versioning.** The version is in the path. `/v1` is stable: within it, changes are additive only (new operations, new optional request fields, new response fields, new enum values a client must tolerate). A breaking change ships only under a new path version. A deprecated operation is marked `deprecated: true` here and answers with `Deprecation` (RFC 9745) and `Sunset` (RFC 8594) headers and `Link: rel="deprecation"` at least six months before it is removed, and is listed under Deprecations on the policy page: https://hyperresearch.ai/docs/versioning'
  contact:
    name: hyperresearch
    url: https://hyperresearch.ai
  x-api-lifecycle:
    version_scheme: path
    current: v1
    status: stable
    breaking_changes: new path version only
    deprecation_notice_min_days: 182
    deprecation_signals:
    - 'openapi deprecated: true'
    - Deprecation header (RFC 9745)
    - Sunset header (RFC 8594)
    - Link rel="deprecation"
    - policy page deprecations list
    policy: https://hyperresearch.ai/docs/versioning
servers:
- url: https://api.hyperresearch.ai
  description: production
tags:
- name: Usage
  description: Usage and billing
paths:
  /v1/usage:
    get:
      tags:
      - Usage
      summary: Usage for this workspace
      description: Runs by tier and metered usage by kind for the period, plus the account plan, spend cap and month-to-date spend. Reads D1 only — never Stripe. `charged` is the runs that were billed, tier by tier, and `totals.charged_cents` is exactly their sum; `included` lists the trial-covered runs apart, at $0, each flagged `included_in_trial`. Each `metered` kind separates its `charges` from its `refunds`, so a net quantity is never shown beside a gross event count.
      operationId: getUsage
      x-hr-scope: admin
      x-hr-auth: key-or-session
      security:
      - bearerAuth:
        - admin
      - sessionCookie:
        - admin
      parameters:
      - schema:
          type: string
          example: '2026-09-01T00:00:00Z'
        required: false
        name: from
        in: query
      - schema:
          type: string
          example: '2026-10-01T00:00:00Z'
        required: false
        name: to
        in: query
      responses:
        '200':
          description: Usage
          headers:
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
            X-RateLimit-Bucket:
              $ref: '#/components/headers/X-RateLimit-Bucket'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UsageSummary'
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: Insufficient scope or plan
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: Rate limited or over quota
          headers:
            Retry-After:
              $ref: '#/components/headers/Retry-After'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
            X-RateLimit-Bucket:
              $ref: '#/components/headers/X-RateLimit-Bucket'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
components:
  schemas:
    ErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum:
              - invalid_request
              - authentication
              - permission
              - not_found
              - rate_limited
              - quota_exceeded
              - budget_blocked
              - conflict
              - provider_unavailable
              - internal
              example: invalid_request
            code:
              type: string
              enum:
              - missing_api_key
              - malformed_api_key
              - invalid_api_key
              - api_key_expired
              - api_key_revoked
              - account_suspended
              - workspace_unavailable
              - session_expired
              - insufficient_scope
              - signup_closed
              - role_not_permitted
              - membership_required
              - csrf_required
              - csrf_invalid
              - origin_not_allowed
              - https_required
              - key_route_restricted
              - tier_not_allowed
              - runs_paused
              - invalid_body
              - invalid_query
              - invalid_path
              - query_too_short
              - query_not_research
              - query_too_long
              - query_refused
              - invalid_title
              - invalid_brief
              - invalid_must_read
              - invalid_research_brief
              - invalid_time_window
              - invalid_pulse
              - invalid_metadata
              - invalid_tier
              - too_many_required_headings
              - sources_max_above_ceiling
              - reuse_scope_needs_project
              - invalid_webhook_url
              - metadata_too_large
              - invalid_settings
              - invalid_step
              - unsupported_tier
              - tier_not_offered
              - plan_not_offered
              - provider_excluded
              - dry_run_not_enabled
              - invalid_dry_run_fault
              - chaos_faults_not_enabled
              - invalid_chaos_fault
              - gone
              - run_not_found
              - key_not_found
              - workspace_not_found
              - webhook_not_found
              - artifact_not_found
              - note_not_found
              - escalation_not_found
              - source_not_found
              - connection_not_found
              - project_not_found
              - answer_not_found
              - account_not_found
              - feature_not_enabled
              - idempotency_key_reuse
              - idempotency_in_flight
              - run_not_resumable
              - run_not_finish_resumable
              - run_finish_resume_exhausted
              - run_recovering
              - rerun_not_platform
              - platform_rerun_taken
              - run_not_clarifying
              - result_not_ready
              - verification_running
              - workspace_not_empty
              - turnstile_invalid
              - turnstile_replayed
              - demo_daily_limit
              - demo_receipt_invalid
              - demo_disabled
              - anon_session_invalid
              - anon_refine_limit
              - anon_refine_capacity
              - refine_draft_not_found
              - user_not_found
              - member_not_found
              - member_exists
              - last_owner
              - cannot_self_elevate
              - workspace_limit
              - invitation_not_found
              - invitation_expired
              - invitation_email_mismatch
              - account_mismatch
              - ip_not_allowed
              - chat_keys_retired
              - project_name_taken
              - project_archived
              - run_not_filable
              - confirmation_mismatch
              - ownership_transfer_required
              - account_deleting
              - export_cooldown
              - migration_pending
              - rate_limit_exceeded
              - concurrency_limit
              - spend_cap_exceeded
              - trial_exhausted
              - payment_method_required
              - payment_disputed
              - card_hold_unavailable
              - card_required
              - fixture_not_shareable
              - too_many_url_sources
              - no_citations
              - insufficient_balance
              - wallet_frozen
              - topup_amount_invalid
              - topup_daily_limit
              - answers_capacity
              - engine_unavailable
              - temporarily_unavailable
              - platform_capacity
              - daily_run_limit
              - internal_budget
              - orchestrator_unavailable
              - source_unconfigured
              - source_rate_limited
              - source_upstream_unavailable
              - deployment_misconfigured
              - not_implemented
              - pdf_unavailable
              - internal_error
              example: query_too_short
            message:
              type: string
            param:
              type: string
            retry_after:
              type: integer
            retryable:
              type: boolean
              description: 'Whether the same request may succeed if sent again unchanged (after `retry_after` when present): true for 429, 500, 502, 503, 504 and `idempotency_in_flight`; false for every other 4xx and 501.'
            request_id:
              type: string
              example: req_01J9ZQ7V6H0000000000000000
          required:
          - type
          - code
          - message
          - retryable
          - request_id
      required:
      - error
    UsageTotals:
      type: object
      properties:
        quantity:
          type: integer
        amount_cents:
          type: integer
        events:
          type: integer
      required:
      - quantity
      - amount_cents
      - events
      description: The non-negative rows only — the metered events themselves, charged and included alike. `charges.quantity === events` for a per-run meter.
    UsageSummary:
      type: object
      properties:
        period:
          type: object
          properties:
            from:
              type: string
            to:
              type: string
          required:
          - from
          - to
        workspace_id:
          type: string
        runs:
          type: array
          items:
            type: object
            properties:
              tier:
                type: string
              runs:
                type: integer
                description: Runs of this tier created in the period.
              price_cents:
                type: integer
                description: Reserved list price on the control rows. 0 for a run an included (trial) allowance covered — this is NOT what was charged.
              amount_cents:
                type: integer
                description: What the billing ledger metered for these runs, refunds included. Sums to `totals.run_amount_cents`.
              metered_runs:
                type: integer
                description: How many of these runs have a ledger row yet. `runs - metered_runs` is the number still in flight.
              included_runs:
                type: integer
                description: How many of these runs an included (trial) allowance covered, metered at $0.
              charged_runs:
                type: integer
                description: '`runs - included_runs`: the runs no allowance covered.'
              charged_cents:
                type: integer
                description: What the ledger metered for the charged runs alone.
              included_cents:
                type: integer
                description: What the ledger metered for the included runs. $0 by definition.
              complimentary_runs:
                type: integer
                description: How many of these runs an operator grant comped (0041). Taken OUT of `charged_runs`, never netted into it.
              complimentary_cents:
                type: integer
                description: What the comped runs would have cost — the real price, which their ledger rows carry. Not part of `charged_cents`.
            required:
            - tier
            - runs
            - price_cents
            - amount_cents
            - metered_runs
            - included_runs
            - charged_runs
            - charged_cents
            - included_cents
        charged:
          type: array
          items:
            type: object
            properties:
              tier:
                type: string
              runs:
                type: integer
              amount_cents:
                type: integer
              metered_runs:
                type: integer
              included_in_trial:
                type: boolean
                enum:
                - false
            required:
            - tier
            - runs
            - amount_cents
            - metered_runs
            - included_in_trial
          description: Runs that were billed this period, by tier, with what they cost. `totals.charged_cents` is exactly the sum of these amounts — no other number has to be subtracted to arrive at it (E2E2-07).
        included:
          type: array
          items:
            type: object
            properties:
              tier:
                type: string
              runs:
                type: integer
              amount_cents:
                type: integer
                description: 'Always 0: an included run is not charged.'
              included_in_trial:
                type: boolean
                enum:
                - true
            required:
            - tier
            - runs
            - amount_cents
            - included_in_trial
          description: Trial-covered runs, listed apart from the charged ones so "$0.00" reads as "included" rather than as "free by accident".
        complimentary:
          type: array
          items:
            type: object
            properties:
              tier:
                type: string
              runs:
                type: integer
              amount_cents:
                type: integer
                description: The real price of these runs, which is what their ledger rows carry — a comped run is not a $0 run.
              complimentary:
                type: boolean
                enum:
                - true
            required:
            - tier
            - runs
            - amount_cents
            - complimentary
          description: Runs an operator grant comped (0041), listed apart from the charged ones and NOT included in `totals.charged_cents`. Absent when this account has none.
        metered:
          type: array
          items:
            type: object
            properties:
              kind:
                type: string
              quantity:
                type: integer
                description: 'Net of refunds, like `amount_cents` beside it. To show a quantity next to `events`, use `charges.quantity`: it counts the same population (E2E2-07).'
              amount_cents:
                type: integer
                description: Net of refunds.
              events:
                type: integer
                description: The metered events of this kind in the period — charged and included together, refunds excluded. ALWAYS equal to `charges.quantity` for a one-unit-per-event meter such as runs, because they are the same rows counted twice; a row that read `Research runs — 7 — $0.00 — 8 events` was counting a refund as an eighth run (E2E4A-10). Show `charges.amount_cents` beside it for what those events were charged.
              gross_events:
                type: integer
                description: Every ledger row of this kind, refunds included. `events + refunds.events`.
              charges:
                $ref: '#/components/schemas/UsageTotals'
              refunds:
                allOf:
                - $ref: '#/components/schemas/UsageTotals'
                - description: The negative rows only; `quantity` and `amount_cents` stay negative. A refund reverses an event, it is not an event, so it is never counted in `events`.
            required:
            - kind
            - quantity
            - amount_cents
            - events
            - gross_events
            - charges
            - refunds
        totals:
          type: object
          properties:
            amount_cents:
              type: integer
              description: Every metered kind in this workspace for the period.
            events:
              type: integer
            runs:
              type: integer
              description: Runs created in the period, all tiers.
            metered_runs:
              type: integer
            included_runs:
              type: integer
            charged_runs:
              type: integer
              description: Runs created in the period that no allowance covered.
            charged_cents:
              type: integer
              description: What the charged runs cost, and exactly the sum of `charged[].amount_cents`. This is the period total a customer is asked to believe.
            run_amount_cents:
              type: integer
              description: The run-kind slice of `amount_cents`; equals the sum of `runs[].amount_cents`.
            complimentary_runs:
              type: integer
              description: Runs created in the period that an operator grant comped (0041).
            complimentary_cents:
              type: integer
              description: 'What those runs would have cost, and exactly the sum of `complimentary[].amount_cents`. Deliberately NOT part of `charged_cents`: the account was not asked to pay it.'
          required:
          - amount_cents
          - events
          - runs
          - metered_runs
          - included_runs
          - charged_runs
          - charged_cents
          - run_amount_cents
        licensed_articles:
          type: object
          properties:
            month:
              type: string
              description: '`YYYY-MM`, the month the source ledger books against.'
            articles:
              type: integer
              description: Licensed article purchases attributed to this workspace this month, settled or in flight.
            settled_micros:
              type: integer
              description: What the vendor confirmed charging, in micro-USD (1000000 = $1.00). Micro-USD and not cents because a licensed page costs $0.001 to $1.00, and most purchases would round to free.
            reserved_micros:
              type: integer
              description: 'Booked and not yet settled: a purchase in flight, held at its worst case.'
            unresolved_micros:
              type: integer
              description: Of `reserved_micros`, the part whose outcome is unknown and awaiting reconciliation against the vendor's own record. Reported apart because an unknown outcome is neither confirmed spend nor available headroom.
          required:
          - month
          - articles
          - settled_micros
          - reserved_micros
          - unresolved_micros
          description: 'Licensed-article spend (rung `LT`), at cost, from the fetch lane''s own source ledger — NOT from `usage_events` and NOT part of `totals`. The two ledgers are separate on purpose: one is what this workspace was charged, the other is what a vendor charged us for a page, and summing them would double-count. All zeros when nothing was licensed, which is the shipping state.'
        account:
          type: object
          properties:
            plan:
              type: string
            spend_cap_cents_month:
              type: integer
            month_to_date_cents:
              type: integer
              description: Account-wide charge for the current month, across EVERY workspace of the account — not just this one. Compare with `totals.amount_cents`, which is this workspace only.
          required:
          - plan
          - spend_cap_cents_month
          - month_to_date_cents
      required:
      - period
      - workspace_id
      - runs
      - charged
      - included
      - metered
      - totals
      - licensed_articles
      - account
  headers:
    Retry-After:
      description: Seconds to wait before retrying. Always present on a 429.
      schema:
        type: integer
        minimum: 0
    X-RateLimit-Remaining:
      description: Requests left in this window (legacy form of `RateLimit`).
      schema:
        type: integer
        minimum: 0
    X-RateLimit-Bucket:
      description: 'Which bucket counted the request: `reads`, `status`, `creates`, `writes` or `public`.'
      schema:
        type: string
    RateLimit-Policy:
      description: 'The quota policy this request was counted against (draft-ietf-httpapi-ratelimit-headers): `"<bucket>";q=<requests>;w=<window seconds>`, e.g. `"reads";q=60;w=60`. Buckets: `reads` 60/min, `status` 600/min, `creates` 10/min, `writes` 20/min per credential; `public` 300/min per network on keyless routes.'
      schema:
        type: string
        example: '"reads";q=60;w=60'
    X-RateLimit-Limit:
      description: Requests allowed per window in this bucket (legacy form of `RateLimit-Policy`).
      schema:
        type: integer
        minimum: 0
    X-RateLimit-Reset:
      description: Unix time (seconds) when the window resets.
      schema:
        type: integer
        minimum: 0
    RateLimit:
      description: 'Remaining quota in the current window: `"<bucket>";r=<remaining>;t=<seconds until reset>`, e.g. `"reads";r=57;t=41`.'
      schema:
        type: string
        example: '"reads";r=57;t=41'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'An API key: `hr_live_…` (billed), `hr_test_…` (fixture mode, free), or `hr_chat_…` (vault chat only).'
    sessionCookie:
      type: apiKey
      in: cookie
      name: hr_session
      description: Console session cookie (HttpOnly, set by `POST /v1/auth/clerk/exchange`). Mutations also require an allowlisted `Origin` and the `X-CSRF-Token` header. The requirement list names the workspace-role scope the session must carry.
    clerkAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: A Clerk-issued session JWT, exchanged once for an `hr_session` cookie. Not a hyperresearch API key.
    oauth2:
      type: oauth2
      description: 'OAuth 2.1 for agents (MCP clients): authorization code with PKCE (S256), public clients, dynamic client registration at `https://api.hyperresearch.ai/oauth/register`, discovery at `https://api.hyperresearch.ai/.well-known/oauth-authorization-server`. Access tokens (15 min) and rotating refresh tokens (30 days) are bound to the resource `https://mcp.hyperresearch.ai/mcp` and are accepted by the hosted MCP server, not by the REST operations in this document, which take an API key (`bearerAuth`). A member can only delegate scopes their workspace role holds; the token response reports the granted `scope`.'
      flows:
        authorizationCode:
          authorizationUrl: https://api.hyperresearch.ai/oauth/authorize
          tokenUrl: https://api.hyperresearch.ai/oauth/token
          refreshUrl: https://api.hyperresearch.ai/oauth/token
          scopes:
            mcp: Connect to the hosted MCP server; every OAuth grant carries it.
            vault:read: 'Read and search the workspace vault: notes, claims, sources, hubs, escalations, scholar lookups.'
            runs:read: 'Read runs: status, event stream, results, reports and artifacts.'
            vault:write: 'Write to the workspace vault: create/update/delete notes, ingest claims, import, resolve escalations.'
            runs:write: Start, clarify, pause, resume, abort and re-run research runs (billable).
            fetch: Fetch a URL into the vault on the workspace’s budget (billable; OAuth grants only, checked against the member’s role).
            verify: Run citation, quote, retraction and independence verification jobs and read their results (billable).
            answers:ask: 'Answers: `ask` and `search` calls billed per call from the prepaid wallet, plus reading the wallet balance (billable).'
externalDocs:
  description: hyperresearch API documentation
  url: https://hyperresearch.ai/docs