{
 "openapi": "3.1.0",
 "jsonSchemaDialect": "https://spec.openapis.org/oas/3.1/dialect/base",
 "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.\n\n**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.\n\n**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).\n\n**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`.\n\n**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).\n\nHuman 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"
  }
 },
 "externalDocs": {
  "description": "Sentiment Data documentation (the human rendering of this document)",
  "url": "https://docs.cryptoquant.com/sentiment/overview"
 },
 "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."
  }
 ],
 "x-plans": {
  "description": "API-key plans (subscription_plans). An API key carries its own plan; the site session plan is separate. `pro` is the pre-2026-10 Enterprise row and behaves as `enterprise`. raw_source_text is NOT a plan right: every plan row receives the excerpt (excerpt only: the first ≤ 200 characters of the original post (sentence / word boundary, original language) + the source url); only a key cleared under an Integration agreement (per-key raw_access — see x-raw-source-text) receives the original statement (original statement (full post title / text) — Integration agreement keys only (per-key raw_access clearance, not a plan); contact sales@cryptoquant.com).",
  "plans": {
   "none": {
    "label": "No key",
    "endpoints": [
     "/assets",
     "/consensus (latest point)",
     "/analyst-views",
     "/calls",
     "/analysts/top",
     "/analysts/`{handle}`"
    ],
    "rate_limit_per_minute": 60,
    "daily_request_limit": null,
    "history": "latest point only",
    "raw_source_text": "excerpt"
   },
   "free": {
    "label": "Free",
    "endpoints": [
     "everything public",
     "/consensus (latest point only, flat object)",
     "/consensus/breakdown (sources as excerpts)"
    ],
    "rate_limit_per_minute": 10,
    "daily_request_limit": 100,
    "max_days_history": 7,
    "history": "latest point only; /sentiment → 401 PLAN_REQUIRED",
    "raw_source_text": "excerpt"
   },
   "premium": {
    "label": "Premium",
    "endpoints": [
     "everything (breakdown sources as excerpts unless the key holds Integration raw access)"
    ],
    "rate_limit_per_minute": 1000,
    "daily_request_limit": null,
    "max_days_history": 36500,
    "history": "full",
    "raw_source_text": "excerpt",
    "how_to_get": "https://cryptoquant.com/pricing"
   },
   "enterprise": {
    "label": "Enterprise",
    "endpoints": [
     "everything (breakdown sources as excerpts unless the key holds Integration raw access)"
    ],
    "rate_limit_per_minute": 1000,
    "daily_request_limit": null,
    "max_days_history": 36500,
    "history": "full",
    "raw_source_text": "excerpt",
    "how_to_get": "https://cryptoquant.com/get-in-touch"
   },
   "pro": {
    "label": "Enterprise (legacy row)",
    "same_as": "enterprise",
    "raw_source_text": "excerpt"
   }
  },
  "raw_source_text": {
   "statement": "original statement (full post title / text) — Integration agreement keys only (per-key raw_access clearance, not a plan); contact sales@cryptoquant.com",
   "excerpt": "excerpt only: the first ≤ 200 characters of the original post (sentence / word boundary, original language) + the source url",
   "field": "BreakdownResponse.source_text names the shape a response carries; /analyst-views and /calls are always excerpts",
   "integration_key": "statement"
  },
  "plan_ids": [
   "free",
   "pro",
   "premium",
   "enterprise"
  ]
 },
 "x-raw-source-text": {
  "field": "GET /consensus/breakdown → sources[].statement (BreakdownSource); every other surface carries sources[].excerpt / excerpt",
  "access": "Integration agreement only — granted per API key (api_keys.raw_access = true), never by plan: a Premium, Enterprise or admin-owned key without the clearance receives the excerpt + source url",
  "how_to_get": "Integration use that needs the original source text: contact sales@cryptoquant.com",
  "contact_email": "sales@cryptoquant.com",
  "contact_url": "https://cryptoquant.com/get-in-touch",
  "request": "source_text=statement on /consensus/breakdown asks for it explicitly; without the clearance the answer is 403 RAW_ACCESS_REQUIRED (contact_email, docs_url) — the default (no parameter) is what the key may receive",
  "excerpt_rule": "excerpt only: the first ≤ 200 characters of the original post (sentence / word boundary, original language) + the source url",
  "docs_url": "https://consensus.cryptoquant.com/docs/api#raw-source-text"
 },
 "x-errors": {
  "API_KEY_REQUIRED": {
   "status": 401,
   "recovery": "Send the key in the X-API-Key header (or the api_key query parameter). The key-free endpoints (/assets, /analysts/*, /analyst-views, /calls, /consensus without days) need none."
  },
  "INVALID_API_KEY": {
   "status": 401,
   "recovery": "The key is unknown, inactive, expired, or its subscription is not active. Do not retry with the same key; obtain a valid one."
  },
  "PLAN_REQUIRED": {
   "status": 401,
   "recovery": "The key's plan cannot read this endpoint. required_plans names the plans that can: upgrade at upgrade_url or contact sales at contact_url. Do not retry on the same plan."
  },
  "RAW_ACCESS_REQUIRED": {
   "status": 403,
   "recovery": "The original source statement (full post text) is provided under an Integration agreement only, per key (not a plan: premium, enterprise and admin-owned keys without it get the excerpt). Drop source_text=statement to receive the ≤ 200-character excerpt + source link, or contact sales@cryptoquant.com (contact_email) for Integration raw-text access. Do not retry unchanged."
  },
  "DAILY_LIMIT_EXCEEDED": {
   "status": 429,
   "recovery": "Free plan daily quota used (limit / used). It resets at 00:00 UTC; wait, or upgrade."
  },
  "RATE_LIMIT_EXCEEDED": {
   "status": 429,
   "recovery": "Per-minute (keyed: your plan's rate_limit_per_minute; key-free: per-IP) limit hit. Wait retry_after seconds (header Retry-After), then retry; spread calls over the minute."
  },
  "INVALID_PARAMETER": {
   "status": 400,
   "recovery": "A query parameter is malformed or outside its accepted values: param names it and message lists what is accepted. Fix the request; never retry it unchanged."
  },
  "UNKNOWN_ASSET": {
   "status": 400,
   "recovery": "The asset is not registered. Resolve the name or ticker through GET /api/v1/assets (symbol, key, name, aliases) and retry with that symbol."
  },
  "ANALYST_NOT_FOUND": {
   "status": 404,
   "recovery": "No tracked analyst matches. Pick a handle from GET /api/v1/analysts/top?asset=<symbol> (field handle) and retry."
  },
  "ANALYST_NOT_ELIGIBLE": {
   "status": 404,
   "recovery": "The analyst exists but is excluded from the stance pipeline (feed, company account, stakeholder). Not retryable."
  },
  "NO_DATA": {
   "status": 404,
   "recovery": "No rows for that analyst × asset pair in the queried range. Not retryable; try another asset the analyst covers (covered_assets on /analysts/{handle})."
  },
  "SERVICE_UNAVAILABLE": {
   "status": 503,
   "recovery": "An upstream list was unavailable. Retry after a few seconds with the same request."
  },
  "INTERNAL_ERROR": {
   "status": 500,
   "recovery": "Our fault. Retry once after a few seconds; if it persists, report the full URL to support."
  }
 },
 "x-conventions": {
  "dates": "`date` = UTC calendar day YYYY-MM-DD (format: date). `date-time` = RFC 3339 / ISO 8601 with an explicit offset (`Z` or `+00:00`), always UTC. The current UTC day is never in a series.",
  "pagination": "None. Series return the whole requested range in one response (`days`, capped by the plan). Lists are capped by `limit` (1–50, clamped silently). There is no cursor, offset or page parameter.",
  "filters": "Query filters combine with AND. Unknown parameters are ignored. Enum parameters outside their values → 400 INVALID_PARAMETER with the accepted list in `message`.",
  "sorting": "Fixed per endpoint: series oldest first by date; /calls newest first; /analysts/top by accuracy_score desc (ties: sample size, then handle); /assets in registry order.",
  "enums": "Every enum is closed and listed in full in this document; `GET /assets` returns them at runtime under `enums`.",
  "caching": "Public payloads: CDN 5 min (`as_of` = generation time); track record and assets: CDN 1 h. Keyed responses are never cached."
 },
 "x-mcp": {
  "url": "https://consensus.cryptoquant.com/mcp",
  "transport": "streamable-http",
  "auth": "none",
  "tools": [
   "get_consensus",
   "get_analyst_views",
   "get_top_analysts",
   "get_analyst_profile",
   "get_recent_calls"
  ]
 },
 "x-alternatives": {
  "cryptoquant_alpha_api": {
   "description": "CryptoQuant Premium keys can also read the Analyst Consensus Index (BTC, ETH, SOL; from/to/limit/order; Bearer auth) from the CryptoQuant Alpha Indicator API — same daily index, no breakdown / stance / track record.",
   "url": "https://alpha.cryptoquant.com/api/consensus/{asset}",
   "docs": "https://docs.cryptoquant.com/indicator-api/consensus"
  },
  "llms_txt": "https://consensus.cryptoquant.com/llms.txt",
  "openapi_yaml": "https://consensus.cryptoquant.com/openapi.yaml"
 },
 "paths": {
  "/assets": {
   "get": {
    "operationId": "listAssets",
    "tags": [
     "Registry"
    ],
    "summary": "Every covered asset + every enum (public, no API key)",
    "description": "The registry every `asset` parameter is resolved against — 1712 canonical assets (one underlying = one asset) plus their alias tickers, each with symbol, route key, name, class, category, market, coverage state and page URL — and the closed enums the other endpoints use (`enums`). Registry order (class, then symbol). Optional AND-filters: `asset_class`, `category`, `q` (substring of symbol / name / key). Public; CDN-cached one hour (`Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400`); no DB read.",
    "security": [],
    "x-plan": {
     "access": "public",
     "api_key": "not required",
     "plans": [
      "none",
      "free",
      "premium",
      "enterprise"
     ],
     "rate_limit": "none (CDN-cached)",
     "daily_limit": "none",
     "cache": "CDN 1 h"
    },
    "parameters": [
     {
      "name": "asset_class",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "enum": [
        "crypto",
        "equities",
        "indices",
        "commodities"
       ]
      },
      "description": "Keep only this asset class."
     },
     {
      "name": "category",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "enum": [
        "crypto",
        "us-equities",
        "indices",
        "commodities",
        "kr-equities"
       ]
      },
      "description": "Keep only this selector category (equities split into US / KR)."
     },
     {
      "name": "q",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "Case-insensitive substring of symbol, name or route key.",
      "example": "gold"
     }
    ],
    "responses": {
     "200": {
      "description": "The asset registry and the enums",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/AssetsResponse"
        },
        "example": {
         "count": 2,
         "rows": 3,
         "filter": {
          "asset_class": null,
          "category": null,
          "q": null
         },
         "asset_classes": {
          "crypto": "Crypto",
          "equities": "Equities",
          "indices": "Indices & ETFs",
          "commodities": "Commodities"
         },
         "categories": {
          "crypto": "Crypto",
          "us-equities": "US Equities",
          "indices": "Indices & ETFs",
          "commodities": "Commodities",
          "kr-equities": "KR Equities"
         },
         "how_to_resolve": "Pass any of symbol, key, name, an alias or a retired symbol as `asset` on the other endpoints (case-insensitive); the response always reports the canonical symbol. coverage=pending assets have a page but no analyst data yet.",
         "assets": [
          {
           "symbol": "BTC",
           "name": "Bitcoin",
           "key": "btc",
           "asset_class": "crypto",
           "category": "crypto",
           "market": "Bitfinex",
           "currency": "USD",
           "source": "alpha",
           "coverage": "live",
           "canonical": null,
           "aliases": [],
           "retired_symbols": [],
           "page_url": "https://consensus.cryptoquant.com/consensus/btc",
           "identity": {
            "asset_class": "crypto",
            "uid": "bitcoin",
            "chain_contract": null,
            "exchange_mic": null,
            "needs_review": false
           }
          },
          {
           "symbol": "GLD",
           "name": "Gold",
           "key": "gld",
           "asset_class": "commodities",
           "category": "commodities",
           "market": "Commodity",
           "currency": "USD",
           "source": "alpha",
           "coverage": "mapped",
           "canonical": "XAU",
           "aliases": [],
           "retired_symbols": [],
           "page_url": "https://consensus.cryptoquant.com/consensus/gld",
           "identity": {
            "asset_class": "etf",
            "uid": "US78463V1070",
            "chain_contract": null,
            "exchange_mic": "XNYS",
            "needs_review": false
           }
          },
          {
           "symbol": "005930",
           "name": "Samsung Electronics",
           "key": "005930",
           "asset_class": "equities",
           "category": "kr-equities",
           "market": "KRX",
           "currency": "KRW",
           "source": "alpha",
           "coverage": "mapped",
           "canonical": null,
           "aliases": [],
           "retired_symbols": [],
           "page_url": "https://consensus.cryptoquant.com/consensus/005930",
           "identity": {
            "asset_class": "equity",
            "uid": "KR7005930003",
            "chain_contract": null,
            "exchange_mic": "XKRX",
            "needs_review": false
           }
          }
         ],
         "enums": {
          "asset_class": [
           "crypto",
           "equities",
           "indices",
           "commodities"
          ],
          "asset_category": [
           "crypto",
           "us-equities",
           "indices",
           "commodities",
           "kr-equities"
          ],
          "asset_source": [
           "alpha",
           "sellside",
           "unbias",
           "legacy"
          ],
          "source_platform": [
           "x",
           "cryptoquant",
           "seekingalpha",
           "tradingview",
           "substack",
           "benzinga"
          ],
          "analyst_source": [
           "twitter",
           "cryptoquant",
           "seekingalpha",
           "tradingview",
           "substack",
           "other"
          ],
          "source_type": [
           "tweet",
           "quicktake",
           "research",
           "news",
           "seekingalpha",
           "tradingview",
           "substack",
           "youtube",
           "telegram",
           "bluesky"
          ],
          "sentiment_label": [
           "bullish",
           "bullish_nuance",
           "neutral",
           "bearish_nuance",
           "bearish"
          ],
          "directional_label": [
           "bullish",
           "bullish_nuance",
           "bearish_nuance",
           "bearish"
          ],
          "stance": [
           "bullish",
           "bearish",
           "neutral"
          ],
          "bias": [
           "bullish",
           "bearish",
           "balanced"
          ],
          "tier_badge": [
           "top1",
           "top5",
           "top10",
           "tracked"
          ],
          "tier_reason": [
           "excluded",
           "ineligible",
           "news_feed",
           "company",
           "unscored",
           "insufficient_calls",
           "pool_too_small"
          ],
          "breakdown_window": [
           "1d",
           "3d",
           "7d",
           "30d"
          ],
          "views_window": [
           "7d",
           "30d"
          ],
          "breakdown_lang": [
           "en",
           "ko",
           "zh",
           "ja",
           "es",
           "pt",
           "ru",
           "hi",
           "de",
           "fr",
           "tr",
           "vi",
           "id",
           "it",
           "th",
           "pl",
           "nl",
           "uk"
          ],
          "via": [
           "chatgpt",
           "claude",
           "mcp",
           "gemini",
           "perplexity",
           "api"
          ],
          "plan": [
           "free",
           "pro",
           "premium",
           "enterprise"
          ]
         },
         "docs_url": "https://consensus.cryptoquant.com/docs/api#assets",
         "as_of": "2026-10-06T07:48:29.000Z"
        }
       }
      }
     },
     "400": {
      "description": "`INVALID_PARAMETER` — asset_class / category outside their enum",
      "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"
        }
       }
      }
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "source": "curl -X GET \"https://consensus.cryptoquant.com/api/v1/assets?category=commodities\""
     },
     {
      "lang": "JavaScript",
      "source": "fetch(\"https://consensus.cryptoquant.com/api/v1/assets?category=commodities\")\n  .then(response => response.json())\n  .then(data => console.log(data))"
     },
     {
      "lang": "NodeJS",
      "source": "require('axios')\n  .get(\"https://consensus.cryptoquant.com/api/v1/assets?category=commodities\")\n  .then(response => console.log(response))"
     },
     {
      "lang": "Ruby",
      "source": "require 'net/http'\nuri = URI(\"https://consensus.cryptoquant.com/api/v1/assets?category=commodities\")\nres = Net::HTTP.get_response(uri)\nputs res.body"
     },
     {
      "lang": "Python",
      "source": "import requests\nurl = \"https://consensus.cryptoquant.com/api/v1/assets?category=commodities\"\nprint(requests.get(url).json())"
     }
    ]
   }
  },
  "/consensus": {
   "get": {
    "operationId": "getConsensusIndex",
    "tags": [
     "Consensus"
    ],
    "summary": "Analyst Consensus Index — latest point (public) or daily series (API key)",
    "description": "WITHOUT a key (the agent path): the latest end-of-day Analyst Consensus Index point for ANY registered asset — `consensus_index` (−100 all bearish … +100 all bullish), `consensus_index_30d_ma`, the bullish / bearish share, `total_analysts`, the one-line `bull_case` / `bear_case`, plus `page_url` / `cite_as` / `data_by` (`ConsensusPublic`, `plan:\"public\"`). Public, no API key. Per-IP limit 60 requests / minute (per function instance); CDN-cached 5 minutes (`Cache-Control: public, s-maxage=300, stale-while-revalidate=3600`). Use this for \"what do analysts think about X / is the market bullish on X\".\n\nWITH a key: the daily series for ANY registered asset (one series per asset the index publisher writes — crypto since 2021-06-01, equities / indices / commodities from their first coverage day; an asset without rows answers an empty series, `count: 0`) with the 30-day MA, a 90-day z-score, analyst and opinion counts, oldest first. Premium / Enterprise keys get `days` of history ending yesterday UTC (capped at the plan's max_days_history, 36,500 = everything); the FREE plan gets ONLY the latest point as a flat object with `plan:\"free\"` (`days` ignored). No pagination: the whole range is one response.",
    "security": [
     {},
     {
      "ApiKeyHeader": []
     },
     {
      "ApiKeyQuery": []
     }
    ],
    "x-plan": {
     "access": "public_or_api_key",
     "api_key": "optional: none = latest point, key = history",
     "plans": [
      "none",
      "free",
      "premium",
      "enterprise"
     ],
     "rate_limit": "free 10 / min, premium + enterprise 1000 / min",
     "daily_limit": "free 100 / UTC day, paid none",
     "cache": "none"
    },
    "parameters": [
     {
      "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": "NVDA"
     },
     {
      "name": "days",
      "in": "query",
      "required": false,
      "description": "Keyed only — days of history ending yesterday UTC. Positive integer (anything else → 400 INVALID_PARAMETER); silently capped at the plan's max_days_history (free 7, premium / enterprise 36500). Ignored without a key and on the free plan's single point.",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 36500,
       "default": 7
      },
      "example": 365
     },
     {
      "name": "granularity",
      "in": "query",
      "required": false,
      "description": "Keyed only — `daily` is the only value (there is no hourly series); anything else → 400 INVALID_PARAMETER.",
      "schema": {
       "type": "string",
       "enum": [
        "daily"
       ],
       "default": "daily"
      }
     },
     {
      "name": "via",
      "in": "query",
      "required": false,
      "description": "The surface the agent runs on; sets `utm_source` on every link in the payload. Unknown values fall back to the default.",
      "schema": {
       "type": "string",
       "enum": [
        "chatgpt",
        "claude",
        "mcp",
        "gemini",
        "perplexity",
        "api"
       ],
       "default": "chatgpt"
      },
      "example": "claude"
     }
    ],
    "responses": {
     "200": {
      "description": "Latest point (no key: ConsensusPublic), series (premium / enterprise: ConsensusSeries) or latest point (free: ConsensusLatest). The three shapes are told apart by `plan` (public / absent / free).",
      "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/ConsensusPublic"
          },
          {
           "$ref": "#/components/schemas/ConsensusSeries"
          },
          {
           "$ref": "#/components/schemas/ConsensusLatest"
          }
         ]
        },
        "examples": {
         "public_no_key": {
          "summary": "No key — latest point for NVDA",
          "value": {
           "asset": "NVDA",
           "asset_name": "NVIDIA",
           "asset_class": "equities",
           "date": "2026-10-05",
           "consensus_index": 71.28,
           "consensus_index_30d_ma": 75.85,
           "bullish_percent": 86,
           "bearish_percent": 14,
           "total_analysts": 62,
           "bull_case": "Early AI buildout plus cheap valuation fuels growth",
           "bear_case": "Limited upside ahead; take profits and rotate",
           "window": "30d",
           "plan": "public",
           "history": "The daily index history needs an API key (X-API-Key) — https://consensus.cryptoquant.com/docs/api",
           "page_url": "https://consensus.cryptoquant.com/consensus/nvda?utm_source=api&utm_medium=gpt-action&utm_campaign=consensus",
           "top_analysts_url": "https://consensus.cryptoquant.com/analysts?asset=nvda&sort=accuracy&utm_source=api&utm_medium=gpt-action&utm_campaign=consensus",
           "methodology_url": "https://consensus.cryptoquant.com/methodology?utm_source=api&utm_medium=gpt-action&utm_campaign=consensus",
           "cite_as": "Data by CryptoQuant Consensus — https://consensus.cryptoquant.com/consensus/nvda",
           "data_by": "Data by CryptoQuant Consensus",
           "as_of": "2026-10-06T07:48:24.833Z"
          }
         },
         "series_paid_key": {
          "summary": "Premium / Enterprise key — daily series",
          "value": {
           "asset": "BTC",
           "period": {
            "start": "2026-10-04",
            "end": "2026-10-05"
           },
           "granularity": "daily",
           "count": 2,
           "data": [
            {
             "date": "2026-10-04",
             "consensus_index": 48.73,
             "consensus_index_30d_ma": 40.2,
             "z_score": 1.09,
             "avg_sentiment_score": 71.93,
             "bullish_analysts": 122,
             "bearish_analysts": 38,
             "total_analysts": 160,
             "bullish_opinions": 20,
             "bearish_opinions": 7,
             "total_opinions": 27
            },
            {
             "date": "2026-10-05",
             "consensus_index": 49.54,
             "consensus_index_30d_ma": 40.37,
             "z_score": 1.12,
             "avg_sentiment_score": 72.22,
             "bullish_analysts": 125,
             "bearish_analysts": 35,
             "total_analysts": 160,
             "bullish_opinions": 26,
             "bearish_opinions": 3,
             "total_opinions": 29
            }
           ]
          }
         },
         "latest_free_key": {
          "summary": "Free key — latest point only",
          "value": {
           "asset": "BTC",
           "date": "2026-10-05",
           "consensus_index": 49.54,
           "consensus_index_30d_ma": 40.37,
           "z_score": 1.12,
           "avg_sentiment_score": 72.22,
           "bullish_analysts": 125,
           "bearish_analysts": 35,
           "total_analysts": 160,
           "bullish_opinions": 26,
           "bearish_opinions": 3,
           "total_opinions": 29,
           "plan": "free",
           "upgrade_message": "Contact us for Enterprise access to full historical data"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "`UNKNOWN_ASSET` (asset not registered / an aggregate) or `INVALID_PARAMETER` (days, granularity)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "error": "Unknown asset",
         "code": "UNKNOWN_ASSET",
         "message": "\"DOGE2\" is not a registered asset. Use a ticker, route key or name from https://consensus.cryptoquant.com/api/v1/assets (symbol / key / name / aliases, case-insensitive). Market aggregates are not assets.",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#errors",
         "param": "asset"
        }
       }
      }
     },
     "401": {
      "description": "Keyed path only: `INVALID_API_KEY`. A request WITHOUT a key is never 401 — it gets the public snapshot.",
      "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"
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "Free plan only: `NO_DATA` when the last 90 days hold no rows",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "error": "No data available",
         "code": "NO_DATA",
         "message": "No index rows for this asset in the last 90 days.",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#errors"
        }
       }
      }
     },
     "429": {
      "description": "No key: per-IP limit (`RATE_LIMIT_EXCEEDED`, `Retry-After`). Key: per-minute limit, or on the free plan the daily limit (`DAILY_LIMIT_EXCEEDED`).",
      "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/consensus?asset=BTC&days=365\" \\\n-H \"X-API-Key: <YOUR_API_KEY>\""
     },
     {
      "lang": "JavaScript",
      "source": "fetch(\"https://consensus.cryptoquant.com/api/v1/consensus?asset=BTC&days=365\", { headers: { \"X-API-Key\": \"<YOUR_API_KEY>\"} })\n  .then(response => response.json())\n  .then(data => console.log(data))"
     },
     {
      "lang": "NodeJS",
      "source": "require('axios')\n  .get(\"https://consensus.cryptoquant.com/api/v1/consensus?asset=BTC&days=365\", { headers: { 'X-API-Key': '<YOUR_API_KEY>' } })\n  .then(response => console.log(response))"
     },
     {
      "lang": "Ruby",
      "source": "require 'net/http'\nuri = URI(\"https://consensus.cryptoquant.com/api/v1/consensus?asset=BTC&days=365\")\nreq = Net::HTTP::Get.new(uri)\nreq[\"X-API-Key\"] = \"<YOUR_API_KEY>\"\nres = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }\nputs res.body"
     },
     {
      "lang": "Python",
      "source": "import requests\nheaders = {'X-API-Key': '<YOUR_API_KEY>'}\nurl = \"https://consensus.cryptoquant.com/api/v1/consensus?asset=BTC&days=365\"\nprint(requests.get(url, headers=headers).json())"
     }
    ]
   }
  },
  "/consensus/breakdown": {
   "get": {
    "operationId": "getConsensusBreakdown",
    "tags": [
     "Consensus"
    ],
    "summary": "Per-asset consensus breakdown — snapshot (API key, any plan; the original source text only on a key with Integration raw access)",
    "description": "One point-in-time snapshot of an asset's analyst consensus for a window: directional opinion counts and shares, the latest daily index + z-score (null for an asset without an index series), the bull-case / bear-case sentences, the viewpoints grouped by thesis with their analysts and source posts. `lang` localises the generated text (titles, cases); source posts stay in their original language. Recomputed daily; opinion counts every 4 hours. Any plan, including free (counts against the free daily limit). **Source text per key** (`source_text` + `note` in the response): a key cleared under an Integration agreement (per-key raw_access, not a plan) gets `sources[].statement` = the original post title / text; every other key — Free, Premium, Enterprise, admin-owned — gets `sources[].excerpt` = the first ≤ 200 characters + `url` instead. `source_text=statement` asks for the original explicitly and is refused with 403 `RAW_ACCESS_REQUIRED` on a key without the clearance (Integration use: contact sales@cryptoquant.com). Viewpoint `title` / `thesis`, the bull / bear case, the index and the labels are identical on every plan.",
    "x-plan": {
     "access": "api_key",
     "api_key": "required",
     "plans": [
      "free",
      "premium",
      "enterprise"
     ],
     "rate_limit": "free 10 / min, premium + enterprise 1000 / min",
     "daily_limit": "free 100 / UTC day, paid none",
     "cache": "none",
     "raw_source_text": {
      "free": "excerpt",
      "premium": "excerpt",
      "enterprise": "excerpt",
      "pro": "excerpt",
      "admin": "excerpt",
      "integration_key": "statement"
     },
     "raw_source_text_rule": {
      "statement": "original statement (full post title / text) — Integration agreement keys only (per-key raw_access clearance, not a plan); contact sales@cryptoquant.com",
      "excerpt": "excerpt only: the first ≤ 200 characters of the original post (sentence / word boundary, original language) + the source url"
     },
     "raw_source_text_contact": "sales@cryptoquant.com"
    },
    "parameters": [
     {
      "name": "asset",
      "in": "query",
      "required": false,
      "description": "Asset SYMBOL, upper-cased by the server (`btc` → `BTC`); NOT resolved through the registry — route keys / names are not accepted here, and an unknown symbol comes back as an empty breakdown (no viewpoints, null index), not a 400. Use the `symbol` from `GET /assets`.",
      "schema": {
       "type": "string",
       "default": "BTC"
      },
      "example": "BTC"
     },
     {
      "name": "window",
      "in": "query",
      "required": false,
      "description": "Window of the opinion counts and viewpoints.",
      "schema": {
       "type": "string",
       "enum": [
        "1d",
        "3d",
        "7d",
        "30d"
       ],
       "default": "3d"
      }
     },
     {
      "name": "lang",
      "in": "query",
      "required": false,
      "description": "Output language of the generated text (bull / bear case, viewpoint titles); a missing translation falls back to English.",
      "schema": {
       "type": "string",
       "enum": [
        "en",
        "ko",
        "zh",
        "ja",
        "es",
        "pt",
        "ru",
        "hi",
        "de",
        "fr",
        "tr",
        "vi",
        "id",
        "it",
        "th",
        "pl",
        "nl",
        "uk"
       ],
       "default": "en"
      }
     },
     {
      "name": "source_text",
      "in": "query",
      "required": false,
      "description": "Which source-text field to receive. Omitted = what the key may receive (statement on a key with Integration raw access, excerpt otherwise). `statement` on a key without the clearance → 403 RAW_ACCESS_REQUIRED (contact sales@cryptoquant.com); `excerpt` is always allowed.",
      "schema": {
       "type": "string",
       "enum": [
        "statement",
        "excerpt"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Breakdown snapshot — `source_text` says which source field the key received",
      "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": {
         "$ref": "#/components/schemas/BreakdownResponse"
        },
        "examples": {
         "integration": {
          "summary": "Key with Integration raw access (api_keys.raw_access): sources[].statement",
          "value": {
           "asset": {
            "symbol": "BTC",
            "name": "Bitcoin"
           },
           "updated_at": "2026-10-06T03:34:55+00:00",
           "window": "3d",
           "lang": "en",
           "source_text": "statement",
           "note": "sources[].statement = the original post title / text (this key holds Integration raw-text access). Viewpoint title / thesis, bull / bear case, index and labels are the same on every plan.",
           "summary": {
            "total_opinions": 56,
            "bullish_opinions": 46,
            "bearish_opinions": 10,
            "bullish_pct": 82,
            "bearish_pct": 18,
            "consensus_index": 49.54,
            "z_score": 1.12
           },
           "overall": {
            "bull_case": {
             "title": "Regulatory wins and dovish data fuel breakout"
            },
            "bear_case": {
             "title": "Macro pressure and resistance threaten deeper pullback"
            }
           },
           "analyst_consensus": {
            "bullish": [
             {
              "id": "a3f1c2d4-7b8e-4a90-9c1d-2e3f4a5b6c7d",
              "stance": "bullish",
              "analyst_count": 2,
              "title": "BTC's first weekly close above the 50WMA in 45 weeks: bear market lows look in",
              "thesis": "50WMA reclaim",
              "analysts": [
               {
                "handle": "CryptoMichNL",
                "display_name": "Michael van de Poppe",
                "avatar_url": "https://pbs.twimg.com/profile_images/1890745133325676544/kcXk6nZx_400x400.jpg",
                "x_url": "https://x.com/CryptoMichNL",
                "profile_url": null
               },
               {
                "handle": "the_daily_digits",
                "display_name": "The Daily Digits",
                "avatar_url": null,
                "x_url": null,
                "profile_url": "https://cryptoquant.com/profile/u/QKJizHT"
               }
              ],
              "sources": [
               {
                "analyst_handle": "CryptoMichNL",
                "statement": "What to expect from #Bitcoin? Honestly, I don't think we're done with the run.",
                "url": "https://x.com/CryptoMichNL/status/2103900910501990879",
                "source_type": "tweet",
                "published_at": "2026-09-26T17:34:00+00:00"
               },
               {
                "analyst_handle": "the_daily_digits",
                "statement": "$2.1B sits on $95K BTC calls for Oct 30, Deribit's biggest strike.",
                "url": "https://cryptoquant.com/insights/quicktake/6ac453b68fa8e62507c0bf1b",
                "source_type": "quicktake",
                "published_at": "2026-10-06T01:49:42+00:00"
               }
              ]
             }
            ],
            "bearish": []
           }
          }
         },
         "premium": {
          "summary": "Any other key — Premium / Free / Enterprise / admin without the clearance: sources[].excerpt (≤ 200 characters) + url",
          "value": {
           "asset": {
            "symbol": "BTC",
            "name": "Bitcoin"
           },
           "updated_at": "2026-10-06T03:34:55+00:00",
           "window": "3d",
           "lang": "en",
           "source_text": "excerpt",
           "note": "sources[].excerpt = the first ≤ 200 characters of the original post (sentence / word boundary, original language) + url; the full statement is provided under an Integration agreement only — contact sales@cryptoquant.com. Viewpoint title / thesis, bull / bear case, index and labels are the same on every plan.",
           "summary": {
            "total_opinions": 56,
            "bullish_opinions": 46,
            "bearish_opinions": 10,
            "bullish_pct": 82,
            "bearish_pct": 18,
            "consensus_index": 49.54,
            "z_score": 1.12
           },
           "overall": {
            "bull_case": {
             "title": "Regulatory wins and dovish data fuel breakout"
            },
            "bear_case": {
             "title": "Macro pressure and resistance threaten deeper pullback"
            }
           },
           "analyst_consensus": {
            "bullish": [
             {
              "id": "a3f1c2d4-7b8e-4a90-9c1d-2e3f4a5b6c7d",
              "stance": "bullish",
              "analyst_count": 2,
              "title": "BTC's first weekly close above the 50WMA in 45 weeks: bear market lows look in",
              "thesis": "50WMA reclaim",
              "analysts": [
               {
                "handle": "CryptoMichNL",
                "display_name": "Michael van de Poppe",
                "avatar_url": "https://pbs.twimg.com/profile_images/1890745133325676544/kcXk6nZx_400x400.jpg",
                "x_url": "https://x.com/CryptoMichNL",
                "profile_url": null
               },
               {
                "handle": "the_daily_digits",
                "display_name": "The Daily Digits",
                "avatar_url": null,
                "x_url": null,
                "profile_url": "https://cryptoquant.com/profile/u/QKJizHT"
               }
              ],
              "sources": [
               {
                "analyst_handle": "CryptoMichNL",
                "excerpt": "What to expect from #Bitcoin? Honestly, I don't think we're done with the run.",
                "url": "https://x.com/CryptoMichNL/status/2103900910501990879",
                "source_type": "tweet",
                "published_at": "2026-09-26T17:34:00+00:00"
               },
               {
                "analyst_handle": "the_daily_digits",
                "excerpt": "$2.1B sits on $95K BTC calls for Oct 30, Deribit's biggest strike.",
                "url": "https://cryptoquant.com/insights/quicktake/6ac453b68fa8e62507c0bf1b",
                "source_type": "quicktake",
                "published_at": "2026-10-06T01:49:42+00:00"
               }
              ]
             }
            ],
            "bearish": []
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "`INVALID_PARAMETER` — window / lang / source_text outside their enum (`message` lists the accepted values)",
      "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`; unknown / inactive / expired key or inactive subscription → `INVALID_API_KEY`. `Authorization: Bearer` is not read.",
      "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"
          }
         }
        }
       }
      }
     },
     "403": {
      "description": "`RAW_ACCESS_REQUIRED` — `source_text=statement` on a key without Integration raw access (per-key clearance, not a plan). Drop the parameter for the excerpt + url, or contact sales@cryptoquant.com (contact_email) for Integration raw-text access.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "error": "Integration agreement required",
         "code": "RAW_ACCESS_REQUIRED",
         "message": "The original source statement (full post text) is provided under an Integration agreement only, per key (not a plan: premium, enterprise and admin-owned keys without it get the excerpt). Drop source_text=statement to receive the ≤ 200-character excerpt + source link, or contact sales@cryptoquant.com (contact_email) for Integration raw-text access. Do not retry unchanged.",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#raw-source-text",
         "contact_email": "sales@cryptoquant.com",
         "contact_url": "https://cryptoquant.com/get-in-touch"
        }
       }
      }
     },
     "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/consensus/breakdown?asset=BTC&window=3d\" \\\n-H \"X-API-Key: <YOUR_API_KEY>\""
     },
     {
      "lang": "JavaScript",
      "source": "fetch(\"https://consensus.cryptoquant.com/api/v1/consensus/breakdown?asset=BTC&window=3d\", { headers: { \"X-API-Key\": \"<YOUR_API_KEY>\"} })\n  .then(response => response.json())\n  .then(data => console.log(data))"
     },
     {
      "lang": "NodeJS",
      "source": "require('axios')\n  .get(\"https://consensus.cryptoquant.com/api/v1/consensus/breakdown?asset=BTC&window=3d\", { headers: { 'X-API-Key': '<YOUR_API_KEY>' } })\n  .then(response => console.log(response))"
     },
     {
      "lang": "Ruby",
      "source": "require 'net/http'\nuri = URI(\"https://consensus.cryptoquant.com/api/v1/consensus/breakdown?asset=BTC&window=3d\")\nreq = Net::HTTP::Get.new(uri)\nreq[\"X-API-Key\"] = \"<YOUR_API_KEY>\"\nres = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }\nputs res.body"
     },
     {
      "lang": "Python",
      "source": "import requests\nheaders = {'X-API-Key': '<YOUR_API_KEY>'}\nurl = \"https://consensus.cryptoquant.com/api/v1/consensus/breakdown?asset=BTC&window=3d\"\nprint(requests.get(url, headers=headers).json())"
     }
    ]
   }
  },
  "/analyst-views": {
   "get": {
    "operationId": "getAnalystViews",
    "tags": [
     "Analyst views"
    ],
    "summary": "What analysts are saying about an asset (public, no API key)",
    "description": "The asset page's Analyst Views as JSON: the bull / bear case, the `narratives` (viewpoints grouped by thesis, with analyst and post counts and a summary) and the `analyst_views` — each tracked analyst with ≥ 3 directional posts and a ≥ 60 % consistent side in the window, with their thesis, ONE quotable `excerpt` (the first ≤ 200 characters of one post, original language — the full statement is provided under an Integration agreement only — per API key, contact sales@cryptoquant.com), its `source_url` and the analyst page (`profile_url`); most active first, at most 12 (`analyst_views_total` says how many qualified). Public, no API key. Per-IP limit 60 requests / minute (per function instance); CDN-cached 5 minutes (`Cache-Control: public, s-maxage=300, stale-while-revalidate=3600`). Backs the MCP tool get_analyst_views. Quote excerpts with their source link; show `data_by`.",
    "security": [],
    "x-plan": {
     "access": "public",
     "api_key": "not required",
     "plans": [
      "none",
      "free",
      "premium",
      "enterprise"
     ],
     "rate_limit": "60 requests / minute per IP",
     "daily_limit": "none",
     "cache": "CDN 5 min"
    },
    "parameters": [
     {
      "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": "NVDA"
     },
     {
      "name": "window",
      "in": "query",
      "required": false,
      "description": "Window of the posts considered.",
      "schema": {
       "type": "string",
       "enum": [
        "7d",
        "30d"
       ],
       "default": "30d"
      }
     },
     {
      "name": "via",
      "in": "query",
      "required": false,
      "description": "The surface the agent runs on; sets `utm_source` on every link in the payload. Unknown values fall back to the default.",
      "schema": {
       "type": "string",
       "enum": [
        "chatgpt",
        "claude",
        "mcp",
        "gemini",
        "perplexity",
        "api"
       ],
       "default": "chatgpt"
      },
      "example": "claude"
     }
    ],
    "responses": {
     "200": {
      "description": "Narratives and per-analyst views",
      "headers": {
       "X-RateLimit-Limit": {
        "description": "Per-IP requests allowed per minute (60).",
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Remaining": {
        "description": "Requests left in the current minute for this IP.",
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/AnalystViewsResponse"
        },
        "example": {
         "asset": "BTC",
         "asset_name": "Bitcoin",
         "window": "30d",
         "bullish_posts": 105,
         "bearish_posts": 62,
         "bullish_pct": 63,
         "bearish_pct": 37,
         "bull_case": "Regulatory wins and dovish data fuel breakout",
         "bear_case": "Macro pressure and resistance threaten deeper pullback",
         "narratives": [
          {
           "theme": "EXCHANGE HACK",
           "headline": "Bitget drained for $387.5M as DPRK hackers spoof backend approvals",
           "stance": "bearish",
           "analyst_count": 8,
           "post_count": 8,
           "summary": "Bitget's breach was first reported at $351.6M and later revised up by about $35M to $387.5M. Attackers used spoofed transaction approvals from a compromised backend rather than stolen keys. The $464M User Protection Fund covers the loss but shrinks to about $76.5M."
          }
         ],
         "analyst_views": [
          {
           "handle": "CryptoMichNL",
           "name": "Michael van de Poppe",
           "stance": "bullish",
           "thesis": "BTC run not over; momentum into October targets ~$90,000 resistance, followed by consolidation and a midterm-driven correction that is a buying opportunity before a new all-time high",
           "excerpt": "What to expect from #Bitcoin?\n\nHonestly, I don't think we're done with the run. Sure, we can have some red weeks in between, but I'm eyeing the $90,000 area as the coming resistance to be, after…",
           "source_url": "https://x.com/CryptoMichNL/status/2103900910501990879",
           "source_type": "tweet",
           "published_at": "2026-09-26T17:34:00+00:00",
           "post_count": 14,
           "consistency_pct": 100,
           "profile_url": "https://consensus.cryptoquant.com/analyst/CryptoMichNL?utm_source=api&utm_medium=gpt-action&utm_campaign=analyst-views"
          }
         ],
         "analyst_views_total": 14,
         "note": "analyst_views = analysts with ≥ 3 directional posts on the asset in the window and a ≥ 60 % consistent side; excerpt = the first ≤ 200 characters of one post (original language), quote it with the source_url — the full statement is provided under an Integration agreement only — contact sales@cryptoquant.com. bullish_pct / bearish_pct are post shares (neutral excluded); the Analyst Consensus Index is on /api/v1/consensus.",
         "page_url": "https://consensus.cryptoquant.com/consensus/btc?utm_source=api&utm_medium=gpt-action&utm_campaign=analyst-views",
         "top_analysts_url": "https://consensus.cryptoquant.com/analysts?asset=btc&sort=accuracy&utm_source=api&utm_medium=gpt-action&utm_campaign=analyst-views",
         "methodology_url": "https://consensus.cryptoquant.com/methodology?utm_source=api&utm_medium=gpt-action&utm_campaign=analyst-views",
         "cite_as": "Data by CryptoQuant Consensus — https://consensus.cryptoquant.com/consensus/btc",
         "data_by": "Data by CryptoQuant Consensus",
         "as_of": "2026-10-06T07:48:25.102Z"
        }
       }
      }
     },
     "400": {
      "description": "`UNKNOWN_ASSET` or `INVALID_PARAMETER` (window)",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "error": "Unknown asset",
         "code": "UNKNOWN_ASSET",
         "message": "\"DOGE2\" is not a registered asset. Use a ticker, route key or name from https://consensus.cryptoquant.com/api/v1/assets (symbol / key / name / aliases, case-insensitive). Market aggregates are not assets.",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#errors",
         "param": "asset"
        }
       }
      }
     },
     "429": {
      "description": "Per-IP limit on the key-free path (60 requests / minute per function instance). Wait `Retry-After` seconds, or use an API key for higher limits.",
      "headers": {
       "Retry-After": {
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Limit": {
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "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
        }
       }
      }
     },
     "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/analyst-views?asset=BTC&window=30d\""
     },
     {
      "lang": "JavaScript",
      "source": "fetch(\"https://consensus.cryptoquant.com/api/v1/analyst-views?asset=BTC&window=30d\")\n  .then(response => response.json())\n  .then(data => console.log(data))"
     },
     {
      "lang": "NodeJS",
      "source": "require('axios')\n  .get(\"https://consensus.cryptoquant.com/api/v1/analyst-views?asset=BTC&window=30d\")\n  .then(response => console.log(response))"
     },
     {
      "lang": "Ruby",
      "source": "require 'net/http'\nuri = URI(\"https://consensus.cryptoquant.com/api/v1/analyst-views?asset=BTC&window=30d\")\nres = Net::HTTP.get_response(uri)\nputs res.body"
     },
     {
      "lang": "Python",
      "source": "import requests\nurl = \"https://consensus.cryptoquant.com/api/v1/analyst-views?asset=BTC&window=30d\"\nprint(requests.get(url).json())"
     }
    ]
   }
  },
  "/calls": {
   "get": {
    "operationId": "getRecentCalls",
    "tags": [
     "Analyst views"
    ],
    "summary": "Newest analyst calls on an asset (public, no API key)",
    "description": "The newest directional calls on one asset, newest first by `published_at`: a call = one bullish / bearish post about the asset by a tracked, eligible analyst (neutral posts are not calls). Each row: analyst, stance, the post's `excerpt` (first ≤ 200 characters, original language — never the full text without a key cleared under an Integration agreement), `source_url`, `published_at`, `profile_url`. Public, no API key. Per-IP limit 60 requests / minute (per function instance); CDN-cached 5 minutes (`Cache-Control: public, s-maxage=300, stale-while-revalidate=3600`). Backs the MCP tool get_recent_calls. History / bulk stays with the keyed API.",
    "security": [],
    "x-plan": {
     "access": "public",
     "api_key": "not required",
     "plans": [
      "none",
      "free",
      "premium",
      "enterprise"
     ],
     "rate_limit": "60 requests / minute per IP",
     "daily_limit": "none",
     "cache": "CDN 5 min"
    },
    "parameters": [
     {
      "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": "NVDA"
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Rows to return, clamped to 1–50 silently; unparsable = 10.",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 50,
       "default": 10
      },
      "example": 20
     },
     {
      "name": "via",
      "in": "query",
      "required": false,
      "description": "The surface the agent runs on; sets `utm_source` on every link in the payload. Unknown values fall back to the default.",
      "schema": {
       "type": "string",
       "enum": [
        "chatgpt",
        "claude",
        "mcp",
        "gemini",
        "perplexity",
        "api"
       ],
       "default": "chatgpt"
      },
      "example": "claude"
     }
    ],
    "responses": {
     "200": {
      "description": "Newest calls",
      "headers": {
       "X-RateLimit-Limit": {
        "description": "Per-IP requests allowed per minute (60).",
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Remaining": {
        "description": "Requests left in the current minute for this IP.",
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/RecentCallsResponse"
        },
        "example": {
         "asset": "BTC",
         "asset_name": "Bitcoin",
         "count": 2,
         "calls": [
          {
           "handle": "trader1sz",
           "name": "TraderSZ",
           "stance": "bearish",
           "excerpt": "now what do you think happens if $BTC takes a little sneeze? https://t.co/E6YKg42FtY",
           "source_url": "https://x.com/trader1sz/status/2107313628827251104",
           "source_type": "tweet",
           "published_at": "2026-10-06T03:34:55+00:00",
           "profile_url": "https://consensus.cryptoquant.com/analyst/trader1sz?utm_source=api&utm_medium=gpt-action&utm_campaign=calls"
          },
          {
           "handle": "the_daily_digits",
           "name": "The Daily Digits",
           "stance": "bullish",
           "excerpt": "$2.1B sits on $95K BTC calls for Oct 30, Deribit's biggest strike.\n\nFutures OI lags 13% below its $29.3B peak.\n\nMax pain sits at $78K, 9% under spot.",
           "source_url": "https://cryptoquant.com/insights/quicktake/6ac453b68fa8e62507c0bf1b",
           "source_type": "quicktake",
           "published_at": "2026-10-06T01:49:42+00:00",
           "profile_url": "https://consensus.cryptoquant.com/analyst/the_daily_digits?utm_source=api&utm_medium=gpt-action&utm_campaign=calls"
          }
         ],
         "definition": "A call = one directional (bullish / bearish) post about this asset by a tracked, eligible analyst; neutral posts are not calls. Newest first. excerpt = the first ≤ 200 characters of the post (original language); quote it with its source_url and the analyst's profile_url — the full statement is provided under an Integration agreement only (contact sales@cryptoquant.com).",
         "page_url": "https://consensus.cryptoquant.com/consensus/btc?utm_source=api&utm_medium=gpt-action&utm_campaign=calls",
         "top_analysts_url": "https://consensus.cryptoquant.com/analysts?asset=btc&sort=accuracy&utm_source=api&utm_medium=gpt-action&utm_campaign=calls",
         "methodology_url": "https://consensus.cryptoquant.com/methodology?utm_source=api&utm_medium=gpt-action&utm_campaign=calls",
         "cite_as": "Data by CryptoQuant Consensus — https://consensus.cryptoquant.com/consensus/btc",
         "data_by": "Data by CryptoQuant Consensus",
         "as_of": "2026-10-06T07:48:25.838Z"
        }
       }
      }
     },
     "400": {
      "description": "`UNKNOWN_ASSET`",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "error": "Unknown asset",
         "code": "UNKNOWN_ASSET",
         "message": "\"DOGE2\" is not a registered asset. Use a ticker, route key or name from https://consensus.cryptoquant.com/api/v1/assets (symbol / key / name / aliases, case-insensitive). Market aggregates are not assets.",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#errors",
         "param": "asset"
        }
       }
      }
     },
     "429": {
      "description": "Per-IP limit on the key-free path (60 requests / minute per function instance). Wait `Retry-After` seconds, or use an API key for higher limits.",
      "headers": {
       "Retry-After": {
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Limit": {
        "schema": {
         "type": "integer"
        }
       },
       "X-RateLimit-Remaining": {
        "schema": {
         "type": "integer"
        }
       }
      },
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "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
        }
       }
      }
     },
     "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/calls?asset=BTC&limit=10\""
     },
     {
      "lang": "JavaScript",
      "source": "fetch(\"https://consensus.cryptoquant.com/api/v1/calls?asset=BTC&limit=10\")\n  .then(response => response.json())\n  .then(data => console.log(data))"
     },
     {
      "lang": "NodeJS",
      "source": "require('axios')\n  .get(\"https://consensus.cryptoquant.com/api/v1/calls?asset=BTC&limit=10\")\n  .then(response => console.log(response))"
     },
     {
      "lang": "Ruby",
      "source": "require 'net/http'\nuri = URI(\"https://consensus.cryptoquant.com/api/v1/calls?asset=BTC&limit=10\")\nres = Net::HTTP.get_response(uri)\nputs res.body"
     },
     {
      "lang": "Python",
      "source": "import requests\nurl = \"https://consensus.cryptoquant.com/api/v1/calls?asset=BTC&limit=10\"\nprint(requests.get(url).json())"
     }
    ]
   }
  },
  "/analysts/top": {
   "get": {
    "operationId": "getTopAnalysts",
    "tags": [
     "Analyst track record"
    ],
    "summary": "Top analysts for an asset by accuracy (public, no API key)",
    "description": "The asset's analysts ranked by accuracy_score among those active in the last three calendar months — the same rows and order as the site's /analysts list sorted by accuracy (ties: sample size, then handle). Public; CDN-cached for one hour (`Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400`). Accuracy scoring covers crypto assets today: other asset classes return `scored: false` with null scores and the list in activity order. Backs the MCP tool get_top_analysts.",
    "security": [],
    "x-plan": {
     "access": "public",
     "api_key": "not required",
     "plans": [
      "none",
      "free",
      "premium",
      "enterprise"
     ],
     "rate_limit": "none (CDN-cached)",
     "daily_limit": "none",
     "cache": "CDN 1 h"
    },
    "parameters": [
     {
      "name": "asset",
      "in": "query",
      "required": false,
      "description": "Registered asset symbol or route key (BTC, ETH, SOL, NVDA, 005930 …), case-insensitive. Unknown → 400 UNKNOWN_ASSET.",
      "schema": {
       "type": "string",
       "default": "BTC"
      },
      "example": "BTC"
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Rows to return, clamped to 1–50 silently; unparsable = 20.",
      "schema": {
       "type": "integer",
       "minimum": 1,
       "maximum": 50,
       "default": 20
      },
      "example": 5
     }
    ],
    "responses": {
     "200": {
      "description": "Ranked analysts",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/TopAnalystsResponse"
        },
        "example": {
         "asset": "BTC",
         "asset_name": "Bitcoin",
         "asset_class": "crypto",
         "as_of": "2026-10-06T07:48:27.635Z",
         "scored": true,
         "ranking": "accuracy_score desc among analysts active in the last three calendar months (ties: sample size, then handle)",
         "tier_asset_class": "crypto",
         "count": 1,
         "total_ranked": 72,
         "total_tracked": 125,
         "analysts": [
          {
           "rank": 1,
           "tier": {
            "asset_class": "crypto",
            "tier": 1,
            "badge": "top1",
            "label": "Top 1%",
            "eligible": true,
            "reason": null,
            "pool_rank": 2,
            "pool_size": 108,
            "percentile": 1.8519,
            "score_version": "v20",
            "computed_at": "2026-10-06T00:59:17.501379+00:00"
           },
           "handle": "CarpeNoctom",
           "name": "CarpeNoctom",
           "profile_url": "https://consensus.cryptoquant.com/analyst/CarpeNoctom",
           "source_url": "https://x.com/CarpeNoctom",
           "sources": [
            "twitter"
           ],
           "role": "PM & Head of Trading",
           "company": "Canary Capital",
           "category": null,
           "citation_count": null,
           "accuracy_score": 69.9,
           "accuracy_sample_size": 2081,
           "call_count": 5374,
           "bull_score": 76,
           "bear_score": 50.3,
           "short_score": 51,
           "long_score": 63.3,
           "balanced_score": 50.3,
           "bullish_bias_pct": 54,
           "bias": "balanced",
           "last_post_at": "2026-10-02T21:04:12+00:00"
          }
         ],
         "score_notes": {
          "accuracy_score": "Overall accuracy, 0–100: an Empirical-Bayes-adjusted hit rate of the analyst's directional calls against subsequent price moves, shrunk toward the pool mean when the sample is small. Comparative — rank analysts on the same asset by it; it is NOT a win probability.",
          "accuracy_sample_size": "Number of evaluated calls behind accuracy_score. Treat scores with a sample below ~10 as weak evidence."
         },
         "list_url": "https://consensus.cryptoquant.com/analysts?asset=btc&sort=accuracy",
         "methodology_url": "https://consensus.cryptoquant.com/methodology",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#analyst-track-record"
        }
       }
      }
     },
     "400": {
      "description": "`UNKNOWN_ASSET`",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "error": "Unknown asset",
         "code": "UNKNOWN_ASSET",
         "message": "\"DOGE2\" is not a registered asset. Use a ticker, route key or name from https://consensus.cryptoquant.com/api/v1/assets (symbol / key / name / aliases, case-insensitive). Market aggregates are not assets.",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#errors",
         "param": "asset"
        }
       }
      }
     },
     "503": {
      "description": "`SERVICE_UNAVAILABLE` — the analyst list could not be loaded; retry shortly",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        },
        "example": {
         "error": "Analyst list unavailable",
         "code": "SERVICE_UNAVAILABLE",
         "message": "The analyst list could not be loaded. Try again shortly.",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#errors"
        }
       }
      }
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "source": "curl -X GET \"https://consensus.cryptoquant.com/api/v1/analysts/top?asset=BTC&limit=5\""
     },
     {
      "lang": "JavaScript",
      "source": "fetch(\"https://consensus.cryptoquant.com/api/v1/analysts/top?asset=BTC&limit=5\")\n  .then(response => response.json())\n  .then(data => console.log(data))"
     },
     {
      "lang": "NodeJS",
      "source": "require('axios')\n  .get(\"https://consensus.cryptoquant.com/api/v1/analysts/top?asset=BTC&limit=5\")\n  .then(response => console.log(response))"
     },
     {
      "lang": "Ruby",
      "source": "require 'net/http'\nuri = URI(\"https://consensus.cryptoquant.com/api/v1/analysts/top?asset=BTC&limit=5\")\nres = Net::HTTP.get_response(uri)\nputs res.body"
     },
     {
      "lang": "Python",
      "source": "import requests\nurl = \"https://consensus.cryptoquant.com/api/v1/analysts/top?asset=BTC&limit=5\"\nprint(requests.get(url).json())"
     }
    ]
   }
  },
  "/analysts/{handle}": {
   "get": {
    "operationId": "getAnalystTrackRecord",
    "tags": [
     "Analyst track record"
    ],
    "summary": "One analyst's track record (public, no API key)",
    "description": "The analyst page's numbers as JSON: stored accuracy scores, category ranks, overall rank, best top-10 placement, call counts split per source and recent bias, covered assets, profile and source URLs. `{handle}` is the X handle, the /analyst/`<slug>` page slug or a `platform:slug` id; a leading @ is ignored and URL-encoding is decoded. Hidden, ineligible, company, news-feed and sell-side rows are 404 `ANALYST_NOT_FOUND`. Public; CDN-cached for one hour. Backs the MCP tool get_analyst_profile.",
    "security": [],
    "x-plan": {
     "access": "public",
     "api_key": "not required",
     "plans": [
      "none",
      "free",
      "premium",
      "enterprise"
     ],
     "rate_limit": "none (CDN-cached)",
     "daily_limit": "none",
     "cache": "CDN 1 h"
    },
    "parameters": [
     {
      "name": "handle",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      },
      "example": "caprioleio",
      "description": "X handle / page slug / `platform:slug` id (case-insensitive)."
     }
    ],
    "responses": {
     "200": {
      "description": "Track record",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/AnalystTrackRecord"
        },
        "example": {
         "handle": "caprioleio",
         "name": "Charles Edwards",
         "slug": "caprioleio",
         "profile_url": "https://consensus.cryptoquant.com/analyst/caprioleio",
         "source_url": "https://x.com/caprioleio",
         "sources": [
          "twitter"
         ],
         "role": "Founder & CEO",
         "company": "Capriole Investments",
         "follower_count": 0,
         "is_verified": false,
         "category": "macro",
         "activity": {
          "last_activity_at": "2026-10-02T12:34:42+00:00",
          "active_last_3_months": true
         },
         "track_record": {
          "accuracy_score": 70.8,
          "accuracy_sample_size": 1624,
          "call_count": 2276,
          "bull_score": 89.8,
          "bear_score": 39.8,
          "short_score": 63.9,
          "long_score": 77,
          "balanced_score": 39.8,
          "overall_rank": {
           "rank": 6,
           "total": 109
          },
          "category_ranks": {
           "bull": {
            "rank": 1,
            "total": 111
           },
           "bear": {
            "rank": 51,
            "total": 111
           },
           "short": {
            "rank": 20,
            "total": 91
           },
           "long": {
            "rank": 29,
            "total": 111
           }
          },
          "best_ranking": {
           "category": "bull",
           "rank": 1,
           "label": "Bull Accuracy",
           "priority": 2
          },
          "scored": true,
          "tier": {
           "asset_class": "crypto",
           "tier": 1,
           "badge": "top1",
           "label": "Top 1%",
           "eligible": true,
           "reason": null,
           "pool_rank": 7,
           "pool_size": 106,
           "percentile": 6.6038,
           "score_version": "v20r-btc-amp31-dir",
           "computed_at": "2026-10-02T21:16:07.550877+00:00"
          },
          "tiers": {
           "crypto": {
            "asset_class": "crypto",
            "tier": 1,
            "badge": "top1",
            "label": "Top 1%",
            "eligible": true,
            "reason": null,
            "pool_rank": 7,
            "pool_size": 106,
            "percentile": 6.6038,
            "score_version": "v20r-btc-amp31-dir",
            "computed_at": "2026-10-02T21:16:07.550877+00:00"
           }
          }
         },
         "calls": {
          "total": 1778,
          "bullish": 1160,
          "bearish": 618,
          "neutral": 542,
          "by_source": {
           "x": 1778,
           "cryptoquant": 0,
           "news": 0,
           "tradingview": 0,
           "seekingalpha": 0,
           "substack": 0,
           "benzinga": 0,
           "other": 0
          },
          "definition": "A call is one directional (bullish or bearish) item about one tracked asset, over the whole stored history: X posts, TradingView ideas, Seeking Alpha articles (title + summary), Substack posts and sell-side analyst ratings. An article on several tickers is one call per ticker on each asset page and counts once per article × ticker pair in its author's total.",
          "recent_bullish_bias_pct": 70,
          "recent_bias": "bullish"
         },
         "covered_assets": [
          {
           "symbol": "BTC",
           "asset_class": "crypto",
           "last_post_at": "2026-09-23T04:02:49+00:00"
          },
          {
           "symbol": "SPY",
           "asset_class": "indices",
           "last_post_at": "2026-08-05T06:40:15+00:00"
          }
         ],
         "citation_count": null,
         "score_notes": {
          "accuracy_score": "Overall accuracy, 0–100: an Empirical-Bayes-adjusted hit rate of the analyst's directional calls against subsequent price moves, shrunk toward the pool mean when the sample is small. Comparative — rank analysts on the same asset by it; it is NOT a win probability.",
          "accuracy_sample_size": "Number of evaluated calls behind accuracy_score. Treat scores with a sample below ~10 as weak evidence."
         },
         "methodology_url": "https://consensus.cryptoquant.com/methodology",
         "docs_url": "https://consensus.cryptoquant.com/docs/api#analyst-track-record",
         "as_of": "2026-10-06T07:48:28.011Z"
        }
       }
      }
     },
     "404": {
      "description": "`ANALYST_NOT_FOUND` — no such tracked analyst (`message` points to /analysts/top for valid handles)",
      "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"
        }
       }
      }
     }
    },
    "x-codeSamples": [
     {
      "lang": "Shell",
      "source": "curl -X GET \"https://consensus.cryptoquant.com/api/v1/analysts/caprioleio\""
     },
     {
      "lang": "JavaScript",
      "source": "fetch(\"https://consensus.cryptoquant.com/api/v1/analysts/caprioleio\")\n  .then(response => response.json())\n  .then(data => console.log(data))"
     },
     {
      "lang": "NodeJS",
      "source": "require('axios')\n  .get(\"https://consensus.cryptoquant.com/api/v1/analysts/caprioleio\")\n  .then(response => console.log(response))"
     },
     {
      "lang": "Ruby",
      "source": "require 'net/http'\nuri = URI(\"https://consensus.cryptoquant.com/api/v1/analysts/caprioleio\")\nres = Net::HTTP.get_response(uri)\nputs res.body"
     },
     {
      "lang": "Python",
      "source": "import requests\nurl = \"https://consensus.cryptoquant.com/api/v1/analysts/caprioleio\"\nprint(requests.get(url).json())"
     }
    ]
   }
  },
  "/sentiment": {
   "get": {
    "operationId": "getAnalystStance",
    "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.",
    "x-plan": {
     "access": "premium_or_enterprise",
     "api_key": "required",
     "plans": [
      "premium",
      "enterprise"
     ],
     "error_below": "PLAN_REQUIRED",
     "rate_limit": "1000 / min",
     "daily_limit": "none",
     "cache": "none"
    },
    "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\" \\\n-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>\"} })\n  .then(response => response.json())\n  .then(data => console.log(data))"
     },
     {
      "lang": "NodeJS",
      "source": "require('axios')\n  .get(\"https://consensus.cryptoquant.com/api/v1/sentiment?handle=caprioleio&asset=BTC&days=30\", { headers: { 'X-API-Key': '<YOUR_API_KEY>' } })\n  .then(response => console.log(response))"
     },
     {
      "lang": "Ruby",
      "source": "require 'net/http'\nuri = URI(\"https://consensus.cryptoquant.com/api/v1/sentiment?handle=caprioleio&asset=BTC&days=30\")\nreq = Net::HTTP::Get.new(uri)\nreq[\"X-API-Key\"] = \"<YOUR_API_KEY>\"\nres = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }\nputs res.body"
     },
     {
      "lang": "Python",
      "source": "import requests\nheaders = {'X-API-Key': '<YOUR_API_KEY>'}\nurl = \"https://consensus.cryptoquant.com/api/v1/sentiment?handle=caprioleio&asset=BTC&days=30\"\nprint(requests.get(url, headers=headers).json())"
     }
    ]
   }
  }
 },
 "components": {
  "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)."
   }
  },
  "schemas": {
   "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."
     }
    }
   },
   "AssetRow": {
    "type": "object",
    "required": [
     "symbol",
     "name",
     "key",
     "asset_class",
     "category",
     "market",
     "currency",
     "source",
     "coverage",
     "canonical",
     "aliases",
     "retired_symbols",
     "page_url",
     "identity"
    ],
    "properties": {
     "symbol": {
      "type": "string",
      "description": "The symbol the API reports and accepts (`BTC`, `NVDA`, `005930`, `XAU`)."
     },
     "name": {
      "type": "string",
      "description": "Display name (also accepted as `asset`)."
     },
     "key": {
      "type": "string",
      "description": "Route key of the site page (`/consensus/<key>`; also accepted as `asset`)."
     },
     "asset_class": {
      "type": "string",
      "enum": [
       "crypto",
       "equities",
       "indices",
       "commodities"
      ]
     },
     "category": {
      "type": "string",
      "enum": [
       "crypto",
       "us-equities",
       "indices",
       "commodities",
       "kr-equities"
      ],
      "description": "Selector category: asset_class with equities split into US / KR by venue."
     },
     "market": {
      "type": "string",
      "description": "Listing venue / price-source label (`Bitfinex`, `NasdaqGS`, `KRX`, `Commodity`, `US`)."
     },
     "currency": {
      "type": "string",
      "description": "ISO 4217 currency of the price series (`USD`, `KRW`)."
     },
     "source": {
      "type": "string",
      "enum": [
       "alpha",
       "sellside",
       "unbias",
       "legacy"
      ],
      "description": "Registry provenance: `alpha` = CryptoQuant Alpha library, `sellside` = licensed ratings coverage only (no price series), `unbias` = crypto registered here, `legacy` = long-tail crypto tag."
     },
     "coverage": {
      "type": "string",
      "enum": [
       "live",
       "mapped",
       "pending"
      ],
      "description": "`live` = the crypto pipeline covers it; `mapped` = analysts / ratings mapped, data collecting; `pending` = page exists, no analyst data yet."
     },
     "canonical": {
      "type": [
       "string",
       "null"
      ],
      "description": "Set on an ALIAS ticker: the canonical symbol whose data it serves (`GLD` → `XAU`). null for a canonical asset."
     },
     "aliases": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Alias tickers of a canonical asset (`XAU` → [`GLD`])."
     },
     "retired_symbols": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "Former tickers that still resolve to this asset (`POL` → [`MATIC`, `POLYGON`])."
     },
     "page_url": {
      "type": "string",
      "format": "uri"
     },
     "identity": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/AssetIdentity"
       },
       {
        "type": "null"
       }
      ],
      "description": "The asset's external identity — `asset_class` on the identity enum + `uid` (CoinGecko id / ISIN / internal code). Two assets may share a `symbol` (STX = Stacks and Seagate); the identity tells them apart. null for an unregistered long-tail ticker."
     }
    }
   },
   "AssetIdentity": {
    "type": "object",
    "required": [
     "asset_class",
     "uid",
     "chain_contract",
     "exchange_mic",
     "needs_review"
    ],
    "description": "One asset = one (asset_class, uid). The identity enum is finer than the registry `asset_class` (an ETF is `etf`, not `indices`; FX is `fx`). `uid` is null while the identity is still under review — never a guess.",
    "properties": {
     "asset_class": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "crypto",
       "equity",
       "etf",
       "index",
       "commodity",
       "fx",
       null
      ],
      "description": "Identity class; null for a series the enum has no value for yet (rate series)."
     },
     "uid": {
      "type": [
       "string",
       "null"
      ],
      "description": "crypto = CoinGecko id (`bitcoin`, `cosmos`); equity / etf = ISIN (`US67066G1040`); index / commodity / fx = internal code (`SPX`, `XAU`, `EURUSD`)."
     },
     "chain_contract": {
      "type": [
       "string",
       "null"
      ],
      "description": "Crypto tokens only: `<coingecko platform>:<contract address>`; null for native coins and every other class."
     },
     "exchange_mic": {
      "type": [
       "string",
       "null"
      ],
      "description": "equity / etf only: ISO 10383 MIC of the listing venue (`XNAS`, `XNYS`, `ARCX`, `XKRX`)."
     },
     "needs_review": {
      "type": "boolean",
      "description": "true while the identity lookup is unconfirmed (uid null)."
     }
    }
   },
   "AssetsResponse": {
    "type": "object",
    "required": [
     "count",
     "rows",
     "assets",
     "enums",
     "as_of"
    ],
    "properties": {
     "count": {
      "type": "integer",
      "minimum": 0,
      "description": "Canonical assets in the response (alias tickers not counted)."
     },
     "rows": {
      "type": "integer",
      "minimum": 0,
      "description": "Rows in `assets` (canonical + alias tickers)."
     },
     "filter": {
      "type": "object",
      "properties": {
       "asset_class": {
        "type": [
         "string",
         "null"
        ]
       },
       "category": {
        "type": [
         "string",
         "null"
        ]
       },
       "q": {
        "type": [
         "string",
         "null"
        ]
       }
      }
     },
     "asset_classes": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      },
      "description": "asset_class → label."
     },
     "categories": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      },
      "description": "category → label."
     },
     "how_to_resolve": {
      "type": "string"
     },
     "assets": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/AssetRow"
      }
     },
     "enums": {
      "type": "object",
      "description": "Every closed value set the API uses, listed in full.",
      "required": [
       "asset_class",
       "asset_category",
       "source_platform",
       "source_type",
       "sentiment_label",
       "stance",
       "plan"
      ],
      "properties": {
       "asset_class": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "crypto",
          "equities",
          "indices",
          "commodities"
         ]
        }
       },
       "asset_category": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "crypto",
          "us-equities",
          "indices",
          "commodities",
          "kr-equities"
         ]
        }
       },
       "asset_source": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "alpha",
          "sellside",
          "unbias",
          "legacy"
         ]
        }
       },
       "source_platform": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "x",
          "cryptoquant",
          "seekingalpha",
          "tradingview",
          "substack",
          "benzinga"
         ]
        },
        "description": "Where tracked analysts publish (`x` = X / Twitter; `benzinga` = licensed sell-side ratings)."
       },
       "analyst_source": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "twitter",
          "cryptoquant",
          "seekingalpha",
          "tradingview",
          "substack",
          "other"
         ]
        }
       },
       "source_type": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "tweet",
          "quicktake",
          "research",
          "news",
          "seekingalpha",
          "tradingview",
          "substack",
          "youtube",
          "telegram",
          "bluesky"
         ]
        },
        "description": "Stored `source_type` of a post."
       },
       "sentiment_label": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "bullish",
          "bullish_nuance",
          "neutral",
          "bearish_nuance",
          "bearish"
         ]
        },
        "description": "Per-post classifier labels, bullish → bearish."
       },
       "directional_label": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "bullish",
          "bullish_nuance",
          "bearish_nuance",
          "bearish"
         ]
        },
        "description": "The labels that count as a call / vote (everything but neutral)."
       },
       "stance": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "bullish",
          "bearish",
          "neutral"
         ]
        }
       },
       "bias": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "bullish",
          "bearish",
          "balanced"
         ]
        }
       },
       "tier_badge": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "top1",
          "top5",
          "top10",
          "tracked"
         ]
        }
       },
       "tier_reason": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "excluded",
          "ineligible",
          "news_feed",
          "company",
          "unscored",
          "insufficient_calls",
          "pool_too_small"
         ]
        }
       },
       "breakdown_window": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "1d",
          "3d",
          "7d",
          "30d"
         ]
        }
       },
       "views_window": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "7d",
          "30d"
         ]
        }
       },
       "breakdown_lang": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "en",
          "ko",
          "zh",
          "ja",
          "es",
          "pt",
          "ru",
          "hi",
          "de",
          "fr",
          "tr",
          "vi",
          "id",
          "it",
          "th",
          "pl",
          "nl",
          "uk"
         ]
        }
       },
       "via": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "chatgpt",
          "claude",
          "mcp",
          "gemini",
          "perplexity",
          "api"
         ]
        }
       },
       "plan": {
        "type": "array",
        "items": {
         "type": "string",
         "enum": [
          "free",
          "pro",
          "premium",
          "enterprise"
         ]
        }
       }
      }
     },
     "docs_url": {
      "type": "string",
      "format": "uri"
     },
     "as_of": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "DailyIndexPoint": {
    "type": "object",
    "required": [
     "date",
     "consensus_index",
     "consensus_index_30d_ma",
     "z_score",
     "bullish_analysts",
     "bearish_analysts",
     "total_analysts"
    ],
    "properties": {
     "date": {
      "type": "string",
      "format": "date",
      "description": "UTC calendar day (end-of-day value; the current UTC day is never included)."
     },
     "consensus_index": {
      "type": [
       "number",
       "null"
      ],
      "minimum": -100,
      "maximum": 100,
      "description": "Daily raw Analyst Consensus Index: weighted bullish − bearish votes over total, −100 (all bearish) … +100 (all bullish). null = no vote that day."
     },
     "consensus_index_30d_ma": {
      "type": [
       "number",
       "null"
      ],
      "minimum": -100,
      "maximum": 100,
      "description": "30-day moving average of consensus_index (null until enough history)."
     },
     "z_score": {
      "type": [
       "number",
       "null"
      ],
      "description": "90-day rolling z-score of the 30-day MA, computed on its 0–100 rescale, 2 decimals. The site colours z ≥ +0.8 bullish and z ≤ −1.5 bearish."
     },
     "avg_sentiment_score": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "deprecated": true,
      "description": "Legacy unweighted mean sentiment (0–100). null once the stance pipeline is on; use consensus_index."
     },
     "bullish_analysts": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "Analysts whose latest stance that day was bullish."
     },
     "bearish_analysts": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0
     },
     "total_analysts": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "bullish_analysts + bearish_analysts (neutral analysts do not vote)."
     },
     "bullish_opinions": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "Directional posts published that day, bullish."
     },
     "bearish_opinions": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0
     },
     "total_opinions": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0
     }
    }
   },
   "ConsensusPublic": {
    "type": "object",
    "description": "No key: the latest end-of-day index point for the asset (what the asset page's band shows) with the bull / bear case and the links to quote.",
    "required": [
     "asset",
     "asset_name",
     "asset_class",
     "date",
     "consensus_index",
     "plan",
     "page_url",
     "cite_as",
     "data_by",
     "as_of"
    ],
    "properties": {
     "asset": {
      "type": "string",
      "description": "Canonical symbol."
     },
     "asset_name": {
      "type": "string"
     },
     "asset_class": {
      "type": "string",
      "enum": [
       "crypto",
       "equities",
       "indices",
       "commodities"
      ]
     },
     "date": {
      "type": [
       "string",
       "null"
      ],
      "format": "date",
      "description": "UTC day of the reading (null = no index yet for this asset)."
     },
     "consensus_index": {
      "type": [
       "number",
       "null"
      ],
      "minimum": -100,
      "maximum": 100,
      "description": "Analyst Consensus Index: −100 all bearish … +100 all bullish (one analyst = one vote, decay × accuracy weighted; see /methodology)."
     },
     "consensus_index_30d_ma": {
      "type": [
       "number",
       "null"
      ],
      "minimum": -100,
      "maximum": 100
     },
     "bullish_percent": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "round((consensus_index + 100) / 2) — the bullish share the site shows."
     },
     "bearish_percent": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "100 − bullish_percent."
     },
     "total_analysts": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "Analysts voting in that day's index."
     },
     "bull_case": {
      "type": [
       "string",
       "null"
      ],
      "description": "One-line bull case for the window (null when no bullish consensus formed)."
     },
     "bear_case": {
      "type": [
       "string",
       "null"
      ]
     },
     "window": {
      "type": "string",
      "enum": [
       "30d"
      ],
      "description": "Window of the bull / bear case."
     },
     "plan": {
      "type": "string",
      "enum": [
       "public"
      ]
     },
     "history": {
      "type": "string",
      "description": "How to get the daily history (API key)."
     },
     "page_url": {
      "type": "string",
      "format": "uri",
      "description": "The canonical page on the site for this answer, UTM-tagged for the calling surface (`via`). Link it in every answer."
     },
     "top_analysts_url": {
      "type": "string",
      "format": "uri",
      "description": "The asset's analysts ranked by accuracy (site page)."
     },
     "methodology_url": {
      "type": "string",
      "format": "uri",
      "description": "How the numbers are made."
     },
     "cite_as": {
      "type": "string",
      "description": "Attribution line + the untagged canonical URL, e.g. \"Data by CryptoQuant Consensus — https://consensus.cryptoquant.com/consensus/btc\"."
     },
     "data_by": {
      "type": "string",
      "enum": [
       "Data by CryptoQuant Consensus"
      ],
      "description": "Show this next to the numbers."
     },
     "as_of": {
      "type": "string",
      "format": "date-time",
      "description": "Generation time of the payload (UTC)."
     }
    }
   },
   "AgentNarrative": {
    "type": "object",
    "required": [
     "stance",
     "analyst_count",
     "post_count"
    ],
    "properties": {
     "theme": {
      "type": [
       "string",
       "null"
      ],
      "description": "Short thesis label (e.g. \"ETF FLOWS\")."
     },
     "headline": {
      "type": [
       "string",
       "null"
      ]
     },
     "stance": {
      "type": "string",
      "enum": [
       "bullish",
       "bearish",
       "neutral"
      ]
     },
     "analyst_count": {
      "type": "integer",
      "minimum": 0
     },
     "post_count": {
      "type": "integer",
      "minimum": 0
     },
     "summary": {
      "type": [
       "string",
       "null"
      ]
     }
    }
   },
   "AgentAnalystView": {
    "type": "object",
    "required": [
     "handle",
     "stance",
     "thesis",
     "post_count",
     "consistency_pct",
     "profile_url"
    ],
    "properties": {
     "handle": {
      "type": "string"
     },
     "name": {
      "type": [
       "string",
       "null"
      ]
     },
     "stance": {
      "type": "string",
      "enum": [
       "bullish",
       "bearish"
      ]
     },
     "thesis": {
      "type": "string",
      "description": "The analyst's representative thesis in the window."
     },
     "excerpt": {
      "type": [
       "string",
       "null"
      ],
      "maxLength": 200,
      "description": "The first ≤ 200 characters of the representative post (sentence / word boundary, ellipsis when cut, original language). Quote with source_url; the full statement is provided under an Integration agreement only — per API key, contact ' + SALES_EMAIL + '."
     },
     "source_url": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri",
      "description": "The underlying post."
     },
     "source_type": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "tweet",
       "quicktake",
       "research",
       "news",
       "seekingalpha",
       "tradingview",
       "substack",
       "youtube",
       "telegram",
       "bluesky",
       null
      ],
      "description": "Stored source type of the post."
     },
     "published_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     },
     "post_count": {
      "type": "integer",
      "minimum": 0,
      "description": "Directional posts by the analyst on the asset in the window."
     },
     "consistency_pct": {
      "type": "integer",
      "minimum": 0,
      "maximum": 100,
      "description": "Share of those posts on the dominant side (≥ 60)."
     },
     "profile_url": {
      "type": "string",
      "format": "uri",
      "description": "The analyst page (cite this)."
     }
    }
   },
   "AnalystViewsResponse": {
    "type": "object",
    "required": [
     "asset",
     "asset_name",
     "window",
     "narratives",
     "analyst_views",
     "analyst_views_total",
     "page_url",
     "cite_as",
     "data_by",
     "as_of"
    ],
    "properties": {
     "asset": {
      "type": "string"
     },
     "asset_name": {
      "type": "string"
     },
     "window": {
      "type": "string",
      "enum": [
       "7d",
       "30d"
      ]
     },
     "bullish_posts": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "Directional posts in the window, bullish (post share — the index is on /consensus)."
     },
     "bearish_posts": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0
     },
     "bullish_pct": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "maximum": 100
     },
     "bearish_pct": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "maximum": 100
     },
     "bull_case": {
      "type": [
       "string",
       "null"
      ]
     },
     "bear_case": {
      "type": [
       "string",
       "null"
      ]
     },
     "narratives": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/AgentNarrative"
      }
     },
     "analyst_views": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/AgentAnalystView"
      },
      "description": "Most active first, at most 12."
     },
     "analyst_views_total": {
      "type": "integer",
      "minimum": 0,
      "description": "How many analysts qualified (the page lists them all)."
     },
     "note": {
      "type": "string",
      "description": "What the fields mean — quote the caveat with the numbers."
     },
     "page_url": {
      "type": "string",
      "format": "uri",
      "description": "The canonical page on the site for this answer, UTM-tagged for the calling surface (`via`). Link it in every answer."
     },
     "top_analysts_url": {
      "type": "string",
      "format": "uri",
      "description": "The asset's analysts ranked by accuracy (site page)."
     },
     "methodology_url": {
      "type": "string",
      "format": "uri",
      "description": "How the numbers are made."
     },
     "cite_as": {
      "type": "string",
      "description": "Attribution line + the untagged canonical URL, e.g. \"Data by CryptoQuant Consensus — https://consensus.cryptoquant.com/consensus/btc\"."
     },
     "data_by": {
      "type": "string",
      "enum": [
       "Data by CryptoQuant Consensus"
      ],
      "description": "Show this next to the numbers."
     },
     "as_of": {
      "type": "string",
      "format": "date-time",
      "description": "Generation time of the payload (UTC)."
     }
    }
   },
   "AgentCall": {
    "type": "object",
    "required": [
     "handle",
     "stance",
     "excerpt",
     "published_at",
     "profile_url"
    ],
    "properties": {
     "handle": {
      "type": "string"
     },
     "name": {
      "type": [
       "string",
       "null"
      ]
     },
     "stance": {
      "type": "string",
      "enum": [
       "bullish",
       "bearish"
      ]
     },
     "excerpt": {
      "type": "string",
      "maxLength": 200,
      "description": "The first ≤ 200 characters of the call's post (sentence / word boundary, ellipsis when cut, original language); never the full text on this key-free endpoint."
     },
     "source_url": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri"
     },
     "source_type": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "tweet",
       "quicktake",
       "research",
       "news",
       "seekingalpha",
       "tradingview",
       "substack",
       "youtube",
       "telegram",
       "bluesky",
       null
      ]
     },
     "published_at": {
      "type": "string",
      "format": "date-time"
     },
     "profile_url": {
      "type": "string",
      "format": "uri"
     }
    }
   },
   "RecentCallsResponse": {
    "type": "object",
    "required": [
     "asset",
     "asset_name",
     "count",
     "calls",
     "definition",
     "page_url",
     "cite_as",
     "data_by",
     "as_of"
    ],
    "properties": {
     "asset": {
      "type": "string"
     },
     "asset_name": {
      "type": "string"
     },
     "count": {
      "type": "integer",
      "minimum": 0
     },
     "calls": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/AgentCall"
      },
      "description": "Newest first."
     },
     "definition": {
      "type": "string",
      "description": "What a call is — quote with the list."
     },
     "page_url": {
      "type": "string",
      "format": "uri",
      "description": "The canonical page on the site for this answer, UTM-tagged for the calling surface (`via`). Link it in every answer."
     },
     "top_analysts_url": {
      "type": "string",
      "format": "uri",
      "description": "The asset's analysts ranked by accuracy (site page)."
     },
     "methodology_url": {
      "type": "string",
      "format": "uri",
      "description": "How the numbers are made."
     },
     "cite_as": {
      "type": "string",
      "description": "Attribution line + the untagged canonical URL, e.g. \"Data by CryptoQuant Consensus — https://consensus.cryptoquant.com/consensus/btc\"."
     },
     "data_by": {
      "type": "string",
      "enum": [
       "Data by CryptoQuant Consensus"
      ],
      "description": "Show this next to the numbers."
     },
     "as_of": {
      "type": "string",
      "format": "date-time",
      "description": "Generation time of the payload (UTC)."
     }
    }
   },
   "ConsensusSeries": {
    "type": "object",
    "description": "Premium / Enterprise plans: the requested window, oldest first.",
    "required": [
     "asset",
     "period",
     "granularity",
     "count",
     "data"
    ],
    "properties": {
     "asset": {
      "type": "string",
      "description": "Canonical symbol."
     },
     "period": {
      "type": "object",
      "required": [
       "start",
       "end"
      ],
      "properties": {
       "start": {
        "type": "string",
        "format": "date",
        "description": "First UTC day requested (today − days)."
       },
       "end": {
        "type": "string",
        "format": "date",
        "description": "Yesterday UTC."
       }
      }
     },
     "granularity": {
      "type": "string",
      "enum": [
       "daily"
      ]
     },
     "count": {
      "type": "integer",
      "minimum": 0,
      "description": "Rows in `data` (0 for an asset without index rows in the range)."
     },
     "data": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/DailyIndexPoint"
      }
     }
    }
   },
   "ConsensusLatest": {
    "type": "object",
    "description": "Free plan: the latest end-of-day point as a flat object (no `data` array).",
    "required": [
     "asset",
     "date",
     "plan"
    ],
    "allOf": [
     {
      "$ref": "#/components/schemas/DailyIndexPoint"
     },
     {
      "type": "object",
      "properties": {
       "asset": {
        "type": "string"
       },
       "plan": {
        "type": "string",
        "enum": [
         "free"
        ]
       },
       "upgrade_message": {
        "type": "string"
       }
      }
     }
    ]
   },
   "BreakdownAnalyst": {
    "type": "object",
    "required": [
     "handle",
     "display_name",
     "avatar_url",
     "x_url",
     "profile_url"
    ],
    "properties": {
     "handle": {
      "type": "string"
     },
     "display_name": {
      "type": [
       "string",
       "null"
      ]
     },
     "avatar_url": {
      "type": [
       "string",
       "null"
      ],
      "description": "May be a generated placeholder."
     },
     "x_url": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri",
      "description": "Only when the X handle is known — never synthesised."
     },
     "profile_url": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri",
      "description": "CryptoQuant profile, when the author has one."
     }
    }
   },
   "BreakdownSource": {
    "type": "object",
    "description": "One source post. Exactly one of `statement` (only on a key with Integration raw access — per-key clearance, not a plan: the original title or text) or `excerpt` (every other key: the first ≤ 200 characters) is present; `BreakdownResponse.source_text` says which.",
    "required": [
     "analyst_handle",
     "url",
     "source_type",
     "published_at"
    ],
    "oneOf": [
     {
      "required": [
       "statement"
      ]
     },
     {
      "required": [
       "excerpt"
      ]
     }
    ],
    "properties": {
     "analyst_handle": {
      "type": [
       "string",
       "null"
      ]
     },
     "statement": {
      "type": "string",
      "description": "Original post title or text, original language — provided under an Integration agreement only, per API key (api_keys.raw_access); absent on every other key whatever its plan (Premium, Enterprise, admin). Integration use that needs the original text: contact sales@cryptoquant.com."
     },
     "excerpt": {
      "type": "string",
      "maxLength": 200,
      "description": "The first ≤ 200 characters of the original post (sentence / word boundary, ellipsis when cut, original language) — every key without Integration raw access; absent on a cleared key. Read the full post at `url`."
     },
     "url": {
      "type": "string",
      "format": "uri",
      "description": "The underlying post."
     },
     "source_type": {
      "type": "string",
      "enum": [
       "tweet",
       "quicktake",
       "research",
       "news",
       "seekingalpha",
       "tradingview",
       "substack",
       "youtube",
       "telegram",
       "bluesky"
      ],
      "description": "Stored source type (`tweet` = X, `quicktake` = CryptoQuant)."
     },
     "published_at": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "BreakdownViewpoint": {
    "type": "object",
    "required": [
     "id",
     "stance",
     "analyst_count",
     "title",
     "thesis",
     "analysts",
     "sources"
    ],
    "properties": {
     "id": {
      "type": "string",
      "description": "Cluster id (opaque)."
     },
     "stance": {
      "type": "string",
      "enum": [
       "bullish",
       "bearish"
      ]
     },
     "analyst_count": {
      "type": "integer",
      "minimum": 0
     },
     "title": {
      "type": [
       "string",
       "null"
      ],
      "description": "Localised when `lang` ≠ en and a translation exists."
     },
     "thesis": {
      "type": [
       "string",
       "null"
      ],
      "description": "Short thesis label the viewpoint is grouped by."
     },
     "analysts": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/BreakdownAnalyst"
      }
     },
     "sources": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/BreakdownSource"
      }
     }
    }
   },
   "BreakdownResponse": {
    "type": "object",
    "required": [
     "asset",
     "updated_at",
     "window",
     "lang",
     "source_text",
     "note",
     "summary",
     "overall",
     "analyst_consensus"
    ],
    "properties": {
     "asset": {
      "type": "object",
      "required": [
       "symbol",
       "name"
      ],
      "properties": {
       "symbol": {
        "type": "string"
       },
       "name": {
        "type": "string"
       }
      }
     },
     "updated_at": {
      "type": "string",
      "format": "date-time",
      "description": "Most recent source post in the response (now when none)."
     },
     "window": {
      "type": "string",
      "enum": [
       "1d",
       "3d",
       "7d",
       "30d"
      ]
     },
     "lang": {
      "type": "string",
      "enum": [
       "en",
       "ko",
       "zh",
       "ja",
       "es",
       "pt",
       "ru",
       "hi",
       "de",
       "fr",
       "tr",
       "vi",
       "id",
       "it",
       "th",
       "pl",
       "nl",
       "uk"
      ]
     },
     "source_text": {
      "type": "string",
      "enum": [
       "statement",
       "excerpt"
      ],
      "description": "Which source-text field this key received: `statement` (original statement (full post title / text) — Integration agreement keys only (per-key raw_access clearance, not a plan); contact sales@cryptoquant.com) or `excerpt` (excerpt only: the first ≤ 200 characters of the original post (sentence / word boundary, original language) + the source url)."
     },
     "note": {
      "type": "string",
      "description": "One sentence saying what sources[] carries on this plan — show it with the sources."
     },
     "summary": {
      "type": "object",
      "required": [
       "total_opinions",
       "bullish_opinions",
       "bearish_opinions",
       "bullish_pct",
       "bearish_pct",
       "consensus_index",
       "z_score"
      ],
      "properties": {
       "total_opinions": {
        "type": "integer",
        "minimum": 0,
        "description": "bullish_opinions + bearish_opinions (neutral excluded)."
       },
       "bullish_opinions": {
        "type": "integer",
        "minimum": 0
       },
       "bearish_opinions": {
        "type": "integer",
        "minimum": 0
       },
       "bullish_pct": {
        "type": [
         "integer",
         "null"
        ],
        "minimum": 0,
        "maximum": 100,
        "description": "null when total_opinions is 0."
       },
       "bearish_pct": {
        "type": [
         "integer",
         "null"
        ],
        "minimum": 0,
        "maximum": 100
       },
       "consensus_index": {
        "type": [
         "number",
         "null"
        ],
        "minimum": -100,
        "maximum": 100,
        "description": "Latest daily index; does not vary by window; null for an asset without an index series."
       },
       "z_score": {
        "type": [
         "number",
         "null"
        ]
       }
      }
     },
     "overall": {
      "type": "object",
      "required": [
       "bull_case",
       "bear_case"
      ],
      "properties": {
       "bull_case": {
        "type": "object",
        "required": [
         "title"
        ],
        "properties": {
         "title": {
          "type": [
           "string",
           "null"
          ],
          "description": "Null when no bullish consensus has formed."
         }
        }
       },
       "bear_case": {
        "type": "object",
        "required": [
         "title"
        ],
        "properties": {
         "title": {
          "type": [
           "string",
           "null"
          ]
         }
        }
       }
      }
     },
     "analyst_consensus": {
      "type": "object",
      "required": [
       "bullish",
       "bearish"
      ],
      "properties": {
       "bullish": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/BreakdownViewpoint"
        }
       },
       "bearish": {
        "type": "array",
        "items": {
         "$ref": "#/components/schemas/BreakdownViewpoint"
        }
       }
      }
     }
    }
   },
   "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."
     }
    }
   },
   "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"
     }
    }
   },
   "AccuracyTier": {
    "type": "object",
    "description": "Accuracy percentile-tier badge for one asset class (Ki 2026-09-30). What people see is the badge, stated against all analysts: tier 1 = \"Top 1%\" (top 10% of the eligible tracked pool), 5 = \"Top 5%\" (top 50%), 10 = \"Top 10%\" (the rest of the eligible pool); null = \"Tracked\" (no badge, `reason` says why). Computed daily from the stored accuracy score; the precise position (pool_rank / pool_size / percentile) is for detail views and agents only.",
    "required": [
     "asset_class",
     "tier",
     "badge",
     "label",
     "eligible",
     "reason"
    ],
    "properties": {
     "asset_class": {
      "type": "string",
      "enum": [
       "crypto",
       "equities",
       "indices",
       "commodities",
       "macro"
      ]
     },
     "tier": {
      "type": [
       "integer",
       "null"
      ],
      "enum": [
       1,
       5,
       10,
       null
      ],
      "description": "Badge number: 1 = Top 1%, 5 = Top 5%, 10 = Top 10%; null = no badge (Tracked)."
     },
     "badge": {
      "type": "string",
      "enum": [
       "top1",
       "top5",
       "top10",
       "tracked"
      ],
      "description": "Stable key for the UI (i18n / icon)."
     },
     "label": {
      "type": "string",
      "enum": [
       "Top 1%",
       "Top 5%",
       "Top 10%",
       "Tracked"
      ],
      "description": "English badge text."
     },
     "eligible": {
      "type": "boolean",
      "description": "True when the analyst is inside the class pool (scored, not excluded, ≥ 30 directional calls). A thin pool (< 10) keeps eligible=true with tier null and reason pool_too_small."
     },
     "reason": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "excluded",
       "ineligible",
       "news_feed",
       "company",
       "unscored",
       "insufficient_calls",
       "pool_too_small",
       null
      ],
      "description": "Why tier is null; null when a tier is set. unscored = no accuracy score for this asset class."
     },
     "pool_rank": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 1,
      "description": "Midrank position from the top inside the eligible pool (1 = best; tied analysts share one position, e.g. 2.5)."
     },
     "pool_size": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "Eligible analysts in the class when computed."
     },
     "percentile": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "100 × pool_rank / pool_size: position from the top in percent (0 = best)."
     },
     "score_version": {
      "type": [
       "string",
       "null"
      ],
      "description": "Score source the tier was computed from (`v20`, or an active alternative such as `v20r-btc-amp31-dir`)."
     },
     "computed_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     }
    }
   },
   "TopAnalystEntry": {
    "type": "object",
    "required": [
     "rank",
     "tier",
     "handle",
     "profile_url",
     "sources",
     "accuracy_score",
     "call_count",
     "bias"
    ],
    "properties": {
     "rank": {
      "type": "integer",
      "minimum": 1,
      "description": "Position in the asset's accuracy ranking among analysts active in the last three calendar months."
     },
     "tier": {
      "allOf": [
       {
        "$ref": "#/components/schemas/AccuracyTier"
       }
      ],
      "description": "The badge for the requested asset's class (tier_asset_class) — show this, not accuracy_score."
     },
     "handle": {
      "type": "string",
      "description": "X handle, or `<platform>:<slug>` for a platform-only analyst."
     },
     "name": {
      "type": [
       "string",
       "null"
      ]
     },
     "profile_url": {
      "type": "string",
      "format": "uri",
      "description": "The analyst page on the site (cite this)."
     },
     "source_url": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri",
      "description": "Where the analyst publishes (X, CryptoQuant, TradingView, Seeking Alpha, Substack)."
     },
     "sources": {
      "type": "array",
      "items": {
       "type": "string",
       "enum": [
        "twitter",
        "cryptoquant",
        "seekingalpha",
        "tradingview",
        "substack",
        "other"
       ]
      },
      "description": "Stored provenance of the analyst row (`twitter` = X)."
     },
     "role": {
      "type": [
       "string",
       "null"
      ]
     },
     "company": {
      "type": [
       "string",
       "null"
      ]
     },
     "category": {
      "type": [
       "string",
       "null"
      ],
      "description": "Analyst category label (e.g. `technical`, `macro`); free text, null when unset."
     },
     "citation_count": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "Times cited by name in tracked news articles."
     },
     "accuracy_score": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "Overall accuracy, 0–100: Empirical-Bayes-adjusted hit rate of directional calls vs. subsequent price, shrunk toward the pool mean for small samples. Comparative within an asset; NOT a win probability."
     },
     "accuracy_sample_size": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "Evaluated calls behind accuracy_score; below ~10 is weak evidence."
     },
     "call_count": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0,
      "description": "All-time directional (bullish or bearish) posts on tracked assets."
     },
     "bull_score": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "Accuracy, 0–100, on calls made in bull market phases."
     },
     "bear_score": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "Accuracy, 0–100, on calls made in bear market phases."
     },
     "short_score": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "Accuracy, 0–100, of calls evaluated over the short horizon."
     },
     "long_score": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "Accuracy, 0–100, of calls evaluated over the long horizon."
     },
     "balanced_score": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "Mean of bull_score and bear_score when both exist."
     },
     "bullish_bias_pct": {
      "type": [
       "number",
       "null"
      ],
      "minimum": 0,
      "maximum": 100,
      "description": "Share of bullish among recent directional calls on the asset (50 = balanced). A stance, not a quality signal."
     },
     "bias": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "bullish",
       "bearish",
       "balanced",
       null
      ]
     },
     "last_post_at": {
      "type": [
       "string",
       "null"
      ],
      "format": "date-time"
     }
    }
   },
   "TopAnalystsResponse": {
    "type": "object",
    "required": [
     "asset",
     "asset_name",
     "asset_class",
     "as_of",
     "scored",
     "ranking",
     "tier_asset_class",
     "count",
     "total_ranked",
     "total_tracked",
     "analysts",
     "score_notes"
    ],
    "properties": {
     "asset": {
      "type": "string"
     },
     "asset_name": {
      "type": "string"
     },
     "asset_class": {
      "type": "string",
      "enum": [
       "crypto",
       "equities",
       "indices",
       "commodities"
      ]
     },
     "as_of": {
      "type": "string",
      "format": "date-time"
     },
     "scored": {
      "type": "boolean",
      "description": "false = accuracy scoring does not cover this asset yet; scores are null and the order is the list order."
     },
     "ranking": {
      "type": "string",
      "description": "Plain-language statement of the sort rule."
     },
     "tier_asset_class": {
      "type": "string",
      "enum": [
       "crypto",
       "equities",
       "indices",
       "commodities",
       "macro"
      ],
      "description": "The asset class each entry's `tier` was read for."
     },
     "count": {
      "type": "integer",
      "minimum": 0
     },
     "total_ranked": {
      "type": "integer",
      "minimum": 0
     },
     "total_tracked": {
      "type": "integer",
      "minimum": 0
     },
     "analysts": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/TopAnalystEntry"
      }
     },
     "score_notes": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      },
      "description": "What each score field means — quote the caveat with the number."
     },
     "list_url": {
      "type": "string",
      "format": "uri"
     },
     "methodology_url": {
      "type": "string",
      "format": "uri"
     },
     "docs_url": {
      "type": "string",
      "format": "uri"
     }
    }
   },
   "AnalystTrackRecord": {
    "type": "object",
    "required": [
     "handle",
     "slug",
     "profile_url",
     "sources",
     "activity",
     "track_record",
     "calls",
     "covered_assets",
     "as_of"
    ],
    "properties": {
     "handle": {
      "type": "string",
      "description": "X handle, or `<platform>:<slug>` for a platform-only analyst."
     },
     "name": {
      "type": [
       "string",
       "null"
      ]
     },
     "profile_url": {
      "type": "string",
      "format": "uri",
      "description": "The analyst page on the site (cite this)."
     },
     "source_url": {
      "type": [
       "string",
       "null"
      ],
      "format": "uri",
      "description": "Where the analyst publishes (X, CryptoQuant, TradingView, Seeking Alpha, Substack)."
     },
     "sources": {
      "type": "array",
      "items": {
       "type": "string",
       "enum": [
        "twitter",
        "cryptoquant",
        "seekingalpha",
        "tradingview",
        "substack",
        "other"
       ]
      },
      "description": "Stored provenance of the analyst row (`twitter` = X)."
     },
     "role": {
      "type": [
       "string",
       "null"
      ]
     },
     "company": {
      "type": [
       "string",
       "null"
      ]
     },
     "category": {
      "type": [
       "string",
       "null"
      ],
      "description": "Analyst category label (e.g. `technical`, `macro`); free text, null when unset."
     },
     "slug": {
      "type": "string",
      "description": "Page slug (`/analyst/<slug>`)."
     },
     "follower_count": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0
     },
     "is_verified": {
      "type": "boolean"
     },
     "activity": {
      "type": "object",
      "required": [
       "last_activity_at",
       "active_last_3_months"
      ],
      "properties": {
       "last_activity_at": {
        "type": [
         "string",
         "null"
        ],
        "format": "date-time"
       },
       "active_last_3_months": {
        "type": [
         "boolean",
         "null"
        ],
        "description": "null = no activity on record (new or seed-only analyst), not \"inactive\"."
       }
      }
     },
     "track_record": {
      "type": "object",
      "required": [
       "accuracy_score",
       "call_count",
       "overall_rank",
       "category_ranks",
       "scored",
       "tier",
       "tiers"
      ],
      "properties": {
       "accuracy_score": {
        "type": [
         "number",
         "null"
        ],
        "minimum": 0,
        "maximum": 100,
        "description": "Overall accuracy, 0–100: Empirical-Bayes-adjusted hit rate of directional calls vs. subsequent price, shrunk toward the pool mean for small samples. Comparative within an asset; NOT a win probability."
       },
       "accuracy_sample_size": {
        "type": [
         "integer",
         "null"
        ],
        "minimum": 0,
        "description": "Evaluated calls behind accuracy_score; below ~10 is weak evidence."
       },
       "call_count": {
        "type": [
         "integer",
         "null"
        ],
        "minimum": 0,
        "description": "All-time directional (bullish or bearish) posts on tracked assets."
       },
       "bull_score": {
        "type": [
         "number",
         "null"
        ],
        "minimum": 0,
        "maximum": 100,
        "description": "Accuracy, 0–100, on calls made in bull market phases."
       },
       "bear_score": {
        "type": [
         "number",
         "null"
        ],
        "minimum": 0,
        "maximum": 100,
        "description": "Accuracy, 0–100, on calls made in bear market phases."
       },
       "short_score": {
        "type": [
         "number",
         "null"
        ],
        "minimum": 0,
        "maximum": 100,
        "description": "Accuracy, 0–100, of calls evaluated over the short horizon."
       },
       "long_score": {
        "type": [
         "number",
         "null"
        ],
        "minimum": 0,
        "maximum": 100,
        "description": "Accuracy, 0–100, of calls evaluated over the long horizon."
       },
       "balanced_score": {
        "type": [
         "number",
         "null"
        ],
        "minimum": 0,
        "maximum": 100,
        "description": "Mean of bull_score and bear_score when both exist."
       },
       "overall_rank": {
        "type": [
         "object",
         "null"
        ],
        "properties": {
         "rank": {
          "type": "integer",
          "minimum": 1
         },
         "total": {
          "type": "integer",
          "minimum": 1
         }
        },
        "description": "Rank by accuracy_score among every analyst holding one."
       },
       "category_ranks": {
        "type": "object",
        "properties": {
         "bull": {
          "type": [
           "object",
           "null"
          ],
          "properties": {
           "rank": {
            "type": "integer",
            "minimum": 1
           },
           "total": {
            "type": "integer",
            "minimum": 1
           }
          }
         },
         "bear": {
          "type": [
           "object",
           "null"
          ],
          "properties": {
           "rank": {
            "type": "integer",
            "minimum": 1
           },
           "total": {
            "type": "integer",
            "minimum": 1
           }
          }
         },
         "short": {
          "type": [
           "object",
           "null"
          ],
          "properties": {
           "rank": {
            "type": "integer",
            "minimum": 1
           },
           "total": {
            "type": "integer",
            "minimum": 1
           }
          }
         },
         "long": {
          "type": [
           "object",
           "null"
          ],
          "properties": {
           "rank": {
            "type": "integer",
            "minimum": 1
           },
           "total": {
            "type": "integer",
            "minimum": 1
           }
          }
         }
        }
       },
       "best_ranking": {
        "type": [
         "object",
         "null"
        ],
        "properties": {
         "category": {
          "type": "string",
          "enum": [
           "bull",
           "bear",
           "short",
           "long",
           "overall"
          ]
         },
         "rank": {
          "type": "integer",
          "minimum": 1
         },
         "label": {
          "type": "string"
         },
         "priority": {
          "type": "integer"
         }
        },
        "description": "The analyst's best top-10 placement across the categories, if any."
       },
       "scored": {
        "type": "boolean"
       },
       "tier": {
        "description": "Headline badge: the best tier across the analyst's scored asset classes (1 < 5 < 10, then class order). null = nothing stored for the analyst (show \"Tracked\").",
        "anyOf": [
         {
          "type": "object",
          "allOf": [
           {
            "$ref": "#/components/schemas/AccuracyTier"
           }
          ]
         },
         {
          "type": "null"
         }
        ]
       },
       "tiers": {
        "type": "object",
        "additionalProperties": {
         "$ref": "#/components/schemas/AccuracyTier"
        },
        "description": "Every asset class with a stored tier row, keyed by class. A class absent here is unscored for this analyst."
       }
      }
     },
     "calls": {
      "type": [
       "object",
       "null"
      ],
      "required": [
       "total",
       "bullish",
       "bearish",
       "neutral",
       "by_source",
       "definition",
       "recent_bullish_bias_pct",
       "recent_bias"
      ],
      "properties": {
       "total": {
        "type": "integer",
        "minimum": 0
       },
       "bullish": {
        "type": "integer",
        "minimum": 0
       },
       "bearish": {
        "type": "integer",
        "minimum": 0
       },
       "neutral": {
        "type": "integer",
        "minimum": 0,
        "description": "Neutral labelled posts — reported for context, never a call."
       },
       "by_source": {
        "type": [
         "object",
         "null"
        ],
        "description": "The analyst's directional calls per source over the whole stored history (sums to `total`). `benzinga` is always 0 for a tracked analyst (sell-side authors are not on the roster). null while the breakdown function is not on the database.",
        "required": [
         "x",
         "cryptoquant",
         "news",
         "tradingview",
         "seekingalpha",
         "substack",
         "benzinga",
         "other"
        ],
        "properties": {
         "x": {
          "type": "integer",
          "minimum": 0
         },
         "cryptoquant": {
          "type": "integer",
          "minimum": 0
         },
         "news": {
          "type": "integer",
          "minimum": 0
         },
         "tradingview": {
          "type": "integer",
          "minimum": 0
         },
         "seekingalpha": {
          "type": "integer",
          "minimum": 0
         },
         "substack": {
          "type": "integer",
          "minimum": 0
         },
         "benzinga": {
          "type": "integer",
          "minimum": 0
         },
         "other": {
          "type": "integer",
          "minimum": 0
         }
        }
       },
       "definition": {
        "type": "string",
        "description": "What one call is: one directional (bullish or bearish) item about one tracked asset, every source, whole history; an article on several tickers counts once per article × ticker pair."
       },
       "recent_bullish_bias_pct": {
        "type": [
         "number",
         "null"
        ],
        "minimum": 0,
        "maximum": 100,
        "description": "Bullish share of the last 30 directional posts within three months."
       },
       "recent_bias": {
        "type": [
         "string",
         "null"
        ],
        "enum": [
         "bullish",
         "bearish",
         "balanced",
         null
        ]
       }
      }
     },
     "covered_assets": {
      "type": "array",
      "items": {
       "type": "object",
       "required": [
        "symbol",
        "asset_class",
        "last_post_at"
       ],
       "properties": {
        "symbol": {
         "type": "string"
        },
        "asset_class": {
         "type": "string",
         "enum": [
          "crypto",
          "equities",
          "indices",
          "commodities"
         ]
        },
        "last_post_at": {
         "type": [
          "string",
          "null"
         ],
         "format": "date-time"
        }
       }
      },
      "description": "Most recent post first."
     },
     "citation_count": {
      "type": [
       "integer",
       "null"
      ],
      "minimum": 0
     },
     "score_notes": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      }
     },
     "methodology_url": {
      "type": "string",
      "format": "uri"
     },
     "docs_url": {
      "type": "string",
      "format": "uri"
     },
     "as_of": {
      "type": "string",
      "format": "date-time"
     }
    }
   }
  }
 }
}
