---
title: "Search website visitors"
method: POST
path: "/api/website-visitors/search"
tags: ["website_visitors"]
---

# Search website visitors

`POST /api/website-visitors/search`

## Request

- Base URL: `https://beta.getsales.io/signals/v1`
- URL: `https://beta.getsales.io/signals/v1/api/website-visitors/search`
- Auth: HTTP bearer and API key in header `Team-SID`

The browsers that visited the team's sites, one row per browser per site: the person it is bound to (person_sid, bound_by: code, identify, fingerprint or manual), the email an identify or a form gave when nobody matched (identified_email: the "Add to table" rows), how often it came (sessions_count), when (first_seen_at, last_seen_at), and what the newest session told about the browser, the place and the network. identified_email is_null false with person_sid is_null true = emails nobody has yet; person_sid eq = one person's browsers ("the same person on two devices"); is_ephemeral eq false hides the one-day strangers. q searches the email and the network name. page_size 0 returns the counts by bound_by and network type. The visits themselves are search_website_sessions; people by a signal are search_people. Private context: never tell the lead we know.

Contract:
- MCP tool `search_website_visitors`, registry package `mcp.signals/website_visitors`, mount `signals.visits`.
- Operation `search`, response envelope `search`.
- Flags: read only.

## Request body

- SearchWebsiteVisitorsRequest: Request body of `search_website_visitors`.
  - `filter` object: Field filters, ANDed: {field: {eq | ne | in | nin | gte | lte | gt | lt | is_null}}; q inside it is free text. Omit for every row.
    - `q` string: Free text over the identified email and the network's name and domain (a LIKE).
    - `sid` object: Visitor sid(s), sg_wv_ prefix.
      - `eq` string: Website visitor sid (sg_wv_...).
      - `in` string[]
    - `site_sid` object: One site's visitors.
      - `eq` string: Website site sid (sg_si_...).
      - `in` string[]
    - `person_sid` object: One person's browsers; is_null false = bound visitors, is_null true = unbound.
      - `eq` string: Person sid (cr_pp_..., external: playtables).
      - `in` string[]
      - `is_null` boolean
    - `identified_email` object: is_null false = an email nobody has yet (with person_sid is_null true: the "Add to table" rows).
      - `eq` string
      - `is_null` boolean
    - `bound_by` object: The rung that bound the visitor; is_null true = unbound.
      - `eq` 'code' | 'identify' | 'fingerprint' | 'manual'
      - `in` string[]
      - `is_null` boolean
    - `is_ephemeral` object: eq false hides the one-day strangers (a hit without a visitor id).
      - `eq` boolean
    - `is_internal` object: The team's own traffic, by the newest session.
      - `eq` boolean
    - `network_type` object: business | isp | mobile | hosting | education | government | unknown; is_null true = no network resolved yet.
      - `eq` string
      - `in` string[]
      - `is_null` boolean
    - `geo_country` object: ISO 3166-1 alpha-2 of the newest session.
      - `eq` string
      - `in` string[]
    - `device` object: desktop | mobile | tablet | unknown.
      - `eq` 'desktop' | 'mobile' | 'tablet' | 'unknown'
      - `in` string[]
    - `sessions_count` object: gte 2 = the returning visitors.
      - `gte` integer
      - `lte` integer
      - `gt` integer
      - `lt` integer
    - `first_seen_at` object: ISO 8601 bounds on the first hit.
      - `gte` string
      - `lte` string
      - `gt` string
      - `lt` string
    - `last_seen_at` object: ISO 8601 bounds on the newest hit ("seen this week").
      - `gte` string
      - `lte` string
      - `gt` string
      - `lt` string
  - `include` string[]: Relations to attach to every item; omit for the bare rows.
  - `sort` object: One column and its direction; omit for the default order.
    - `field` 'last_seen_at' | 'first_seen_at' | 'sessions_count', required: The column to order by.
    - `direction` 'asc' | 'desc': asc or desc; default desc.
  - `page_size` integer: 0..500, default 50; 0 = counts only, 1 = the first row.
  - `cursor` string, nullable: pagination.next_cursor of the previous page.

## Response `200`

`search` success envelope.

- SearchWebsiteVisitorsResponse
  - `success` true, required
  - `operation` 'search', required
  - `items` object[], required
    - `item` object, required
      - `sid` string, required: Website visitor sid (sg_wv_...).
      - `team_sid` string, required: Owning team (ts_tm_..., external: gs.service.id).
      - `site_sid` string, required: The site the browser visited (sg_si_...): the same browser on two sites is two rows.
      - `visitor_id` string, required: The first-party visitor id the script stores (_gs_vid, a lowercase UUID v4), or eph_ plus 40 hex characters for a hit that carried none.
      - `is_ephemeral` boolean, required: The row belongs to a day, not to a browser (consent not given, storage blocked): a stranger the next day, never bound.
      - `person_sid` string, nullable, required: The person the visitor is bound to (cr_pp_..., playtables); null while unbound.
      - `identified_email` string, nullable, required: The email an identify or a form gave when no person matched: the "Add to table" rows of the feed. Null otherwise.
      - `bound_by` 'code' | 'identify' | 'fingerprint' | 'manual', nullable, required: Which rung bound the visitor: code (the person code in our link), identify (an email a person matched), fingerprint (the device), manual (a bind by hand). Null while unbound.
      - `bound_at` string, nullable, required: ISO 8601 UTC, when the bind was written; null while unbound.
      - `first_seen_at` string, required: ISO 8601 UTC, the receipt time of the first hit.
      - `last_seen_at` string, required: ISO 8601 UTC, the receipt time of the newest hit.
      - `sessions_count` integer, required: Sessions opened for this visitor: the returning visitors.
      - `browser` string, nullable, required: From the newest session; null when unknown.
      - `browser_version` string, nullable, required
      - `os` string, nullable, required
      - `os_version` string, nullable, required
      - `device` 'desktop' | 'mobile' | 'tablet' | 'unknown', required: desktop | mobile | tablet | unknown, from the newest session.
      - `screen` string, nullable, required
      - `language` string, nullable, required
      - `timezone` string, nullable, required
      - `geo_country` string, nullable, required: ISO 3166-1 alpha-2 of the newest session; null when the edge gave none.
      - `geo_region` string, nullable, required
      - `geo_city` string, nullable, required
      - `network_type` string, nullable, required: The newest resolved session's classification: business, isp, mobile, hosting, education, government or unknown; null until one resolved.
      - `network_name` string, nullable, required: The organization behind the newest resolved session's address; null until one resolved.
      - `network_domain` string, nullable, required: The company domain the data service derived; null when none.
      - `asn` integer, nullable, required
      - `as_name` string, nullable, required
      - `is_internal` boolean, required: The newest session was internal (the site's ignored_ips, or an identified email of the team's own domains).
      - `created_at` string, required: ISO 8601 UTC.
      - `updated_at` string, required: ISO 8601 UTC.
    - `included` object, required
      - `person` object: The bound person: { sid } today (cr_pp_...), the compact card is owed. Absent while person_sid is null.
        - `sid` string, required
      - `last_session` object: The visitor's newest session, the WebsiteSessionDomain verbatim (pages, key pages, events, the source, the network; // see ./website_sessions.md). Absent for a visitor with no session.
        - `sid` string, required
  - `pagination` object, required
    - `next_cursor` string, nullable, required
    - `has_more` boolean, required
    - `total_count` integer, nullable, required
  - `applied_filters` object, required
  - `includes` string[], required
  - `meta` object, required
    - `trace_id` string, required: UUID v7; same 128-bit value as the X-Trace-Id header.
    - `span_id` string, required: 16 hex chars, root span of this request.
    - `timestamp` string, required: ISO 8601 UTC (Y-m-dTH:i:sZ), response time.
    - `duration_ms` integer, required: Server-side wall clock.
    - `team_sid` string, nullable: The team this call ran in (the token team, or the team_sid override). Null when unauthenticated; absent from pre-2026-08-20 backends.
    - `actor_type` string, nullable: user | agent | api_key | system. Null when unauthenticated; absent from pre-2026-08-20 backends.
  - `counts` object
    - `total_count` integer, required: Visitors matching the filter (full filter scope, not the page).
    - `groups` object, required
      - `bound_by` union, required: Bound visitors per rung (code, identify, fingerprint, manual); the unbound are not a group; [] when none.
        - object
        - unknown[]
          - unknown
      - `network_type` union, required: Visitors per network classification of their newest resolved session.
        - object
        - unknown[]
          - unknown

## Other responses

- `4XX`: MCP error envelope. `error.code` is one of validation_failed, nothing_to_update, not_found, relation_not_found, invalid_transition, limit_exceeded, payment_required, duplicate_rejected, conflict, delete_blocked, unauthorized, forbidden, rate_limited, not_implemented.
- `5XX`: MCP error envelope with `error.code` internal_error or service_unavailable.

---

[API](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-signals.md) - [All operations](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-signals/llms.txt) - [OpenAPI document](https://skmtc.dev/getsales/apis/getsales-public-contract-gs-service-signals/revisions/310ca5d2219d?raw)
