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

# Analyst Stance

> One analyst's daily stance on one asset, oldest first — each sentiment_score from 0 (bearish) to 100 (bullish). Premium and Enterprise keys.

## What it returns

One analyst's daily stance on one asset, oldest first. Each `sentiment_score` ranges from **0**
(bearish) to **100** (bullish).

## Access

Per-analyst data is available on the **Premium** and **Enterprise** plans; a Free key answers
`401 PLAN_REQUIRED`. Valid handles come from [`/analysts/top`](/sentiment/top-analysts) or the
[analysts page](https://consensus.cryptoquant.com/analysts).

## Parameters

| Name | Default | Description |
| - | - | - |
| `handle` | — | Analyst handle (required unless `analyst_id` is given); case-insensitive. |
| `analyst_id` | — | Analyst UUID (alternative to `handle`; wins when both are given). |
| `asset` | `BTC` | Any registered asset (see [`/assets`](/sentiment/assets)). A pair with no rows answers `count: 0`. |
| `days` | `30` | Days of history ending yesterday UTC; capped at 36,500. |

## Notes

* `sentiment_score_key_calls` and `key_calls_count` describe the analyst's key calls that day;
  `sentiment_score_key_calls` is `null` when there are none.
* No tracked analyst matching `handle` answers `404 ANALYST_NOT_FOUND`; an analyst excluded from
  the stance pipeline answers `404 ANALYST_NOT_ELIGIBLE`; no rows for the analyst × asset pair in
  the range answers `404 NO_DATA` — see [Errors](/sentiment/errors).


## OpenAPI

````yaml openapi/sentiment.json GET /sentiment
openapi: 3.1.0
info:
  title: Analyst Consensus API — CryptoQuant
  version: 2.2.0
  summary: >-
    What curated analysts say about crypto, stocks, indices and commodities, as
    numbers.
  description: >-
    What curated analysts on X, CryptoQuant, Seeking Alpha, TradingView and
    Substack (plus licensed sell-side ratings on equities) say about 1712 assets
    — crypto, US and KR equities, index ETFs, commodities — as numbers: the
    daily Analyst Consensus Index (−100 … +100, one series per asset), a
    per-asset consensus breakdown (bull / bear case, viewpoints grouped by
    thesis, source posts), per-analyst stance series, and each analyst's
    accuracy track record.


    **Two access levels.** Public, no key (per-IP rate limit, CDN-cached): `GET
    /assets` (the asset registry + every enum), `GET /consensus` without a key
    (the LATEST index point for any asset with the bull / bear case), `GET
    /analyst-views`, `GET /calls`, `GET /analysts/top`, `GET
    /analysts/{handle}`. Every public payload carries `page_url` (link it),
    `cite_as` and `data_by` ("Data by CryptoQuant Consensus" — show it with the
    numbers). With an API key (`X-API-Key` header or `api_key` query parameter):
    the index HISTORY (`GET /consensus` with `days`) and `GET
    /consensus/breakdown` on any plan; the per-analyst series `GET /sentiment`
    on the Premium and Enterprise plans only (`PLAN_REQUIRED` below). Keys are
    provisioned with the CryptoQuant Premium plan or an Enterprise agreement;
    there is no self-serve key endpoint on this host. **Raw source text** (the
    original post title / text) is provided under an Integration agreement only,
    per API key (`api_keys.raw_access`, never a plan): `GET
    /consensus/breakdown` sends `sources[].statement` on a cleared key and a ≤
    200-character `sources[].excerpt` + url on every other key — Premium,
    Enterprise and admin-owned keys included; the key-free `/analyst-views` and
    `/calls` excerpts follow the same rule. Integration use that needs the
    original text: contact sales@cryptoquant.com (`x-raw-source-text`). Thesis,
    narratives, index and labels are the same on every plan.


    **Conventions.** Dates: `date` = UTC calendar day `YYYY-MM-DD`; `date-time`
    = RFC 3339 / ISO 8601 with offset, UTC. Index and stance series are
    end-of-day: the current UTC day is excluded until complete, so values never
    change retroactively. No pagination anywhere: a series comes back whole
    (`days` is capped by the plan, 36,500 = everything; the full BTC history is
    ≈ 1,950 rows); lists are capped by `limit` (1–50). Sorting is fixed per
    endpoint and stated in its description. Filters combine with AND. Every
    error is the `Error` object: branch on `code`, read `message`, follow
    `docs_url`; `x-errors` lists every code with its recovery. Every operation
    carries `x-plan` (who may call it).


    **Agents.** Import this document as a ChatGPT GPT Action (Authentication:
    None for the public operations) or any OpenAPI tool loader; the Markdown
    version of these docs is `/llms.txt`; the same data is an MCP server at
    `/mcp` (Streamable HTTP, no auth). Pass `via=chatgpt|claude|…` so the links
    you show carry the right `utm_source`.


    **Freshness.** Index and stance series: end-of-day UTC. The breakdown is
    recomputed daily (English by 04:00 UTC, translations by 06:00 UTC); its
    opinion counts refresh every 4 hours. Public payloads are CDN-cached 5
    minutes (track record and assets: 1 hour).


    Human docs: https://consensus.cryptoquant.com/docs/api · how the numbers are
    made: https://consensus.cryptoquant.com/methodology · Markdown for agents:
    https://consensus.cryptoquant.com/llms.txt
  contact:
    name: CryptoQuant — API access and Enterprise data
    url: https://cryptoquant.com/get-in-touch
    email: support@cryptoquant.com
  termsOfService: https://consensus.cryptoquant.com/terms
  license:
    name: Proprietary — CryptoQuant Terms of Service
    url: https://consensus.cryptoquant.com/terms
servers:
  - url: https://consensus.cryptoquant.com/api/v1
    description: Analyst Consensus by CryptoQuant
security:
  - ApiKeyHeader: []
  - ApiKeyQuery: []
tags:
  - name: Registry
    description: Public, no API key. The assets the API covers and every enum it uses.
  - name: Consensus
    description: >-
      The Analyst Consensus Index: latest point public (no key); history and the
      per-asset breakdown with an API key.
  - name: Analyst views
    description: >-
      Public, no API key. What analysts are saying: narratives, per-analyst
      views, the newest calls.
  - name: Analyst track record
    description: >-
      Public, no API key. Who to trust: accuracy scores, ranks and badges per
      analyst.
  - name: Analyst stance
    description: Premium / Enterprise API key. One analyst's daily stance on an asset.
externalDocs:
  description: Sentiment Data documentation (the human rendering of this document)
  url: https://docs.cryptoquant.com/sentiment/overview
paths:
  /sentiment:
    get:
      tags:
        - Analyst stance
      summary: One analyst's daily stance on an asset (Premium / Enterprise API key)
      description: >-
        Daily stance / sentiment score of one analyst on one asset, oldest
        first. Identify the analyst by `handle` (case-insensitive) or
        `analyst_id` (UUID); `asset` is any registered asset (symbol, key, name
        or alias — canonical symbol in the response). Premium / Enterprise keys
        (and the legacy Enterprise row) get `days` of history ending yesterday
        UTC; a FREE key is refused with 401 `PLAN_REQUIRED` (Ki 2026-10-06 —
        before that date the free plan got the latest point, the `StanceLatest`
        shape is kept for the day a free per-analyst tier returns). A pair with
        no rows answers `count: 0`. Field set depends on the stance pipeline
        flag: `stance` / `confidence` / `post_count` appear once it is on,
        `sentiment_score_key_calls` is then always null.
      operationId: getAnalystStance
      parameters:
        - name: handle
          in: query
          required: false
          description: >-
            Analyst handle (required unless analyst_id is given); valid handles
            come from /analysts/top. Case-insensitive.
          schema:
            type: string
          example: caprioleio
        - name: analyst_id
          in: query
          required: false
          description: Analyst UUID (alternative to handle; wins when both are given).
          schema:
            type: string
            format: uuid
          example: c897b6ac-0cf2-41f2-98d5-84b7464cef41
        - name: asset
          in: query
          required: false
          description: >-
            Any registered asset, case-insensitive: the symbol (`BTC`, `NVDA`,
            `005930`, `XAU`), the route key (`bitcoin`, `samsung-electronics`),
            the display name (`Bitcoin`), an alias ticker (`GLD` → XAU) or a
            retired ticker (`MATIC` → POL). The response reports the canonical
            symbol. The full list: `GET /assets`. A market aggregate
            (`crypto-market`) is not accepted. Unknown → 400 `UNKNOWN_ASSET`.
          schema:
            type: string
            default: BTC
          example: BTC
        - name: days
          in: query
          required: false
          description: >-
            Days of history ending yesterday UTC. Positive integer (else 400
            INVALID_PARAMETER); silently capped at the plan's max_days_history
            (36500 on premium / enterprise).
          schema:
            type: integer
            minimum: 1
            maximum: 36500
            default: 30
          example: 90
      responses:
        '200':
          description: >-
            Series (premium / enterprise: StanceSeries). StanceLatest is the
            reserved free-plan shape (not served today).
          headers:
            X-RateLimit-Limit:
              description: Requests allowed per minute on your plan.
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests left in the current minute.
              schema:
                type: integer
            X-Daily-Limit:
              description: 'Free plan only: requests allowed per UTC day.'
              schema:
                type: integer
            X-Daily-Used:
              description: 'Free plan only: requests used today, including this one.'
              schema:
                type: integer
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/StanceSeries'
                  - $ref: '#/components/schemas/StanceLatest'
              examples:
                series:
                  summary: Premium / Enterprise key
                  value:
                    analyst_id: c897b6ac-0cf2-41f2-98d5-84b7464cef41
                    asset: BTC
                    period:
                      start: '2026-10-04'
                      end: '2026-10-05'
                    count: 2
                    data:
                      - date: '2026-10-04'
                        sentiment_score: 80
                        sentiment_score_key_calls: null
                        key_calls_count: 7
                      - date: '2026-10-05'
                        sentiment_score: 82
                        sentiment_score_key_calls: null
                        key_calls_count: 5
                latest_free_reserved:
                  summary: Reserved free-plan shape (401 PLAN_REQUIRED today)
                  value:
                    analyst_id: c897b6ac-0cf2-41f2-98d5-84b7464cef41
                    asset: BTC
                    date: '2026-10-05'
                    sentiment_score: 82
                    sentiment_score_key_calls: null
                    key_calls_count: 5
                    plan: free
                    upgrade_message: >-
                      Contact us for Enterprise access to full historical
                      sentiment data
        '400':
          description: >-
            `INVALID_PARAMETER` (days; neither handle nor analyst_id → `error:
            "analyst_id or handle parameter required"`) or `UNKNOWN_ASSET`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Invalid window
                code: INVALID_PARAMETER
                message: 'window must be one of: 1d, 3d, 7d, 30d'
                docs_url: https://consensus.cryptoquant.com/docs/api#errors
                param: window
        '401':
          description: >-
            Missing key → `API_KEY_REQUIRED`; invalid key → `INVALID_API_KEY`;
            free-plan key → `PLAN_REQUIRED` (`required_plans`, `upgrade_url`,
            `contact_url`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missing:
                  value:
                    error: API key required
                    code: API_KEY_REQUIRED
                    message: >-
                      Use the X-API-Key header or the api_key query parameter.
                      Keys start with "unbias_live_".
                    docs_url: https://consensus.cryptoquant.com/docs/api#errors
                    docs: https://consensus.cryptoquant.com/docs/api
                invalid:
                  value:
                    error: Invalid API key
                    code: INVALID_API_KEY
                    message: >-
                      The key is unknown, inactive or expired, or its
                      subscription is not active.
                    docs_url: https://consensus.cryptoquant.com/docs/api#errors
                plan:
                  value:
                    error: Premium or Enterprise plan required
                    code: PLAN_REQUIRED
                    message: >-
                      This key (Free plan) has no access to per-analyst data.
                      Per-analyst endpoints are available on the Premium and
                      Enterprise plans: subscribe to CryptoQuant Premium
                      (upgrade_url) or contact CryptoQuant for Enterprise /
                      trial access (contact_url).
                    docs_url: https://consensus.cryptoquant.com/docs/api#plans
                    plan: FREE
                    required_plans:
                      - premium
                      - enterprise
                    upgrade_url: https://cryptoquant.com/pricing
                    contact_url: https://cryptoquant.com/get-in-touch
                    upgradeUrl: https://cryptoquant.com/pricing
        '404':
          description: >-
            `ANALYST_NOT_FOUND`, `ANALYST_NOT_ELIGIBLE` (excluded / ineligible
            analysts) or `NO_DATA` (free-plan latest point, reserved)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Analyst not found
                code: ANALYST_NOT_FOUND
                message: >-
                  No tracked analyst "nobody". Valid handles are listed by GET
                  https://consensus.cryptoquant.com/api/v1/analysts/top?asset=<symbol>.
                docs_url: https://consensus.cryptoquant.com/docs/api#errors
        '429':
          description: >-
            Per-minute limit (`RATE_LIMIT_EXCEEDED`, header `Retry-After: 60`)
            or, on the free plan, the daily limit (`DAILY_LIMIT_EXCEEDED`,
            header `X-RateLimit-Reset: midnight UTC`).
          headers:
            Retry-After:
              description: Seconds to wait (per-minute limit only).
              schema:
                type: integer
            X-RateLimit-Limit:
              schema:
                type: integer
            X-RateLimit-Remaining:
              schema:
                type: integer
            X-RateLimit-Reset:
              description: 'Daily limit only: the literal `midnight UTC`.'
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                per_minute:
                  value:
                    error: Rate limit exceeded
                    code: RATE_LIMIT_EXCEEDED
                    message: >-
                      Public endpoint: per-IP limit reached. Wait 37 s
                      (Retry-After), or use an API key for higher limits.
                    docs_url: https://consensus.cryptoquant.com/docs/api#errors
                    retry_after: 37
                daily:
                  value:
                    error: Daily API limit exceeded
                    code: DAILY_LIMIT_EXCEEDED
                    message: >-
                      Free plan: the daily request quota is used up. It resets
                      at 00:00 UTC; the Premium and Enterprise plans have no
                      daily cap.
                    docs_url: https://consensus.cryptoquant.com/docs/api#errors
                    limit: 100
                    used: 100
                    reset: Daily at midnight UTC
                    upgrade_url: https://cryptoquant.com/pricing
                    contact_url: https://cryptoquant.com/get-in-touch
        '500':
          description: '`INTERNAL_ERROR` — retry once after a few seconds.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error: Internal server error
                code: INTERNAL_ERROR
                message: >-
                  Something went wrong on our side. Retry once after a few
                  seconds; if it persists, report the URL to support.
                docs_url: https://consensus.cryptoquant.com/docs/api#errors
      x-codeSamples:
        - lang: Shell
          source: >-
            curl -X GET
            "https://consensus.cryptoquant.com/api/v1/sentiment?handle=caprioleio&asset=BTC&days=30"
            \

            -H "X-API-Key: <YOUR_API_KEY>"
        - lang: JavaScript
          source: >-
            fetch("https://consensus.cryptoquant.com/api/v1/sentiment?handle=caprioleio&asset=BTC&days=30",
            { headers: { "X-API-Key": "<YOUR_API_KEY>"} })
              .then(response => response.json())
              .then(data => console.log(data))
        - lang: NodeJS
          source: |-
            require('axios')
              .get("https://consensus.cryptoquant.com/api/v1/sentiment?handle=caprioleio&asset=BTC&days=30", { headers: { 'X-API-Key': '<YOUR_API_KEY>' } })
              .then(response => console.log(response))
        - lang: Ruby
          source: >-
            require 'net/http'

            uri =
            URI("https://consensus.cryptoquant.com/api/v1/sentiment?handle=caprioleio&asset=BTC&days=30")

            req = Net::HTTP::Get.new(uri)

            req["X-API-Key"] = "<YOUR_API_KEY>"

            res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) {
            |http| http.request(req) }

            puts res.body
        - lang: Python
          source: >-
            import requests

            headers = {'X-API-Key': '<YOUR_API_KEY>'}

            url =
            "https://consensus.cryptoquant.com/api/v1/sentiment?handle=caprioleio&asset=BTC&days=30"

            print(requests.get(url, headers=headers).json())
components:
  schemas:
    StanceSeries:
      type: object
      description: 'Premium / Enterprise plans: the requested window, oldest first.'
      required:
        - analyst_id
        - asset
        - period
        - count
        - data
      properties:
        analyst_id:
          type: string
          format: uuid
        asset:
          type: string
          description: Canonical symbol.
        period:
          type: object
          required:
            - start
            - end
          properties:
            start:
              type: string
              format: date
            end:
              type: string
              format: date
              description: Yesterday UTC.
        count:
          type: integer
          minimum: 0
        data:
          type: array
          items:
            $ref: '#/components/schemas/StancePoint'
    StanceLatest:
      type: object
      description: >-
        Reserved free-plan shape (not served since 2026-10-06): the latest
        end-of-day point as a flat object.
      required:
        - analyst_id
        - asset
        - date
        - plan
      properties:
        analyst_id:
          type: string
          format: uuid
        asset:
          type: string
        date:
          type: string
          format: date
          description: UTC calendar day.
        stance:
          type:
            - number
            - 'null'
          minimum: -100
          maximum: 100
          description: >-
            Canonical analyst stance on the asset for the day (−100 bearish …
            +100 bullish). Present only while the stance pipeline flag is on.
        confidence:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 1
          description: Confidence of `stance`, 0–1 (present with it).
        post_count:
          type:
            - integer
            - 'null'
          minimum: 0
          description: Directional posts behind `stance` (present with it).
        sentiment_score:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 100
          description: >-
            0 (bearish) … 100 (bullish), integer-rounded. With the stance
            pipeline on it is the alias round((stance + 100) / 2).
        sentiment_score_key_calls:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 100
          deprecated: true
          description: >-
            Score over golden (explicit price-direction) calls only; null when
            none, always null once the stance pipeline is on.
        key_calls_count:
          type:
            - integer
            - 'null'
          minimum: 0
          description: >-
            Golden calls that day; aliases post_count once the stance pipeline
            is on.
        plan:
          type: string
          enum:
            - free
        upgrade_message:
          type: string
    Error:
      type: object
      description: >-
        Every non-2xx body. Branch on `code`; `error` is the legacy short reason
        kept for pre-2026-10-06 clients.
      required:
        - error
        - code
        - message
        - docs_url
      properties:
        error:
          type: string
          description: >-
            Legacy short reason ("Invalid asset", "API key required", "Rate
            limit exceeded" …), unchanged wording.
        code:
          type: string
          enum:
            - API_KEY_REQUIRED
            - INVALID_API_KEY
            - PLAN_REQUIRED
            - RAW_ACCESS_REQUIRED
            - DAILY_LIMIT_EXCEEDED
            - RATE_LIMIT_EXCEEDED
            - INVALID_PARAMETER
            - UNKNOWN_ASSET
            - ANALYST_NOT_FOUND
            - ANALYST_NOT_ELIGIBLE
            - NO_DATA
            - SERVICE_UNAVAILABLE
            - INTERNAL_ERROR
          description: >-
            Stable machine code. Status and recovery per code: see `x-errors` at
            the document root.
        message:
          type: string
          description: Human explanation, with the accepted values for a parameter error.
        docs_url:
          type: string
          format: uri
          description: >-
            Where the error is documented (/docs/api#errors, or #plans for
            PLAN_REQUIRED).
        param:
          type: string
          description: The offending query parameter (INVALID_PARAMETER, UNKNOWN_ASSET).
        required_plans:
          type: array
          items:
            type: string
            enum:
              - premium
              - enterprise
          description: Plans that may call the endpoint (PLAN_REQUIRED).
        plan:
          type: string
          description: The key's current plan label, upper-cased (PLAN_REQUIRED).
        upgrade_url:
          type: string
          format: uri
          description: Self-serve upgrade (PLAN_REQUIRED, DAILY_LIMIT_EXCEEDED).
        contact_url:
          type: string
          format: uri
          description: Enterprise / sales contact (PLAN_REQUIRED, DAILY_LIMIT_EXCEEDED).
        limit:
          type:
            - integer
            - 'null'
          description: Daily quota (DAILY_LIMIT_EXCEEDED).
        used:
          type:
            - integer
            - 'null'
          description: Requests used today (DAILY_LIMIT_EXCEEDED).
        reset:
          type: string
          description: When the quota resets ("Daily at midnight UTC").
        retry_after:
          type: integer
          minimum: 1
          description: Seconds to wait (RATE_LIMIT_EXCEEDED; also the Retry-After header).
        docs:
          type: string
          format: uri
          deprecated: true
          description: Legacy alias of docs_url on API_KEY_REQUIRED.
        upgradeUrl:
          type: string
          format: uri
          deprecated: true
          description: Legacy alias of upgrade_url on PLAN_REQUIRED.
    StancePoint:
      type: object
      required:
        - date
        - sentiment_score
        - sentiment_score_key_calls
        - key_calls_count
      properties:
        date:
          type: string
          format: date
          description: UTC calendar day.
        stance:
          type:
            - number
            - 'null'
          minimum: -100
          maximum: 100
          description: >-
            Canonical analyst stance on the asset for the day (−100 bearish …
            +100 bullish). Present only while the stance pipeline flag is on.
        confidence:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 1
          description: Confidence of `stance`, 0–1 (present with it).
        post_count:
          type:
            - integer
            - 'null'
          minimum: 0
          description: Directional posts behind `stance` (present with it).
        sentiment_score:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 100
          description: >-
            0 (bearish) … 100 (bullish), integer-rounded. With the stance
            pipeline on it is the alias round((stance + 100) / 2).
        sentiment_score_key_calls:
          type:
            - number
            - 'null'
          minimum: 0
          maximum: 100
          deprecated: true
          description: >-
            Score over golden (explicit price-direction) calls only; null when
            none, always null once the stance pipeline is on.
        key_calls_count:
          type:
            - integer
            - 'null'
          minimum: 0
          description: >-
            Golden calls that day; aliases post_count once the stance pipeline
            is on.
  securitySchemes:
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        Provisioned with the CryptoQuant Premium plan or an Enterprise agreement
        (no self-serve). Keys start with `unbias_live_`.
    ApiKeyQuery:
      type: apiKey
      in: query
      name: api_key
      description: Same key as a query parameter (avoid in logged URLs).

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.