Hoot Lens Visits API

The Visits API from Hoot Lens — 3 operation(s) for visits.

GitHub OpenAPI

Operations 3

GET /v1/projects/{projectId}/sessions List visits #
GET /v1/projects/{projectId}/sessions/{sessionId} Get a visit #
GET /v1/projects/{projectId}/sessions/{sessionId}/narrative Narrative of a visit #

Work with this as data

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

MCP server

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

https://apis.io/mcp

Tools for apis

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

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/hootlens-com:hootlens-com-visits-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

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

Get an API key

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

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

OpenAPI Specification

hootlens-com-visits-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hoot Lens Visits API
  version: v1
  summary: Read website interaction evidence and log changes.
  description: 'The Hoot Lens REST API records website interaction evidence for the sites you add and returns it as visits, recordings, heatmaps, goals, funnels and change impact. This document describes the endpoints a person or an agent uses to read that evidence and to log changes. Everything returned is observed behavior with its evidence; the API makes no claims about visitors'' emotions or intent.


    Authentication. Send `Authorization: Bearer `. The credential is either a Firebase ID token (a signed-in person, whose role on each site decides what they may do) or a personal access token (`hl_pat_...`) made in the dashboard. A personal access token carries the scopes `read`, `replay` and `annotate`, belongs to one site, expires in 30, 60 or 90 days, and can do no more than its creator''s current role allows. Operations marked `x-hootlens-sign-in-only` need a signed-in person and answer 403 to a token.


    OAuth. The hosted MCP endpoint (`/mcp`, Streamable HTTP) signs assistants in with OAuth 2.1 authorization code and PKCE (S256), with discovery at `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`. Its access tokens (`hl_oat_...`) are bound to the MCP resource and are accepted only there; sending one to any route in this document answers 401. Use a personal access token or a Firebase ID token for these routes.


    Not described here. Collector intake, public tracker configuration, billing, OAuth, sharing links, issues and feedback, invitations, members, access tokens, notifications and the audit log also exist; see docs/API.md for them.


    Site (project) IDs are public. Authorization is enforced by the API on every request. Rate limits answer 429. A caller who is not a member of a site receives 404, so a site''s existence is not revealed.'
  contact:
    name: Parallel Platforms
    url: https://hootlens.com
servers:
- url: https://hootlens-api-368877148687.us-east1.run.app
  description: Production
security:
- bearerAuth: []
tags:
- name: Visits
paths:
  /v1/projects/{projectId}/sessions:
    get:
      tags:
      - Visits
      summary: List visits
      description: 'Visits (page views stitched by the tag), newest first, each with a one-line summary. A visit is listed when any of its page views matches the filters.


        Required token scopes are listed in x-hootlens-scopes. A signed-in person needs the matching permission on the site instead (the viewer and editor roles are presets of the permissions).'
      operationId: listSessions
      parameters:
      - $ref: '#/components/parameters/projectId'
      - name: cursor
        in: query
        required: false
        description: Pass the nextCursor of the previous page to continue.
        schema:
          type: string
          maxLength: 300
      - name: limit
        in: query
        required: false
        description: Page size.
        schema:
          type: integer
          minimum: 1
          maximum: 50
          default: 25
      - name: path
        in: query
        required: false
        description: Exact recorded page path.
        schema:
          type: string
          pattern: ^/
          maxLength: 300
      - name: group
        in: query
        required: false
        description: Page group ID, as shown in heatmap cohorts.
        schema:
          type: string
          minLength: 1
          maxLength: 300
      - name: locale
        in: query
        required: false
        description: Primary language code such as es.
        schema:
          type: string
          pattern: ^[a-z]{2,3}$
      - name: variant
        in: query
        required: false
        description: Experiment variant.
        schema:
          type: string
          minLength: 1
          maxLength: 100
      - name: tag
        in: query
        required: false
        description: Only recordings the team tagged with this lower-case tag.
        schema:
          type: string
          maxLength: 32
          pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$
      - $ref: '#/components/parameters/deviceClass'
      - name: minDurationMs
        in: query
        required: false
        description: Minimum duration in milliseconds.
        schema:
          type: integer
          minimum: 0
          maximum: 2592000000
      - name: signal
        in: query
        required: false
        description: 'Only page views with this kind of snag: rage is a pile-up, dead a no-show, error a misfire.'
        schema:
          type: string
          enum:
          - rage
          - dead
          - error
      - name: controlled
        in: query
        required: false
        description: Test visits (from Playwright runs) are excluded by default; include lists both, only lists just test visits.
        schema:
          type: string
          enum:
          - exclude
          - include
          - only
          default: exclude
      - name: automated
        in: query
        required: false
        description: Automated visits (bots, headless browsers, scripted clicking) are excluded by default; include lists both, only lists just those.
        schema:
          type: string
          enum:
          - exclude
          - include
          - only
          default: exclude
      - name: internal
        in: query
        required: false
        description: Internal visits (made by the site's own team, through the internal link or from a listed address) are excluded by default; include lists both, only lists just those.
        schema:
          type: string
          enum:
          - exclude
          - include
          - only
          default: exclude
      - name: goal
        in: query
        required: false
        description: 'Only page views that converted this goal: a goal ID or its name. An unknown goal is a 404.'
        schema:
          type: string
          minLength: 1
          maxLength: 100
      - name: element
        in: query
        required: false
        description: 'Only page views with a click on this element: a data-hoot-id (home-menu-toggle), an element key as findings and heatmaps list it, or a selector. Matches page views recorded after the filter shipped; each page view remembers its first 60 distinct clicked elements.'
        schema:
          type: string
          minLength: 1
          maxLength: 300
      - name: utmSource
        in: query
        required: false
        description: Campaign source.
        schema:
          type: string
          minLength: 1
          maxLength: 100
      - name: utmCampaign
        in: query
        required: false
        description: Campaign name.
        schema:
          type: string
          minLength: 1
          maxLength: 100
      - name: referrer
        in: query
        required: false
        description: Referrer origin or host.
        schema:
          type: string
          minLength: 1
          maxLength: 200
      - name: browser
        in: query
        required: false
        description: Browser family.
        schema:
          type: string
          enum:
          - chrome
          - safari
          - firefox
          - edge
          - other
      - name: os
        in: query
        required: false
        description: Operating system family.
        schema:
          type: string
          enum:
          - windows
          - macos
          - ios
          - android
          - linux
          - other
      - name: newVisitor
        in: query
        required: false
        description: true for new visitors, false for returning visitors.
        schema:
          type: string
          enum:
          - 'true'
          - 'false'
      - name: segmentId
        in: query
        required: false
        description: A saved segment ID. Its filters apply under any filters sent with the request.
        schema:
          $ref: '#/components/schemas/Id'
      - $ref: '#/components/parameters/from'
      - $ref: '#/components/parameters/to'
      responses:
        '200':
          description: A page of visits.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionPage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      security:
      - bearerAuth: []
      x-hootlens-scopes:
      - read
  /v1/projects/{projectId}/sessions/{sessionId}:
    get:
      tags:
      - Visits
      summary: Get a visit
      description: 'The page views of one visit, ordered by page index.


        Required token scopes are listed in x-hootlens-scopes. A signed-in person needs the matching permission on the site instead (the viewer and editor roles are presets of the permissions).'
      operationId: getSession
      parameters:
      - $ref: '#/components/parameters/projectId'
      - $ref: '#/components/parameters/sessionId'
      responses:
        '200':
          description: The visit.
          content:
            application/json:
              schema:
                type: object
                properties:
                  sessionId:
                    type: string
                  segments:
                    type: array
                    items:
                      $ref: '#/components/schemas/RecordingSummary'
                required:
                - sessionId
                - segments
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      security:
      - bearerAuth: []
      x-hootlens-scopes:
      - replay
  /v1/projects/{projectId}/sessions/{sessionId}/narrative:
    get:
      tags:
      - Visits
      summary: Narrative of a visit
      description: 'A step-by-step account of a whole visit, up to 25 page views. Observed behavior only; it never states emotion or intent. The person behind the credential must hold the replay permission on the site (403 replay_required otherwise).


        Required token scopes are listed in x-hootlens-scopes. A signed-in person needs the matching permission on the site instead (the viewer and editor roles are presets of the permissions).'
      operationId: getSessionNarrative
      parameters:
      - $ref: '#/components/parameters/projectId'
      - $ref: '#/components/parameters/sessionId'
      responses:
        '200':
          description: The narrative.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Narrative'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      security:
      - bearerAuth: []
      x-hootlens-scopes:
      - read
components:
  responses:
    NotFound:
      description: The site, or the record inside it, does not exist or the caller is not a member (a non-member cannot tell the two apart).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: The request fields are invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: The credential or the person's permissions on the site do not permit this action.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooManyRequests:
      description: A rate limit or a per-site limit was reached.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: The credential is missing, expired or revoked.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Id:
      type: string
      description: 'An identifier: 1 to 80 letters, digits, underscores or hyphens.'
      pattern: ^[a-zA-Z0-9_-]{1,80}$
    SessionSummary:
      type: object
      properties:
        sessionId:
          $ref: '#/components/schemas/Id'
        startedAt:
          type: integer
          description: Epoch milliseconds.
          minimum: 0
        endedAt:
          type: integer
          description: Epoch milliseconds.
          minimum: 0
        pageCount:
          type: integer
        paths:
          type: array
          items:
            type: string
        groups:
          type: array
          items:
            type: string
        locales:
          type: array
          items:
            type: string
        device:
          $ref: '#/components/schemas/DeviceClass'
        browser:
          type: string
          enum:
          - chrome
          - safari
          - firefox
          - edge
          - other
        os:
          type: string
          enum:
          - windows
          - macos
          - ios
          - android
          - linux
          - other
        maxScrollPercent:
          type: number
        marks:
          type: array
          items:
            $ref: '#/components/schemas/ClickMark'
        clickCount:
          type: integer
        rageClickCount:
          type: integer
        deadClickCount:
          type: integer
        errorClickCount:
          type: integer
        automated:
          type: boolean
        automationReasons:
          type: array
          items:
            type: string
        internal:
          type: boolean
          description: True when any page view of the visit was made by the site's own team.
        goals:
          type: array
          items:
            type: object
            properties:
              goalId:
                $ref: '#/components/schemas/Id'
              name:
                type: string
            required:
            - goalId
            - name
            additionalProperties: true
          description: The goals the visit converted, in goal order, under each goal's current definition.
        exitTo:
          type: string
          description: 'Where the visit went when its last click left through a link: a same-site path, or another site''s origin only.'
        summary:
          type: string
          description: One or two sentences describing the visit from observed behavior. Ends with the top snag when the visit has one.
        topSnag:
          type: object
          description: The element with the most snag clicks in the visit. Absent when the visit had none, and for visits recorded before it was kept.
          properties:
            kind:
              type: string
              enum:
              - pile-up
              - no-show
              - misfire
            count:
              type: integer
              description: The element's snag clicks of this kind in the visit.
            selector:
              type: string
            name:
              type: string
              description: The name the team gave the element, when it has one.
            label:
              type: string
              description: The most recent recorded label of the element.
            context:
              $ref: '#/components/schemas/ElementContext'
            interactive:
              type: boolean
              enum:
              - false
              description: False when the element is not a link, button or other control.
          required:
          - kind
          - count
          - selector
          additionalProperties: true
      required:
      - sessionId
      - startedAt
      - endedAt
      - pageCount
      - paths
      - device
      - clickCount
      - rageClickCount
      - deadClickCount
      - errorClickCount
      - summary
      additionalProperties: true
    ClickMark:
      type: object
      properties:
        t:
          type: integer
          description: Milliseconds from the start of the page view.
        k:
          type: string
          enum:
          - click
          - rage-click
          - dead-click
          - error-click
        y:
          type: number
          description: Where the click landed down the document, 0 to 1.
      required:
      - t
      - k
      additionalProperties: true
    RecordingSummary:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        sessionId:
          $ref: '#/components/schemas/Id'
        projectId:
          $ref: '#/components/schemas/Id'
        path:
          type: string
        revision:
          type: string
        buildId:
          type: string
          description: The release.
        variant:
          type: string
        viewport:
          $ref: '#/components/schemas/Viewport'
        deviceClass:
          $ref: '#/components/schemas/DeviceClass'
        startedAt:
          type: integer
          description: Epoch milliseconds.
          minimum: 0
        endedAt:
          type: integer
          description: Epoch milliseconds.
          minimum: 0
        pageIndex:
          type: integer
        group:
          type: string
          description: Page group ID.
        pageVersion:
          type: string
        browser:
          type: string
          enum:
          - chrome
          - safari
          - firefox
          - edge
          - other
        os:
          type: string
          enum:
          - windows
          - macos
          - ios
          - android
          - linux
          - other
        eventCount:
          type: integer
        batchCount:
          type: integer
        clickCount:
          type: integer
        rageClickCount:
          type: integer
        deadClickCount:
          type: integer
        errorClickCount:
          type: integer
        errorCount:
          type: integer
          description: JavaScript errors recorded in the page view.
        marks:
          type: array
          items:
            $ref: '#/components/schemas/ClickMark'
        tags:
          type: array
          items:
            type: string
        automated:
          type: boolean
          description: True when strong automation signals fired.
        automation:
          type: object
          properties:
            level:
              type: string
              enum:
              - suspected
              - automated
          additionalProperties: true
        internal:
          type: boolean
          description: True when the site's own team made this page view. It reaches no aggregate.
        goalIds:
          type: array
          items:
            type: string
          description: Goals this page view converted, as goal IDs.
        exitTo:
          type: string
          description: 'Where the last link click led when it left the page: a same-site path, or another site''s origin only. Never a query or fragment.'
      required:
      - id
      - sessionId
      - projectId
      - path
      - startedAt
      - endedAt
      - eventCount
      - batchCount
      - clickCount
      - rageClickCount
      - deadClickCount
      - errorClickCount
      additionalProperties: true
    DeviceClass:
      type: string
      enum:
      - desktop
      - tablet
      - mobile
    Error:
      type: object
      properties:
        error:
          type: string
          description: A short message safe to show to a person.
        code:
          type: string
          description: A stable machine-readable code, on some errors.
      required:
      - error
      additionalProperties: true
    SessionPage:
      type: object
      properties:
        sessions:
          type: array
          items:
            $ref: '#/components/schemas/SessionSummary'
        nextCursor:
          type: string
          description: Pass back as cursor. Absent on the last page.
      required:
      - sessions
      additionalProperties: true
    ElementContext:
      type: object
      description: Short context recorded with a click. The strict preset records tag and region only.
      properties:
        tag:
          type: string
          description: Tag of the clicked element, such as div, li, p or img.
        region:
          type: string
          description: 'The nearest region: a data-hoot-region, an ancestor data-hoot-id, a section with an id or aria-label, else the nearest landmark tag. At most 60 characters.'
        heading:
          type: string
          description: Text of the nearest h1 to h3 above the element. Full and balanced presets only; at most 60 characters.
        text:
          type: string
          description: The element's own text, shortened. Full and balanced presets only; at most 60 characters.
      additionalProperties: true
    Narrative:
      type: object
      properties:
        summary:
          type: string
        facts:
          type: object
          properties:
            durationMs:
              type: integer
            activeMs:
              type: integer
            pages:
              type: array
              items:
                type: string
            device:
              $ref: '#/components/schemas/DeviceClass'
            browser:
              type: string
            os:
              type: string
            referrer:
              type: string
            utmSource:
              type: string
            maxScrollPercent:
              type: number
            clicks:
              type: integer
            touch:
              type: boolean
            pileUps:
              type: integer
            pileUpsOnNonControls:
              type: integer
              description: Pile-up episodes on an element that is not a link, button or other control. Absent when none.
            noShows:
              type: integer
            misfires:
              type: integer
            exitPage:
              type: string
          required:
          - durationMs
          - activeMs
          - pages
          - device
          - clicks
          - pileUps
          - noShows
          - misfires
          - exitPage
          additionalProperties: true
        steps:
          type: array
          items:
            type: object
            properties:
              atMs:
                type: integer
              clock:
                type: string
              kind:
                type: string
                enum:
                - landed
                - scrolled
                - clicked
                - snag
                - error
                - navigated
                - resized
                - idle
                - left
              text:
                type: string
              recordingId:
                type: string
                description: The page view the moment belongs to.
              offsetMs:
                type: integer
                description: Position to seek to in that recording.
              selector:
                type: string
              label:
                type: string
              source:
                type: string
                description: Source file and line behind the clicked element, such as `src/components/Pricing.tsx:42`. Present on click and snag steps when the site uses the Hoot Lens build plugin.
              interactive:
                type: boolean
                enum:
                - false
                description: False when the clicked element is not a link, button or other control. Present only on click and snag steps for such elements.
            required:
            - atMs
            - clock
            - kind
            - text
            - recordingId
            - offsetMs
            additionalProperties: true
        omittedSteps:
          type: integer
        omittedPages:
          type: integer
        note:
          type: string
        annotations:
          type: object
          description: 'The team''s own notes and tags: free text a person wrote, never captured from visitors.'
          properties: {}
          additionalProperties: true
      required:
      - summary
      - facts
      - steps
      - omittedSteps
      - omittedPages
      additionalProperties: true
    Viewport:
      type: object
      properties:
        width:
          type: integer
        height:
          type: integer
      required:
      - width
      - height
      additionalProperties: true
  parameters:
    sessionId:
      name: sessionId
      in: path
      required: true
      description: 'A visit ID (a session in the API): the page views of one visit.'
      schema:
        $ref: '#/components/schemas/Id'
    deviceClass:
      name: deviceClass
      in: query
      required: false
      description: Device class. Viewports are bucketed by device class, never by exact pixel size.
      schema:
        $ref: '#/components/schemas/DeviceClass'
    to:
      name: to
      in: query
      required: false
      description: End of the range, epoch milliseconds. Defaults to now.
      schema:
        type: integer
        minimum: 0
    projectId:
      name: projectId
      in: path
      required: true
      description: The project ID. Project IDs are public identifiers, not secrets.
      schema:
        $ref: '#/components/schemas/Id'
    from:
      name: from
      in: query
      required: false
      description: Start of the range, epoch milliseconds. Counts are daily, so the range covers every UTC day it touches.
      schema:
        type: integer
        minimum: 0
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A Firebase ID token, or a personal access token (hl_pat_...) with the scopes read, replay, annotate, configure, conversions:write. OAuth access tokens (hl_oat_...) are for the hosted MCP resource only.
externalDocs:
  description: Full API reference, including endpoints not described here
  url: https://hootlens.com/docs/API.md