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

# Get score series

> Score time series.

Ascending by date over the window. Without `country`: omit `platform` for the AIS Score series (each point carrying the scoring generation PERSISTED with it, or null where that was not recorded); set it for one platform's composite series (`score_version` always null); competitor ids are accepted — sentiment is null by design, a brand scored under several own brands keeps the latest pairing's numbers (this series is the persisted row verbatim, so that caveat covers an own brand tracked as a sibling's rival as well), and `score_version` follows the same rule as an own brand's, since the same writer scores competitors, so a competitor point scored since that generation was first persisted carries it and only older points are null. With `country`: the per-market series — every item is a `CountryScorePoint` whose `score` is `visibility_index` (`score_kind: "visibility_index"`), NOT the headline AIS Score; `platform` narrows it to one platform's rows; own brands only, and an unsupported market is 400 `validation_error` (`unsupported_country`). The two series issue distinct cursors.



## OpenAPI

````yaml /api-reference/openapi.json get /orgs/{orgId}/brands/{brandId}/scores
openapi: 3.1.0
info:
  title: Surfais API
  version: 1.0.0
  description: >-
    The Surfais API gives programmatic access to the AI-visibility data Surfais
    measures for your brands: organisations, brands and competitors, tracked
    prompts, per-run results and mentions, scores, share of voice and cited
    sources. It can also provision brands, manage prompts and competitors,
    request on-demand scans, and deliver signed webhooks when a scan completes
    or a score or sentiment threshold is crossed.


    Base URL: `https://api.surfais.com/v1`. Send your API key as a Bearer token
    (`Authorization: Bearer sfs_live_…`). Test keys (`sfs_test_…`) are rejected
    by the production API. Requests and responses are JSON: a success is `{
    "data": … }`, an error is `{ "error": { "code", "message" }, "request_id"
    }`.


    Guides for authentication, pagination, errors, rate limits, idempotency and
    webhooks are at https://docs.surfais.com/api/introduction.
  contact:
    name: Surfais support
    email: support@surfais.com
    url: https://docs.surfais.com/api/support
servers:
  - url: https://api.surfais.com/v1
    description: >-
      Production (the `/v1` prefix is part of the server URL; paths below are
      relative to it).
security:
  - bearerKey: []
tags:
  - name: orgs
    description: Organisations reachable by the key, their usage, and the partner link.
  - name: brands
    description: Own brands (properties) and competitor rows.
  - name: prompts
    description: 'Tracked prompts: reads, CRUD and the declarative sync.'
  - name: results
    description: Finalised run-level data.
  - name: scores
    description: Persisted scores, per-market history and share of voice.
  - name: sources
    description: Cited domains.
  - name: scans
    description: On-demand scan requests.
  - name: webhooks
    description: >-
      Outbound event delivery: endpoints, subscriptions, deliveries (partner
      keys).
externalDocs:
  description: Guides, concepts and webhooks
  url: https://docs.surfais.com/api/introduction
paths:
  /orgs/{orgId}/brands/{brandId}/scores:
    get:
      tags:
        - scores
      summary: Get score series
      description: >-
        Score time series.


        Ascending by date over the window. Without `country`: omit `platform`
        for the AIS Score series (each point carrying the scoring generation
        PERSISTED with it, or null where that was not recorded); set it for one
        platform's composite series (`score_version` always null); competitor
        ids are accepted — sentiment is null by design, a brand scored under
        several own brands keeps the latest pairing's numbers (this series is
        the persisted row verbatim, so that caveat covers an own brand tracked
        as a sibling's rival as well), and `score_version` follows the same rule
        as an own brand's, since the same writer scores competitors, so a
        competitor point scored since that generation was first persisted
        carries it and only older points are null. With `country`: the
        per-market series — every item is a `CountryScorePoint` whose `score` is
        `visibility_index` (`score_kind: "visibility_index"`), NOT the headline
        AIS Score; `platform` narrows it to one platform's rows; own brands
        only, and an unsupported market is 400 `validation_error`
        (`unsupported_country`). The two series issue distinct cursors.
      operationId: listBrandScores
      parameters:
        - in: path
          name: orgId
          schema:
            type: string
            format: uuid
            pattern: >-
              ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
            description: >-
              Organisation id. Org keys: the key's own org. Partner keys: any
              org with an active link. Anything else is 404.
          required: true
          description: >-
            Organisation id. Org keys: the key's own org. Partner keys: any org
            with an active link. Anything else is 404.
        - in: path
          name: brandId
          schema:
            type: string
            format: uuid
            pattern: >-
              ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
            description: >-
              Brand id within the org (own brand or competitor row); 404 when
              not in the org. Writes that add tracking need an own (400
              `validation_error` with `details[].code = "own_brand_only"`),
              non-archived (409 `brand_archived`) brand.
          required: true
          description: >-
            Brand id within the org (own brand or competitor row); 404 when not
            in the org. Writes that add tracking need an own (400
            `validation_error` with `details[].code = "own_brand_only"`),
            non-archived (409 `brand_archived`) brand.
        - in: query
          name: limit
          schema:
            default: 50
            description: Page size (1–200). Default 50.
            type: integer
            minimum: 1
            maximum: 200
          description: Page size (1–200). Default 50.
        - in: query
          name: cursor
          schema:
            description: >-
              Opaque cursor from a previous page's `pagination.next_cursor`. It
              pins the walk — its sort anchor, the resolved date window and the
              FILTERS the first page ran under — so continue with the cursor
              alone: omitted filters are restored from it. Naming a different
              value for one of them is 400 `invalid_cursor` (`details.reason =
              "filter_changed"`, `details.parameter` the one that differs);
              start again from the first page to change a filter.
            type: string
            maxLength: 2048
          description: >-
            Opaque cursor from a previous page's `pagination.next_cursor`. It
            pins the walk — its sort anchor, the resolved date window and the
            FILTERS the first page ran under — so continue with the cursor
            alone: omitted filters are restored from it. Naming a different
            value for one of them is 400 `invalid_cursor` (`details.reason =
            "filter_changed"`, `details.parameter` the one that differs); start
            again from the first page to change a filter.
        - in: query
          name: from
          schema:
            description: 'Window start (inclusive). Default: `to` minus 30 days.'
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
            format: date
          description: 'Window start (inclusive). Default: `to` minus 30 days.'
        - in: query
          name: to
          schema:
            description: >-
              Window end (inclusive). Default: today (UTC). Max span 366 days;
              `from` after `to` is 400 `validation_error`. On a paginated
              endpoint the resolved window is pinned by the first page and
              carried in the cursor — continue with the cursor alone; naming a
              different `from`/`to` on a continuation is 400 `invalid_cursor`
              (`details.reason = "window_changed"`).
            type: string
            pattern: ^\d{4}-\d{2}-\d{2}$
            format: date
          description: >-
            Window end (inclusive). Default: today (UTC). Max span 366 days;
            `from` after `to` is 400 `validation_error`. On a paginated endpoint
            the resolved window is pinned by the first page and carried in the
            cursor — continue with the cursor alone; naming a different
            `from`/`to` on a continuation is 400 `invalid_cursor`
            (`details.reason = "window_changed"`).
        - in: query
          name: platform
          schema:
            description: >-
              Omit for the all-platform aggregate series (the AIS Score; with
              `country`, the market's all-platform `visibility_index`); set for
              one platform's series (its composite score; with `country`, that
              platform's `visibility_index` in the market).
            type: string
            enum:
              - perplexity
              - chatgpt
              - gemini
              - claude
              - ai_overviews
          description: >-
            Omit for the all-platform aggregate series (the AIS Score; with
            `country`, the market's all-platform `visibility_index`); set for
            one platform's series (its composite score; with `country`, that
            platform's `visibility_index` in the market).
        - in: query
          name: country
          schema:
            description: >-
              Per-market series: `data` items are `CountryScorePoint` (`score` =
              `visibility_index`, `score_kind` = `visibility_index`) instead of
              `ScorePoint`. Must be a market Surfais scans (400
              `validation_error`, `details[].code = "unsupported_country"`
              otherwise); a supported market with no rows is an empty series.
              Own brands only (400 `validation_error` with `details[].code =
              "own_brand_only"` for a competitor id). Bound into that series'
              cursor: continue with the cursor alone and the market is restored,
              and naming a different one is 400 `invalid_cursor` with
              `details.reason = "filter_changed"`.
            type: string
            pattern: ^[A-Za-z]{2}$
          description: >-
            Per-market series: `data` items are `CountryScorePoint` (`score` =
            `visibility_index`, `score_kind` = `visibility_index`) instead of
            `ScorePoint`. Must be a market Surfais scans (400
            `validation_error`, `details[].code = "unsupported_country"`
            otherwise); a supported market with no rows is an empty series. Own
            brands only (400 `validation_error` with `details[].code =
            "own_brand_only"` for a competitor id). Bound into that series'
            cursor: continue with the cursor alone and the market is restored,
            and naming a different one is 400 `invalid_cursor` with
            `details.reason = "filter_changed"`.
      responses:
        '200':
          description: >-
            Score points — `ScorePoint` items, or `CountryScorePoint` items when
            `country` is set.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      anyOf:
                        - $ref: '#/components/schemas/ScorePoint'
                        - $ref: '#/components/schemas/CountryScorePoint'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                required:
                  - data
                  - pagination
        '400':
          description: >-
            `body_not_allowed` — a GET or HEAD that declared a body, refused by
            the intake guard BEFORE the parser, which is the only 400 a read
            operation reaches by that route — or `validation_error` (with
            `details`), `invalid_cursor`, `invalid_json` or `invalid_body`, the
            last two reachable only where a body is accepted at all. All are
            raised after authentication and the minute charge (the body is
            parsed only for an authenticated, admitted request), so every 400
            carries the `X-RateLimit-*` trio and is metered.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: '`invalid_api_key` — uniform for every authentication failure.'
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: >-
            `tier_not_entitled` — the org key's organisation left an
            API-entitled tier. The request is charged to the key's minute
            allowance (so it carries the `X-RateLimit-*` trio, and an exhausted
            allowance answers 429 first) and is metered.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: >-
            `not_found` — the org is not reachable by this key or a nested id is
            not in it. Raised after authentication and the minute charge, so it
            carries the `X-RateLimit-*` trio and is metered.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '413':
          description: >-
            `payload_too_large` — a JSON body over 1 MB. The body is parsed only
            for an authenticated, admitted request, so this carries the
            `X-RateLimit-*` trio and is metered.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: >-
            `rate_limited` (per-minute limit, or too many failed authentications
            from this address) or `quota_exceeded`.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            Retry-After:
              required: true
              description: Seconds to wait before retrying — present on every 429 and 503.
              schema:
                type: string
                description: >-
                  Seconds to wait before retrying — present on every 429 and
                  503.
            X-RateLimit-Limit:
              required: true
              description: >-
                Requests allowed per minute for this key. On a 429
                `rate_limited` from the failed-authentication gate: failed
                attempts allowed per minute for the client address.
              schema:
                type: string
                description: >-
                  Requests allowed per minute for this key. On a 429
                  `rate_limited` from the failed-authentication gate: failed
                  attempts allowed per minute for the client address.
            X-RateLimit-Remaining:
              required: true
              description: >-
                Requests left in the current minute window (0 on the
                failed-authentication 429).
              schema:
                type: string
                description: >-
                  Requests left in the current minute window (0 on the
                  failed-authentication 429).
            X-RateLimit-Reset:
              required: true
              description: Unix seconds at which the current minute window ends.
              schema:
                type: string
                description: Unix seconds at which the current minute window ends.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: >-
            `internal_error` — quote `request_id`. A failure inside a route
            answers after the minute charge and carries the `X-RateLimit-*`
            trio; a failure before the limiter carries only `X-Request-Id`.
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            X-RateLimit-Limit:
              description: >-
                Present when the failure occurred after the minute charge
                (inside a route); absent on a failure before the limiter.
                Requests allowed per minute for this key.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge
                  (inside a route); absent on a failure before the limiter.
                  Requests allowed per minute for this key.
                type: string
            X-RateLimit-Remaining:
              description: >-
                Present when the failure occurred after the minute charge.
                Requests left in the current minute window.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge.
                  Requests left in the current minute window.
                type: string
            X-RateLimit-Reset:
              description: >-
                Present when the failure occurred after the minute charge. Unix
                seconds at which the current minute window ends.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge.
                  Unix seconds at which the current minute window ends.
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          description: >-
            `api_unavailable` (switched off; `Retry-After` is 60),
            `rate_limit_unavailable` (fail-closed limiter — its store, or a
            saturated failed-authentication gate; before the minute charge, so
            no `X-RateLimit-*` trio), or `store_unavailable` (a tenant-store
            call behind an authenticated request exceeded 15000 ms — raised
            after the minute charge, so it is counted and carries the trio).
          headers:
            X-Request-Id:
              required: true
              description: >-
                Correlation id for this request; equals `request_id` in an error
                envelope.
              schema:
                type: string
                description: >-
                  Correlation id for this request; equals `request_id` in an
                  error envelope.
            Retry-After:
              required: true
              description: Seconds to wait before retrying — present on every 429 and 503.
              schema:
                type: string
                description: >-
                  Seconds to wait before retrying — present on every 429 and
                  503.
            X-RateLimit-Limit:
              description: >-
                Present when the failure occurred after the minute charge
                (inside a route); absent on a failure before the limiter.
                Requests allowed per minute for this key.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge
                  (inside a route); absent on a failure before the limiter.
                  Requests allowed per minute for this key.
                type: string
            X-RateLimit-Remaining:
              description: >-
                Present when the failure occurred after the minute charge.
                Requests left in the current minute window.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge.
                  Requests left in the current minute window.
                type: string
            X-RateLimit-Reset:
              description: >-
                Present when the failure occurred after the minute charge. Unix
                seconds at which the current minute window ends.
              schema:
                description: >-
                  Present when the failure occurred after the minute charge.
                  Unix seconds at which the current minute window ends.
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
components:
  schemas:
    ScorePoint:
      type: object
      properties:
        date:
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
          description: Calendar date, `YYYY-MM-DD` (UTC).
          format: date
        score:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            Aggregate series: the AIS Score /100. Per-platform series
            (`?platform=`): that platform's diagnostic `composite_score`. Null
            when not yet computed (e.g. a competitor shared by several own
            brands).
        score_version:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Scoring model that PRODUCED this point's `score` — the generation
            persisted with it when it was scored, never the model the API is
            running today. Null only where no generation was recorded: points
            recorded before this field existed, and per-platform points
            (`?platform=`), whose `score` is the unversioned diagnostic
            composite rather than the headline model. Competitor brands are NOT
            a standing null — they are scored the same way, so a competitor
            point scored since this field existed carries its generation like
            any own-brand point.
        panel_version:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Measurement panel (model set) that produced the point. Compare
            scores only within one panel_version and — on the AIS series — one
            score_version.
        presence_pct:
          type: number
          description: '% of runs mentioning the brand.'
        avg_position:
          type: number
        avg_sentiment:
          anyOf:
            - type: number
            - type: 'null'
        source_visibility_pct:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            % of mentioning runs citing an owned domain; null when the brand has
            no domains.
        composite_score:
          type: number
          description: >-
            Internal diagnostic composite (presence/position/sentiment/platform
            coverage/stability weighted 30/25/15/15/15) — NOT the headline.
        total_mentions:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        total_runs:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        citation_count:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
      required:
        - date
        - score
        - score_version
        - panel_version
        - presence_pct
        - avg_position
        - avg_sentiment
        - source_visibility_pct
        - composite_score
        - total_mentions
        - total_runs
        - citation_count
    CountryScorePoint:
      type: object
      properties:
        date:
          type: string
          pattern: ^\d{4}-\d{2}-\d{2}$
          description: Calendar date, `YYYY-MM-DD` (UTC).
          format: date
        country:
          type: string
          description: Upper-cased alpha-2 — the market this point describes.
        score:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            The visibility index: the per-country blend of presence / position /
            sentiment / platform coverage / stability through the canonical
            composite functions. NOT the headline AIS Score (which is not
            country-decomposable) and NOT `composite_score` (different grain and
            universe). Null when the writer could not compute it.
        score_kind:
          type: string
          const: visibility_index
          description: >-
            Always `visibility_index` on this series — the discriminator against
            the all-market `ScorePoint`.
        presence_pct:
          type: number
          description: '% of the brand''s unit-grain runs in the country that mentioned it.'
        avg_position:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            Mean ordinal when mentioned; null when nothing was mentioned (not
            0).
        avg_sentiment:
          anyOf:
            - type: number
            - type: 'null'
          description: Mean scored sentiment when mentioned; null when none was scored.
        sov_pct:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            Aggregate rows only: own ÷ (own + tracked competitors + unattributed
            field) × 100 within the country; null on per-platform rows and when
            a term could not be computed.
        citation_count:
          type: integer
          minimum: -9007199254740991
          maximum: 9007199254740991
        source_visibility_pct:
          anyOf:
            - type: number
            - type: 'null'
          description: >-
            % of mentioning runs citing an owned domain; null when the brand has
            no domains.
        panel_version:
          type: string
          description: Measurement panel that produced the point — compare only within one.
        score_version:
          type: string
          description: Scoring-model generation of the series.
      required:
        - date
        - country
        - score
        - score_kind
        - presence_pct
        - avg_position
        - avg_sentiment
        - sov_pct
        - citation_count
        - source_visibility_pct
        - panel_version
        - score_version
    Pagination:
      type: object
      properties:
        next_cursor:
          anyOf:
            - type: string
            - type: 'null'
          description: Opaque cursor for the next page; null on the last page.
        has_more:
          type: boolean
      required:
        - next_cursor
        - has_more
    ErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Stable machine code — see the error list in the description.
            message:
              type: string
            details: {}
          required:
            - code
            - message
        request_id:
          type: string
          description: Echoed in the `X-Request-Id` header.
      required:
        - error
        - request_id
  securitySchemes:
    bearerKey:
      type: http
      scheme: bearer
      bearerFormat: sfs_live_… / sfs_test_…
      description: >-
        A Surfais API key — an organisation key or a partner key. Issued by
        Surfais; shown once. Rotate by creating a new key, then revoking the old
        one.

````