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

# Get fan insights in bulk (POST batch)

> Returns detailed insights for up to 100 fans in a single request, keyed by the input fan UUID.

This is the high-cap, JSON-body counterpart to `GET /insights/fans` (capped at 20 via query string). It is intended for initial-sync flows that need to hydrate many fans without per-fan round trips.

Per-key errors are reported inside the 200 response body so a single forbidden or missing fan never collapses the whole request — consumers can keep partial results and only retry the failing keys.

Every field carries the same meaning as on `GET /insights/fans/{userUuid}`, including which money fields are net of reversals and which are gross, and the same absence of caching.

<Note>Maximum 100 fan UUIDs per request. Failed keys are reported as `{ "error": "forbidden" | "not_found" | "internal" }`.</Note>

<Info>
  **Required scopes**

  * `read:insights` — Access analytics, metrics, and insights data for performance tracking.
  * `read:fan` — Access fan-related data and information within the platform.
</Info>


## OpenAPI

````yaml /openapi-v1.json post /v1/insights/fans/batch
openapi: 3.1.0
info:
  title: Fanvue API
  version: '0.1'
servers:
  - url: https://api.fanvue.com
security: []
paths:
  /v1/insights/fans/batch:
    post:
      summary: Get fan insights in bulk (POST batch)
      description: >-
        Returns detailed insights for up to 100 fans in a single request, keyed
        by the input fan UUID.


        This is the high-cap, JSON-body counterpart to `GET /insights/fans`
        (capped at 20 via query string). It is intended for initial-sync flows
        that need to hydrate many fans without per-fan round trips.


        Per-key errors are reported inside the 200 response body so a single
        forbidden or missing fan never collapses the whole request — consumers
        can keep partial results and only retry the failing keys.


        Every field carries the same meaning as on `GET
        /insights/fans/{userUuid}`, including which money fields are net of
        reversals and which are gross, and the same absence of caching.


        <Note>Maximum 100 fan UUIDs per request. Failed keys are reported as `{
        "error": "forbidden" | "not_found" | "internal" }`.</Note>
      operationId: batchFanInsights
      parameters:
        - $ref: '#/components/parameters/ApiVersionHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                userUuids:
                  type: array
                  items:
                    type: string
                    format: uuid
                  minItems: 1
                  maxItems: 100
                  description: Array of fan UUIDs to fetch insights for (1-100)
              required:
                - userUuids
      responses:
        '200':
          description: >-
            Per-key insights or error for each requested fan. Always 200 when
            the request itself is valid, even if every key fails.
          content:
            application/json:
              schema:
                type: object
                additionalProperties:
                  anyOf:
                    - type: object
                      properties:
                        status:
                          type: string
                          enum:
                            - subscriber
                            - expired
                            - follower
                            - not_contactable
                          description: Current fan status
                        spending:
                          type: object
                          properties:
                            lastPurchaseAt:
                              type:
                                - string
                                - 'null'
                              format: date-time
                              description: >-
                                When this fan last paid this creator (ISO 8601),
                                or null if they never have. Fan-initiated
                                payments only, so an automatic subscription
                                renewal never moves it, and App Store purchases
                                do not count as a purchase here. Reversed
                                purchases are NOT skipped: a purchase later
                                refunded or charged back still counts as the
                                last purchase, because the fan did reach for
                                their wallet at that moment, which makes this
                                recency of intent rather than recency of valid
                                spend. Use `lastValidPurchaseAt` for the latter.
                            lastValidPurchaseAt:
                              type:
                                - string
                                - 'null'
                              format: date-time
                              description: >-
                                Same as `lastPurchaseAt` but skipping purchases
                                later refunded or charged back, so this is
                                recency of valid spend. Null when every
                                self-initiated purchase was reversed.
                            total:
                              type: object
                              properties:
                                gross:
                                  type: number
                                  description: >-
                                    DEPRECATED: use `total`. Total amount this
                                    fan paid across all transactions, in USD
                                    cents. Same value as `total`, and net of
                                    reversals despite the field name.
                                total:
                                  type: number
                                  description: >-
                                    Total amount this fan paid across all
                                    transactions, in USD cents. Excludes App
                                    Store purchases, which record a creator
                                    buying an app from its developer rather than
                                    a fan paying this creator. Net of reversals:
                                    a refund or chargeback is a separate invoice
                                    for the full original amount and is
                                    subtracted here, so this is normally lower
                                    than the sum of the gross `sources` figures
                                    and can be negative. Reversals are selected
                                    by their own type rather than by what they
                                    reverse, so a clawed-back creator reward the
                                    fan never paid for is subtracted here too,
                                    and this can fall below what the fan
                                    actually paid. It is not the same as
                                    `netTotal`, which is aggregated over the
                                    surviving purchases instead; the two differ
                                    for a purchase reversed without a reversal
                                    invoice, which has no negative row for this
                                    figure to subtract.
                                netTotal:
                                  type: number
                                  description: >-
                                    Total this fan paid with refunded and
                                    charged-back purchases removed, in USD
                                    cents. Aggregated over the surviving
                                    purchases themselves, so it and the
                                    per-source `netTotal` values describe one
                                    set of purchases and add up. It usually
                                    equals `total`, which subtracts reversals by
                                    counting their negative rows; the two part
                                    company only for a reversal recorded on the
                                    original purchase without a row of its own,
                                    which `total` cannot see.
                              required:
                                - gross
                                - total
                                - netTotal
                            maxSinglePayment:
                              type: object
                              properties:
                                gross:
                                  type: number
                                  description: >-
                                    DEPRECATED: use `total`. Largest single
                                    payment this fan made, in USD cents. Same
                                    value as `total`, and excludes reversed
                                    purchases.
                                total:
                                  type: number
                                  description: >-
                                    Largest single payment this fan made, in USD
                                    cents. Excludes App Store purchases, as
                                    `total` does. Excludes reversed purchases: a
                                    payment later refunded or charged back is
                                    not eligible, so this can be lower than the
                                    largest amount the fan ever paid. Reversal
                                    invoices themselves are never counted. This
                                    has been the behaviour since July 2025. A
                                    refund or chargeback marker on the payment
                                    counts as a reversal in its own right when
                                    no reversal invoice is linked, so a purchase
                                    reversed that way is excluded too; a
                                    purchase whose reversal was created and then
                                    failed stays eligible, because that reversal
                                    never settled.
                              required:
                                - gross
                                - total
                            sources:
                              type: object
                              additionalProperties:
                                type: object
                                properties:
                                  gross:
                                    type: number
                                    description: >-
                                      DEPRECATED: use `total`. Total amount paid
                                      by the fan for this source type, in USD
                                      cents. Same value as `total`, and gross of
                                      reversals.
                                  total:
                                    type: number
                                    description: >-
                                      Total amount paid by the fan for this
                                      source type, in USD cents. Gross of
                                      reversals: a purchase that was later
                                      refunded or charged back is still counted
                                      here in full. Use `netTotal` for the
                                      figure with those purchases removed.
                                  netTotal:
                                    type: number
                                    description: >-
                                      Total paid from this source with refunded
                                      and charged-back purchases removed, in USD
                                      cents: the sum of the purchases that still
                                      stand, and the numerator of `netAverage`.
                                      Net of reversals only, not of Fanvue fees,
                                      unlike the `net` returned by
                                      /insights/earnings and
                                      /agencies/insights/*.
                                  count:
                                    type: integer
                                    description: >-
                                      Number of purchases the fan made from this
                                      source, counting purchases later refunded
                                      or charged back. The denominator of
                                      `average`.
                                  netCount:
                                    type: integer
                                    description: >-
                                      Number of purchases the fan made from this
                                      source that were not later refunded or
                                      charged back. The denominator of
                                      `netAverage`.
                                  average:
                                    type: number
                                    description: >-
                                      Average purchase from this source, in USD
                                      cents: `total` divided by `count`, so
                                      refunded and charged-back purchases count
                                      on both sides. Not rounded.
                                  netAverage:
                                    type:
                                      - number
                                      - 'null'
                                    description: >-
                                      Average surviving purchase from this
                                      source, in USD cents: the sum of this
                                      source's purchases that were never
                                      refunded or charged back, divided by
                                      `netCount`. Reversed purchases are dropped
                                      from both sides. `null` when `netCount` is
                                      0. Not rounded.
                                required:
                                  - gross
                                  - total
                                  - netTotal
                                  - count
                                  - netCount
                                  - average
                                  - netAverage
                              description: >-
                                Breakdown of this fan's spend by source, keyed
                                by the same source names /insights/earnings uses
                                (so `renewal` is a catch-all for every recurring
                                charge after the first payment). `appStore` is
                                never present: an App Store invoice records a
                                creator buying an app from its developer, not a
                                fan paying this creator. `fanExperience` is
                                fan-to-creator spend and is included. Refunds
                                and chargebacks are never a source entry of
                                their own. Each entry carries both treatments:
                                `total`, `count` and `average` are gross of
                                reversals, so the `total` values do not sum to
                                `total.total` and the difference is exactly the
                                reversals, while `netTotal`, `netCount` and
                                `netAverage` drop the reversed purchases and the
                                `netTotal` values do sum to `total.netTotal`.
                          required:
                            - lastPurchaseAt
                            - lastValidPurchaseAt
                            - total
                            - maxSinglePayment
                            - sources
                        subscription:
                          type: object
                          properties:
                            createdAt:
                              type:
                                - string
                                - 'null'
                              format: date-time
                              description: >-
                                Date subscription was created (ISO 8601) or null
                                if no subscription
                            renewsAt:
                              type:
                                - string
                                - 'null'
                              format: date-time
                              description: >-
                                Date subscription renews (ISO 8601) or null if
                                no active subscription
                            autoRenewalEnabled:
                              type: boolean
                              description: Whether fan has active recurring subscription
                          required:
                            - createdAt
                            - renewsAt
                            - autoRenewalEnabled
                      required:
                        - status
                        - spending
                        - subscription
                    - type: object
                      properties:
                        error:
                          type: string
                          enum:
                            - forbidden
                            - not_found
                            - internal
                      required:
                        - error
                description: >-
                  Map of input fan UUID to insights or a per-key error
                  (forbidden, not_found, or internal).
              example:
                11111111-1111-4111-8111-111111111111:
                  status: subscriber
                  spending:
                    lastPurchaseAt: '2026-04-12T08:32:00.000Z'
                    lastValidPurchaseAt: '2026-03-30T19:05:00.000Z'
                    total:
                      gross: 7000
                      total: 7000
                      netTotal: 7000
                    maxSinglePayment:
                      gross: 5000
                      total: 5000
                    sources:
                      subscription:
                        gross: 7000
                        total: 7000
                        netTotal: 7000
                        count: 7
                        netCount: 7
                        average: 1000
                        netAverage: 1000
                      tips:
                        gross: 5000
                        total: 5000
                        netTotal: 0
                        count: 2
                        netCount: 0
                        average: 2500
                        netAverage: null
                  subscription:
                    createdAt: '2026-01-15T00:00:00.000Z'
                    renewsAt: '2026-06-15T00:00:00.000Z'
                    autoRenewalEnabled: true
                22222222-2222-4222-8222-222222222222:
                  error: not_found
                33333333-3333-4333-9333-333333333333:
                  error: internal
        '400':
          description: Bad Request - API version not supported OR validation failed
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/UnsupportedVersionError'
                  - $ref: '#/components/schemas/ValidationError'
        '401':
          $ref: '#/components/responses/UnauthorizedResponse'
        '403':
          $ref: '#/components/responses/UnauthorizedResponse'
        '410':
          $ref: '#/components/responses/SunsetVersionResponse'
        '429':
          $ref: '#/components/responses/RateLimitResponse'
      security:
        - BearerAuth:
            - read:insights
            - read:fan
components:
  parameters:
    ApiVersionHeader:
      name: X-Fanvue-API-Version
      in: header
      required: true
      schema:
        type: string
        default: '2025-06-26'
        example: '2025-06-26'
      description: API version to use for the request
  schemas:
    UnsupportedVersionError:
      type: object
      properties:
        error:
          type: string
        message:
          type: string
      required:
        - error
        - message
      description: API version not supported
    ValidationError:
      type: object
      properties:
        errors:
          type: array
          items:
            type: string
      required:
        - errors
      description: Request validation failed
  responses:
    UnauthorizedResponse:
      description: Unauthorized Response
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
            required:
              - error
    SunsetVersionResponse:
      description: API version no longer supported (sunset)
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
              message:
                type: string
              nextVersion:
                type: string
            required:
              - error
              - message
    RateLimitResponse:
      description: Too many requests - rate limit exceeded
      headers:
        Retry-After:
          description: Number of seconds to wait before retrying the request
          schema:
            type: integer
        X-RateLimit-Limit:
          description: The maximum number of requests allowed in the current window
          schema:
            type: integer
        X-RateLimit-Remaining:
          description: The number of requests remaining in the current window
          schema:
            type: integer
        X-RateLimit-Reset:
          description: The Unix timestamp (seconds) when the rate limit window resets
          schema:
            type: integer
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
            required:
              - error
  securitySchemes:
    BearerAuth:
      type: oauth2
      description: >-
        OAuth 2.0 access token, presented as a JWT bearer token in the
        `Authorization` header. Obtain a token via the authorization-code flow;
        the scopes granted to the token determine which operations it may call.
      flows:
        authorizationCode:
          authorizationUrl: https://auth.fanvue.com/oauth2/auth
          tokenUrl: https://auth.fanvue.com/oauth2/token
          refreshUrl: https://auth.fanvue.com/oauth2/token
          scopes:
            read:self: >-
              Access your own user profile information, including basic account
              details and settings.
            read:chat: >-
              Read chat conversations, messages, and chat-related data. This
              includes viewing chat lists and message history.
            write:chat: >-
              Create new chats and send messages. This scope is required for any
              chat-related actions that modify data.
            read:experience: >-
              Exchange a fan's experience token for the resolved experience and
              the fan's identity, so an embedded fan-facing experience can
              render the right content.
            write:experience: >-
              Request that the creator publish or unpublish a fan-facing
              experience. The app mints a request token; the creator confirms
              and Fanvue performs the change.
            read:fan: Access fan-related data and information within the platform.
            read:post: Read posts, including post details, comments, likes, and tips.
            write:post: Create, edit, and manage posts and content on behalf of users.
            read:media: Access media files, images, videos, and other content assets.
            write:media: >-
              Upload, modify, and manage media files and content assets. Also
              required for vault folder management.
            read:creator: >-
              Access creator profiles, content, and creator-specific
              information.
            write:creator: Modify creator profiles, settings, and creator-specific data.
            read:insights: >-
              Access analytics, metrics, and insights data for performance
              tracking.
            read:tracking_links: >-
              Read tracking links and the users associated with them, including
              per-user tracking metadata.
            write:tracking_links: Create and delete tracking links.
            read:agency: Read agency information, including the agency's team members.
            write:agency: >-
              Manage agency team members and invites, including inviting new
              team members and creators.

````