Skip to main content
The most reliable Visor workflow is facet first, listing second.
  • Use /v1/facets to discover the values that exist in inventory.
  • Use /v1/listings to fetch matching vehicle rows.
  • Use /v1/vins/{vin}, /v1/listings/{listing_id}, and /v1/dealers/{dealer_id} when you already know the identifier.
This matters because vehicle data has real-world naming differences. A user may type f150, but the canonical model value in inventory may be F-150.

Discover before filtering

Start broad:
curl "https://api.visor.vin/v1/facets?facets=make&facet_value_limit=100" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Then use the exact returned make to discover models:
curl "https://api.visor.vin/v1/facets?facets=model&make=Ford&facet_value_limit=100" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Then query listings:
curl "https://api.visor.vin/v1/listings?make=Ford&model=F-150&state=TX&limit=10" \
  -H "Authorization: Bearer $VISOR_API_KEY"

Facet response shape

Facet totals are returned at data.total. Categorical buckets are keyed by facet name under data.facets:
{
  "data": {
    "total": 1234,
    "facets": {
      "model": [{ "value": "F-150", "count": 456 }]
    }
  }
}
Numeric range buckets are under data.range_facets, and numeric stats are under data.stats.

Filter syntax

Most filters are query parameters:
make=Toyota&model=Camry&state=CA&limit=10
Comma-separated filters match any of the values:
make=Toyota,Honda&state=CA,TX
Ranges use min_ and max_ fields:
min_price=20000&max_price=35000&min_year=2021
Some fields are closed vocabularies and reject unsupported values instead of returning a silent empty result. Examples include inventory_type, keywords, and dealer_type. Open categorical values such as trim, version, features, options_packages, assembly_country, and color values should be discovered with /v1/facets before filtering.
fields controls response projection. It does not filter rows. For example, use features=sunroof to filter and fields=default,features to include the feature list in each returned row.

Geography

Use state filters for broad searches:
state=CA
Use a ZIP-code radius for local searches:
postal_code=98101&radius=100&sort=distance
Use latitude and longitude when you already have coordinates:
latitude=47.6062&longitude=-122.3321&radius=100&sort=distance
Use bbox for map viewport searches.

Inventory status

Active inventory is the default:
inventory_status=active
Use sold inventory when you need market history:
inventory_status=sold&sold_within_days=30
Sold inventory starts only after a listing has been missing from the dealer’s inventory feed for at least three days and passed sold-quality checks. If it has been missing for less than three days, it is missing, not sold. Use snapshot_date=YYYY-MM-DD for historical active-inventory snapshots. snapshot_date cannot be combined with sold inventory filters.

VIN patterns

Use vin_pattern to match one or more positions in a 17-character US VIN:
vin_pattern=1HGCM826*
? matches exactly one VIN position. * may appear only at the end. Short masks are treated as prefixes, so vin_pattern=1HG is equivalent to 1HG*.

Dealer attribution

Public inventory is deduplicated by VIN and assigned to the dealer Visor believes owns the listing. Dealer-scoped inventory counts can differ from raw dealer feed counts when the same VIN appears in multiple feeds. Use /v1/dealers/{dealer_id}/listings for one dealer. Use /v1/listings?dealer_id=id1,id2 for an explicit multi-dealer listing search.

When a search returns no rows

  1. Remove the narrowest filter and retry.
  2. Run /v1/facets with the remaining filters to inspect available values.
  3. Check spelling and punctuation for make, model, trim, options, and features.
  4. Check whether you meant active inventory, sold inventory, or a historical snapshot.
  5. Check whether a geographic filter is too tight.
Successful zero-result searches are billable, so start with facets when you are unsure.