# Methodology: Emergency veterinary hospitals: what 25 US vet chains list

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: Listings across all 27 chains and across the 25 that are not host-store chains

- **Asset:** Corporate-chain veterinary locations by the format their listed name states (derived from the chain-location crawl) (csv, first-party data); owner Vettica.
- **Collection method:** Each location listed by the 27 chains in Vettica's chain crawl of 27-28 September 2026 (most from public location finders, four from careers or office listings), after the chain hub's 71 exclusions, is classified by its own listed name - emergency ('emergenc', '24-hour', '24/7', 'critical care', 'ER'), then urgent care ('urgent'), then specialty or referral ('speciali', 'specialt', 'referral'), else not stated.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 7894 input; 7894 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 2 of 2 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", "chain"
  FROM "asset"
),
rows AS (
  SELECT *,
    CASE "chain" WHEN ? THEN ? WHEN ? THEN ? ELSE ? END AS "scope"
  FROM filtered
)
SELECT "scope", COUNT(*) AS "locations", COUNT(DISTINCT NULLIF(TRIM("brand"), '')) AS "chains", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "scope"
HAVING COUNT(*) >= ?
```
Parameters: `["tractor_supply_petvet", "host-store chains (Tractor Supply PetVet Clinics and VIP Petcare / PetIQ)", "vip_petcare", "host-store chains (Tractor Supply PetVet Clinics and VIP Petcare / PetIQ)", "the 25 other chains", 5]`

### Methodology: Crawl rows the chain profiles leave out, by reason

- **Asset:** Rows the Vettica chain hub excludes from the 27-chain crawl (published excluded-rows.csv) (csv, first-party data); owner Vettica.
- **Collection method:** Rows of the 27-chain crawl that the chain profiles leave out, one row each with the reason.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 71 input; 71 after filters and de-duplication.
- **Threshold:** at least 1 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 "class"
  FROM "asset"
),
rows AS (
  SELECT *,
    CASE "class" WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? ELSE ? END AS "reason"
  FROM filtered
)
SELECT "reason", COUNT(*) AS "rows", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "reason"
HAVING COUNT(*) >= ?
```
Parameters: `["closed", "National Veterinary Associates listings marked CLOSED", "office", "office or remote-role rows", "duplicate", "repeated GoodVets rows", "other", 1]`

### Methodology: Corporate-chain clinic and hospital locations by the format their listed name states

- **Asset:** Corporate-chain veterinary locations by the format their listed name states (derived from the chain-location crawl) (csv, first-party data); owner Vettica.
- **Collection method:** Each location listed by the 27 chains in Vettica's chain crawl of 27-28 September 2026 (most from public location finders, four from careers or office listings), after the chain hub's 71 exclusions, is classified by its own listed name - emergency ('emergenc', '24-hour', '24/7', 'critical care', 'ER'), then urgent care ('urgent'), then specialty or referral ('speciali', 'specialt', 'referral'), else not stated.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 7894 input; 5069 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 4 of 4 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", "named_format", "state"
  FROM "asset"
  WHERE "chain" NOT IN (?, ?)
),
rows AS (
  SELECT *,
    CASE "named_format" WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? WHEN ? THEN ? ELSE ? END AS "format_phrase"
  FROM filtered
)
SELECT "named_format", "format_phrase", COUNT(*) AS "locations", COUNT(DISTINCT NULLIF(TRIM("brand"), '')) AS "chains", COUNT(DISTINCT NULLIF(TRIM("state"), '')) AS "states", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "named_format", "format_phrase"
HAVING COUNT(*) >= ?
```
Parameters: `["tractor_supply_petvet", "vip_petcare", "emergency", "states emergency", "urgent care", "states urgent care", "specialty or referral", "states specialty or referral", "not stated", "states no format", "states another format", 5]`

### Methodology: Emergency-named listings and the distinct addresses they cover

- **Asset:** Corporate-chain veterinary locations by the format their listed name states (derived from the chain-location crawl) (csv, first-party data); owner Vettica.
- **Collection method:** Each location listed by the 27 chains in Vettica's chain crawl of 27-28 September 2026 (most from public location finders, four from careers or office listings), after the chain hub's 71 exclusions, is classified by its own listed name - emergency ('emergenc', '24-hour', '24/7', 'critical care', 'ER'), then urgent care ('urgent'), then specialty or referral ('speciali', 'specialt', 'referral'), else not stated.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 7894 input; 122 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 "address_key", "brand", "named_format", "state"
  FROM "asset"
  WHERE "named_format" = ?
    AND "chain" NOT IN (?, ?)
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "named_format", COUNT(*) AS "locations", COUNT(DISTINCT NULLIF(TRIM("address_key"), '')) AS "addresses", COUNT(DISTINCT NULLIF(TRIM("brand"), '')) AS "chains", COUNT(DISTINCT NULLIF(TRIM("state"), '')) AS "states", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "named_format"
HAVING COUNT(*) >= ?
```
Parameters: `["emergency", "tractor_supply_petvet", "vip_petcare", 5]`

### Methodology: Locations named as emergency hospitals, by chain

- **Asset:** Corporate-chain veterinary locations by the format their listed name states (derived from the chain-location crawl) (csv, first-party data); owner Vettica.
- **Collection method:** Each location listed by the 27 chains in Vettica's chain crawl of 27-28 September 2026 (most from public location finders, four from careers or office listings), after the chain hub's 71 exclusions, is classified by its own listed name - emergency ('emergenc', '24-hour', '24/7', 'critical care', 'ER'), then urgent care ('urgent'), then specialty or referral ('speciali', 'specialt', 'referral'), else not stated.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 7894 input; 122 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 5 of 10 groups published (5 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", "state"
  FROM "asset"
  WHERE "named_format" = ?
    AND "chain" NOT IN (?, ?)
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(*) AS "locations", COUNT(DISTINCT NULLIF(TRIM("state"), '')) AS "states", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "brand"
HAVING COUNT(*) >= ?
```
Parameters: `["emergency", "tractor_supply_petvet", "vip_petcare", 5]`

Denominator `chain_locations` from `vettica-chain-named-formats` (every location the chain lists (27-28 September 2026), after the chain hub's 71 exclusions): published counts {"Tractor Supply PetVet Clinics": 1703, "VIP Petcare / PetIQ": 1122, "National Veterinary Associates": 903, "Mission Veterinary Partners / Mission Pet Health": 765, "Vetcor": 416, "PetVet Care Centers": 384, "Western Veterinary Partners": 379, "Heartland Veterinary Partners": 297, "Vetco Total Care": 276, "Blue River PetCare": 212, "AmeriVet Veterinary Partners": 211, "CareVet": 205, "Veterinary Practice Partners": 199, "Ethos Veterinary Health": 130, "United Veterinary Care": 102, "GoodVets": 91, "American Veterinary Group": 90, "Innovetive Petcare": 82, "Suveto": 79, "VetEvolve": 53, "PetSmart Veterinary Services": 52, "Chewy Vet Care": 32, "Destination Pet": 29, "Lakefield Veterinary Group": 29, "PetWellClinic": 29, "Altano Group": 19, "PetMedic Urgent Care": 5}; threshold 5.

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

### Methodology: Ethos Veterinary Health emergency-named listings at an address another chain also lists, by that chain

- **Asset:** Corporate-chain veterinary locations by the format their listed name states (derived from the chain-location crawl) (csv, first-party data); owner Vettica.
- **Collection method:** Each location listed by the 27 chains in Vettica's chain crawl of 27-28 September 2026 (most from public location finders, four from careers or office listings), after the chain hub's 71 exclusions, is classified by its own listed name - emergency ('emergenc', '24-hour', '24/7', 'critical care', 'ER'), then urgent care ('urgent'), then specialty or referral ('speciali', 'specialt', 'referral'), else not stated.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 7894 input; 22 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", "other_chains"
  FROM "asset"
  WHERE "named_format" = ?
    AND "cross_chain" = ?
    AND "brand" = ?
    AND "chain" NOT IN (?, ?)
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", "other_chains", COUNT(*) AS "locations", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "brand", "other_chains"
HAVING COUNT(*) >= ?
```
Parameters: `["emergency", "1", "Ethos Veterinary Health", "tractor_supply_petvet", "vip_petcare", 5]`

### Methodology: Locations named as emergency hospitals, by state

- **Asset:** Corporate-chain veterinary locations by the format their listed name states (derived from the chain-location crawl) (csv, first-party data); owner Vettica.
- **Collection method:** Each location listed by the 27 chains in Vettica's chain crawl of 27-28 September 2026 (most from public location finders, four from careers or office listings), after the chain hub's 71 exclusions, is classified by its own listed name - emergency ('emergenc', '24-hour', '24/7', 'critical care', 'ER'), then urgent care ('urgent'), then specialty or referral ('speciali', 'specialt', 'referral'), else not stated.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 7894 input; 122 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 12 of 32 groups published (20 below threshold, 0 secondary).
- **Engine:** pandas.read_csv(dtype=str, keep_default_na=False) -> in-memory SQLite table "asset"

```sql
WITH filtered AS (
  SELECT "address_key", "brand", "state"
  FROM "asset"
  WHERE "named_format" = ?
    AND "chain" NOT IN (?, ?)
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "state", COUNT(*) AS "locations", COUNT(DISTINCT NULLIF(TRIM("brand"), '')) AS "chains", COUNT(DISTINCT NULLIF(TRIM("address_key"), '')) AS "addresses", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "state"
HAVING COUNT(*) >= ?
```
Parameters: `["emergency", "tractor_supply_petvet", "vip_petcare", 5]`

### Methodology: States with 1 to 4 emergency-named listings in the 25 chains

- **Asset:** Emergency-named listings of the 25 non-host-store chains per jurisdiction (50 states + DC), bucketed (csv, first-party data); owner Vettica.
- **Collection method:** Counts of emergency-named listings per jurisdiction from the 25 non-host-store chains, bucketed.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 51 input; 20 after filters and de-duplication.
- **Threshold:** at least 1 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 "bucket"
  FROM "asset"
  WHERE "bucket" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "bucket", COUNT(*) AS "jurisdictions", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "bucket"
HAVING COUNT(*) >= ?
```
Parameters: `["1 to 4", 1]`

### Methodology: Jurisdictions with no emergency-named listing in the 25 chains

- **Asset:** Emergency-named listings of the 25 non-host-store chains per jurisdiction (50 states + DC), bucketed (csv, first-party data); owner Vettica.
- **Collection method:** Counts of emergency-named listings per jurisdiction from the 25 non-host-store chains, bucketed.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 51 input; 19 after filters and de-duplication.
- **Threshold:** at least 1 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 "bucket"
  FROM "asset"
  WHERE "bucket" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "bucket", COUNT(*) AS "jurisdictions", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "bucket"
HAVING COUNT(*) >= ?
```
Parameters: `["none", 1]`

### Methodology: States (DC left out) with no emergency-named listing in the 25 chains

- **Asset:** Emergency-named listings of the 25 non-host-store chains per jurisdiction (50 states + DC), bucketed (csv, first-party data); owner Vettica.
- **Collection method:** Counts of emergency-named listings per jurisdiction from the 25 non-host-store chains, bucketed.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 51 input; 18 after filters and de-duplication.
- **Threshold:** at least 1 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 "bucket"
  FROM "asset"
  WHERE "bucket" = ?
    AND "is_state" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "bucket", COUNT(*) AS "jurisdictions", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "bucket"
HAVING COUNT(*) >= ?
```
Parameters: `["none", "1", 1]`

### Methodology: Locations named as urgent care, by chain

- **Asset:** Corporate-chain veterinary locations by the format their listed name states (derived from the chain-location crawl) (csv, first-party data); owner Vettica.
- **Collection method:** Each location listed by the 27 chains in Vettica's chain crawl of 27-28 September 2026 (most from public location finders, four from careers or office listings), after the chain hub's 71 exclusions, is classified by its own listed name - emergency ('emergenc', '24-hour', '24/7', 'critical care', 'ER'), then urgent care ('urgent'), then specialty or referral ('speciali', 'specialt', 'referral'), else not stated.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 7894 input; 46 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 3 of 10 groups published (7 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"
  FROM "asset"
  WHERE "named_format" = ?
    AND "chain" NOT IN (?, ?)
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(*) AS "locations", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "brand"
HAVING COUNT(*) >= ?
```
Parameters: `["urgent care", "tractor_supply_petvet", "vip_petcare", 5]`

### Methodology: Locations named as specialty or referral hospitals, by chain

- **Asset:** Corporate-chain veterinary locations by the format their listed name states (derived from the chain-location crawl) (csv, first-party data); owner Vettica.
- **Collection method:** Each location listed by the 27 chains in Vettica's chain crawl of 27-28 September 2026 (most from public location finders, four from careers or office listings), after the chain hub's 71 exclusions, is classified by its own listed name - emergency ('emergenc', '24-hour', '24/7', 'critical care', 'ER'), then urgent care ('urgent'), then specialty or referral ('speciali', 'specialt', 'referral'), else not stated.
- **As of:** 2026-09-28; cadence quarterly; contribution type first-party data.
- **Rows:** 7894 input; 59 after filters and de-duplication.
- **Threshold:** at least 5 source rows per published group; 5 of 9 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"
  FROM "asset"
  WHERE "named_format" = ?
    AND "chain" NOT IN (?, ?)
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "brand", COUNT(*) AS "locations", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "brand"
HAVING COUNT(*) >= ?
```
Parameters: `["specialty or referral", "tractor_supply_petvet", "vip_petcare", 5]`

### Methodology: Emergency clinics in South Carolina's board roster

- **Asset:** Facility records in 14 state veterinary board rosters (status and field flags only) (csv, public source); owner Vettica.
- **Collection method:** Downloaded or queried the public facility roster of 14 state veterinary boards between 25 and 30 September 2026; raw copies retained, with sha256 recorded for 10 of the 14 files; one row per facility record.
- **As of:** 2026-09-29; cadence quarterly; contribution type first-party data.
- **Rows:** 16026 input; 12 after filters and de-duplication.
- **Threshold:** at least 1 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 "board", "registry_state"
  FROM "asset"
  WHERE "registry_state" = ?
),
rows AS (
  SELECT *
  FROM filtered
)
SELECT "registry_state", "board", COUNT(*) AS "records", COUNT(*) AS "_n_rows"
FROM rows
GROUP BY "registry_state", "board"
HAVING COUNT(*) >= ?
```
Parameters: `["SC", 1]`

