# Methodology: Veterinary PIMS and other software on chain clinic websites

Every figure on the page is an aggregate. No location-level row, address or phone number is published. Each block below gives the source, the exact query, the row counts and the minimum group size applied.

### Methodology: Clinic websites scanned, by outcome

- **Asset:** Corporate-chain veterinary clinic websites scanned for software (one row per host) (csv, first-party data); owner Vettica.
- **Collection method:** Every clinic website host listed in the 27-chain location crawl, scanned on 3 October 2026 (home page plus one further linked page, the likeliest booking or portal link found (on the clinic's site or a vendor's; on 1,052 websites a Facebook page), where robots rules allowed; no JavaScript run).
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 1961 input; 1961 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 3 of 3 groups published (0 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "host", "status"
  FROM "asset"
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "status", COUNT(*) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "status"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `[5]`

### Methodology: Chains with clinic websites in the scan

- **Asset:** Corporate-chain veterinary clinic websites scanned for software (one row per host) (csv, first-party data); owner Vettica.
- **Collection method:** Every clinic website host listed in the 27-chain location crawl, scanned on 3 October 2026 (home page plus one further linked page, the likeliest booking or portal link found (on the clinic's site or a vendor's; on 1,052 websites a Facebook page), where robots rules allowed; no JavaScript run).
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 1961 input; 1961 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 1 of 1 groups published (0 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "brand", "host", "status"
  FROM "asset"
),
rows AS (
  SELECT *,
    CASE "status" WHEN ? THEN ? ELSE ? END AS "scope"
  FROM filtered
)
SELECT "scope", COUNT(DISTINCT NULLIF(TRIM("brand"), '')) AS "chains", COUNT(*) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "scope"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["_none_", "all scanned clinic websites", "all scanned clinic websites", 5]`

### Methodology: Second page on fetched clinic websites, by outcome

- **Asset:** Corporate-chain veterinary clinic websites scanned for software (one row per host) (csv, first-party data); owner Vettica.
- **Collection method:** Every clinic website host listed in the 27-chain location crawl, scanned on 3 October 2026 (home page plus one further linked page, the likeliest booking or portal link found (on the clinic's site or a vendor's; on 1,052 websites a Facebook page), where robots rules allowed; no JavaScript run).
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 1961 input; 1755 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 5 of 5 groups published (0 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "host", "secondary_status"
  FROM "asset"
  WHERE "status" = ?
),
rows AS (
  SELECT *,
    CASE "secondary_status" WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? ELSE ? END AS "second_page"
  FROM filtered
)
SELECT "second_page", COUNT(*) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "second_page"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["fetched", "fetched", "fetched on the clinic's own site", "fetched_third_party", "fetched on a vendor's host", "blocked", "blocked by robots rules", "none_selected", "no second-page link selected", "error", "errored or was the home page", "same_page", "errored or was the home page", "other", 5]`

### Methodology: Fetched clinic websites whose second page was fetched (clinic site or vendor host)

- **Asset:** Corporate-chain veterinary clinic websites scanned for software (one row per host) (csv, first-party data); owner Vettica.
- **Collection method:** Every clinic website host listed in the 27-chain location crawl, scanned on 3 October 2026 (home page plus one further linked page, the likeliest booking or portal link found (on the clinic's site or a vendor's; on 1,052 websites a Facebook page), where robots rules allowed; no JavaScript run).
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 1961 input; 321 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 1 of 1 groups published (0 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "host", "status"
  FROM "asset"
  WHERE "status" = ?
    AND "secondary_status" IN (?, ?)
),
rows AS (
  SELECT *,
    CASE "status" WHEN ? THEN ? ELSE ? END AS "second_page"
  FROM filtered
)
SELECT "second_page", COUNT(*) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "second_page"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["fetched", "fetched", "fetched_third_party", "fetched", "fetched (on the clinic's site or a vendor's host)", "fetched (on the clinic's site or a vendor's host)", 5]`

### Methodology: Fetched clinic websites whose second-page link was the clinic's Facebook page

- **Asset:** Corporate-chain veterinary clinic websites scanned for software (one row per host) (csv, first-party data); owner Vettica.
- **Collection method:** Every clinic website host listed in the 27-chain location crawl, scanned on 3 October 2026 (home page plus one further linked page, the likeliest booking or portal link found (on the clinic's site or a vendor's; on 1,052 websites a Facebook page), where robots rules allowed; no JavaScript run).
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 1961 input; 1052 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 1 of 1 groups published (0 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "host", "status"
  FROM "asset"
  WHERE "status" = ?
    AND "secondary_status" = ?
    AND "secondary_host" IN (?, ?, ?)
),
rows AS (
  SELECT *,
    CASE "status" WHEN ? THEN ? ELSE ? END AS "second_page"
  FROM filtered
)
SELECT "second_page", COUNT(*) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "second_page"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["fetched", "blocked", "facebook.com", "www.facebook.com", "m.facebook.com", "fetched", "the clinic's Facebook page (blocked by robots rules)", "the clinic's Facebook page (blocked by robots rules)", 5]`

### Methodology: Vendors detected on fetched clinic websites, by category

- **Asset:** Software detected on corporate-chain veterinary clinic websites (one row per host and detection) (csv, first-party data); owner Vettica.
- **Collection method:** Published vendor signatures (scripts, iframes, vendor links, structured data) matched on fetched pages; a detection is evidence the vendor is present, a non-detection is unknown.
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 5638 input; 2721 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 14 of 22 groups published (8 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "brand", "category", "host", "technology", "unit"
  FROM "asset"
  WHERE "category" IN (?, ?, ?, ?, ?, ?, ?)
),
rows AS (
  SELECT *,
    CASE "category" WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? ELSE ? END AS "category_label"
  FROM filtered
)
SELECT "category_label", "technology", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "hosts", COUNT(DISTINCT NULLIF(TRIM("unit"), '')) AS "units", COUNT(DISTINCT NULLIF(TRIM("brand"), '')) AS "chains", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "category_label", "technology"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["online_booking", "online_pharmacy", "payments", "client_portal_app", "pims_portal", "reviews_platform", "site_platform", "online_booking", "online booking", "online_pharmacy", "online pharmacy", "payments", "payment and financing", "client_portal_app", "client app", "pims_portal", "practice-software client portal", "reviews_platform", "reviews platform", "site_platform", "website platform", "other", 5]`

### Methodology: Chains and fetched websites with any detection, by category (before the five-website threshold)

- **Asset:** Software detected on corporate-chain veterinary clinic websites (one row per host and detection) (csv, first-party data); owner Vettica.
- **Collection method:** Published vendor signatures (scripts, iframes, vendor links, structured data) matched on fetched pages; a detection is evidence the vendor is present, a non-detection is unknown.
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 5638 input; 2721 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 7 of 7 groups published (0 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "brand", "category", "host"
  FROM "asset"
  WHERE "category" IN (?, ?, ?, ?, ?, ?, ?)
),
rows AS (
  SELECT *,
    CASE "category" WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? ELSE ? END AS "category_label",
    CASE "category" WHEN ? THEN ? ELSE ? END AS "scope"
  FROM filtered
)
SELECT "category_label", "scope", COUNT(DISTINCT NULLIF(TRIM("brand"), '')) AS "chains", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "category_label", "scope"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["online_booking", "online_pharmacy", "payments", "client_portal_app", "pims_portal", "reviews_platform", "site_platform", "online_booking", "online booking", "online_pharmacy", "online pharmacy", "payments", "payment and financing", "client_portal_app", "client app", "pims_portal", "practice-software client portal", "reviews_platform", "reviews platform", "site_platform", "website platform", "other", "_none_", "fetched", "fetched", 5]`

Denominator `fetched_hosts` from `vettica-techscan-hosts` (clinic websites the scan fetched (3 October 2026)): published counts {"fetched": 1755}; threshold 5.

```sql
WITH filtered AS (
  SELECT "status"
  FROM "asset"
  WHERE "status" = ?
),
rows AS (
  SELECT *,
    CASE "status" WHEN ? THEN ? ELSE ? END AS "scope"
  FROM filtered
)
SELECT "scope", COUNT(*) AS "count", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "scope"
HAVING COUNT(*) >= ?
```

### Methodology: Fetched clinic websites with an ezyVet client portal detected

- **Asset:** Software detected on corporate-chain veterinary clinic websites (one row per host and detection) (csv, first-party data); owner Vettica.
- **Collection method:** Published vendor signatures (scripts, iframes, vendor links, structured data) matched on fetched pages; a detection is evidence the vendor is present, a non-detection is unknown.
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 5638 input; 29 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 1 of 1 groups published (0 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "brand", "category", "host", "technology"
  FROM "asset"
  WHERE "category" = ?
    AND "technology" = ?
),
rows AS (
  SELECT *,
    CASE "category" WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? ELSE ? END AS "category_label",
    CASE "category" WHEN ? THEN ? ELSE ? END AS "scope"
  FROM filtered
)
SELECT "technology", "scope", COUNT(DISTINCT NULLIF(TRIM("brand"), '')) AS "chains", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "technology", "scope"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["pims_portal", "ezyVet", "online_booking", "online booking", "online_pharmacy", "online pharmacy", "payments", "payment and financing", "client_portal_app", "client app", "pims_portal", "practice-software client portal", "reviews_platform", "reviews platform", "site_platform", "website platform", "other", "_none_", "fetched", "fetched", 5]`

Denominator `fetched_hosts` from `vettica-techscan-hosts` (clinic websites the scan fetched (3 October 2026)): published counts {"fetched": 1755}; threshold 5.

```sql
WITH filtered AS (
  SELECT "status"
  FROM "asset"
  WHERE "status" = ?
),
rows AS (
  SELECT *,
    CASE "status" WHEN ? THEN ? ELSE ? END AS "scope"
  FROM filtered
)
SELECT "scope", COUNT(*) AS "count", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "scope"
HAVING COUNT(*) >= ?
```

### Methodology: Fetched clinic websites with an online-booking vendor detected, by chain

- **Asset:** Software detected on corporate-chain veterinary clinic websites (one row per host and detection) (csv, first-party data); owner Vettica.
- **Collection method:** Published vendor signatures (scripts, iframes, vendor links, structured data) matched on fetched pages; a detection is evidence the vendor is present, a non-detection is unknown.
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 5638 input; 97 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 7 of 11 groups published (4 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "brand", "host"
  FROM "asset"
  WHERE "category" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "brand"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["online_booking", 5]`

Denominator `fetched_hosts` from `vettica-techscan-hosts` (the chain's clinic websites the scan fetched (3 October 2026)): published counts {"National Veterinary Associates": 881, "AmeriVet Veterinary Partners": 198, "Veterinary Practice Partners": 170, "American Veterinary Group": 88, "Innovetive Petcare": 79, "Ethos Veterinary Health": 64, "Suveto": 60, "United Veterinary Care": 53, "VetEvolve": 50, "Blue River PetCare": 30, "Lakefield Veterinary Group": 29, "Destination Pet": 28, "Altano Group": 14}; threshold 5.

```sql
WITH filtered AS (
  SELECT "brand"
  FROM "asset"
  WHERE "status" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(*) AS "count", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "brand"
HAVING COUNT(*) >= ?
```

### Methodology: Fetched clinic websites with an online-pharmacy vendor detected, by chain

- **Asset:** Software detected on corporate-chain veterinary clinic websites (one row per host and detection) (csv, first-party data); owner Vettica.
- **Collection method:** Published vendor signatures (scripts, iframes, vendor links, structured data) matched on fetched pages; a detection is evidence the vendor is present, a non-detection is unknown.
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 5638 input; 190 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 7 of 11 groups published (4 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "brand", "host"
  FROM "asset"
  WHERE "category" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "brand"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["online_pharmacy", 5]`

Denominator `fetched_hosts` from `vettica-techscan-hosts` (the chain's clinic websites the scan fetched (3 October 2026)): published counts {"National Veterinary Associates": 881, "AmeriVet Veterinary Partners": 198, "Veterinary Practice Partners": 170, "American Veterinary Group": 88, "Innovetive Petcare": 79, "Ethos Veterinary Health": 64, "Suveto": 60, "United Veterinary Care": 53, "VetEvolve": 50, "Blue River PetCare": 30, "Lakefield Veterinary Group": 29, "Destination Pet": 28, "Altano Group": 14}; threshold 5.

```sql
WITH filtered AS (
  SELECT "brand"
  FROM "asset"
  WHERE "status" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(*) AS "count", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "brand"
HAVING COUNT(*) >= ?
```

### Methodology: Fetched clinic websites with a payment or financing vendor detected, by chain

- **Asset:** Software detected on corporate-chain veterinary clinic websites (one row per host and detection) (csv, first-party data); owner Vettica.
- **Collection method:** Published vendor signatures (scripts, iframes, vendor links, structured data) matched on fetched pages; a detection is evidence the vendor is present, a non-detection is unknown.
- **As of:** 2026-10-03; cadence quarterly; contribution type first-party data.
- **Rows:** 5638 input; 863 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 10 of 13 groups published (3 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "brand", "host"
  FROM "asset"
  WHERE "category" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "hosts", COUNT(*) AS "_n_rows", COUNT(DISTINCT NULLIF(TRIM("host"), '')) AS "_n_units"
FROM rows
GROUP BY "brand"
HAVING COUNT(DISTINCT NULLIF(TRIM("host"), '')) >= ?
```
Parameters: `["payments", 5]`

Denominator `fetched_hosts` from `vettica-techscan-hosts` (the chain's clinic websites the scan fetched (3 October 2026)): published counts {"National Veterinary Associates": 881, "AmeriVet Veterinary Partners": 198, "Veterinary Practice Partners": 170, "American Veterinary Group": 88, "Innovetive Petcare": 79, "Ethos Veterinary Health": 64, "Suveto": 60, "United Veterinary Care": 53, "VetEvolve": 50, "Blue River PetCare": 30, "Lakefield Veterinary Group": 29, "Destination Pet": 28, "Altano Group": 14}; threshold 5.

```sql
WITH filtered AS (
  SELECT "brand"
  FROM "asset"
  WHERE "status" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(*) AS "count", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "brand"
HAVING COUNT(*) >= ?
```

