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: 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