Skip to main content
The public beta API is available at:
https://api.visor.vin/v1
The generated OpenAPI document is public:
https://api.visor.vin/v1/openapi.json
All other /v1 endpoints require an API key:
export VISOR_API_KEY="vis_live_..."

curl "https://api.visor.vin/v1/listings?limit=2&make=toyota&state=CA" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Do not send API keys in query strings. Requests such as ?api_key=... are rejected.

MCP server for agents

Prefer to let an AI agent query Visor for you? The hosted MCP server lets Claude, ChatGPT, Codex, and other agents call the Public API in plain language — no HTTP requests to write:
https://mcp.visor.vin/mcp
Most apps connect in under a minute: you sign in to Visor and pick your account, with no key to copy. Clients that set request headers can authenticate with a dashboard token sent as Authorization: Bearer <VISOR_API_KEY> instead. See Connect to your AI agent for step-by-step setup for Claude, ChatGPT, Claude Code, Codex, OpenCode, and the OpenAI and Claude APIs, plus how the search and execute tools work.

Listings

curl "https://api.visor.vin/v1/listings?limit=10&offset=0&make=toyota&model=camry&year=2024&state=CA&sort=price" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Use /v1/listings with limit and offset for pagination. The deprecated /v1/listings/search compatibility path still accepts per_page and one-based page, maps them to limit and offset, and returns deprecation headers. Migrate old requests such as /v1/listings/search?per_page=100&page=2 to /v1/listings?limit=100&offset=100. Useful filters include vehicle fields (make, model, trim, year, body_type, transmission, drivetrain, fuel_type, powertrain_type, engine, colors, seating_capacity, cylinders, doors), dealer/inventory fields (dealer_id, state, dealer_type, availability_status, inventory_type), option and keyword fields (options_packages, features, keywords plus exclude_* variants), VIN masks (vin_pattern), ranges (min_price, max_price, min_mileage, max_mileage, min_msrp, max_msrp, min_days_on_market, max_days_on_market), and geography (postal_code or latitude/longitude with radius, plus bbox for map viewports). Closed-vocabulary filters reject unsupported values instead of returning a silent empty result. inventory_type accepts new, used, and certified; cpo is accepted as an alias for certified. keywords is for provenance/history tokens only: one_owner, clean_title, branded, and fleet. Discover open categorical values, such as trim, version, assembly_country, features, and options_packages, with /v1/facets before filtering. Use fields to return specific public listing summary fields. The default response fields do not change unless fields is supplied. id and vin are always returned, and default includes the normal listing summary fieldset plus selected extras. Unknown fields are rejected:
curl "https://api.visor.vin/v1/listings?dealer_id=dealer-1,dealer-2&fields=default,exterior_color,interior_color,msrp&limit=100" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Supported fields values are:
default, id, vin, year, make, model, trim, version,
body_type, drivetrain, fuel_type, powertrain_type, transmission, engine,
cylinders, doors, seating_capacity,
exterior_color, interior_color, base_exterior_color, base_interior_color,
msrp, price, miles, days_on_market, status, inventory_status, inventory_type,
stock_number, vdp_url, sold_date, dealer_id, dealer_name, dealer_type, city, state,
latitude, longitude, distance_miles, photo_urls, features, options_packages,
discount_from_msrp
Use vin_pattern to filter by one or more positions in a 17-character US VIN. Pass up to 10 comma-separated masks. VIN characters match themselves, ? matches exactly one VIN position, and * may appear only at the end. Short masks are treated as prefixes, so vin_pattern=1HG is equivalent to 1HG*. Examples: vin_pattern=1HG for WMI, vin_pattern=1HGCM826* for WMI plus VDS, and vin_pattern=???????J* for a specific eighth VIN character. VIN input is normalized to uppercase and may not contain I, O, or Q. Sort listing collections with field names. Prefix a field with - for descending order. The default is sort=days_on_market, which returns newest listings first. Supported fields are days_on_market, price, miles, msrp, discount, and distance. discount_from_msrp is a signed fraction calculated as (price - combined_msrp) / combined_msrp; it uses combined MSRP, not base MSRP. Negative values are below MSRP. sort=discount returns the best discount from MSRP first. sort=distance requires postal_code or latitude/longitude. inventory_status=active is the default. Use inventory_status=sold or sold_within_days=30 for sold inventory. A listing is sold only after it has been missing from the dealer’s inventory feed for at least three days and passed sold-quality checks; before then 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. 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 dealer feeds. price is the dealer’s main advertised listing price when Visor has a usable value. It may or may not include fees, add-ons, incentives, taxes, registration, or other adjustments depending on how the dealer presents pricing. More detailed pricing information is coming soon. Fetch one listing:
curl "https://api.visor.vin/v1/listings/listing-1?include=price_history,options" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Listing detail is listing-centered. Vehicle facts are nested under vehicle, with installed options under vehicle.build.options when requested. Include price history or decoded installed options directly on listing search pages:
curl "https://api.visor.vin/v1/listings?inventory_status=sold&postal_code=98101&radius=100&limit=100&fields=default,msrp,discount_from_msrp,features,options_packages,sold_date&include=price_history,options" \
  -H "Authorization: Bearer $VISOR_API_KEY"
include=price_history adds a price_history array to each listing in the returned page. include=options adds an options array of { code, name, msrp } objects decoded from the listing’s option/package codes. Listing collection pages remain capped at 100 records and are priced as enriched listing search requests when either expansion is requested.

Facets

curl "https://api.visor.vin/v1/facets?facets=make,model,price&make=toyota&state=CA" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Facets use the listing filter surface except app/control fields such as limit, offset, sort, fields, radius_id, columns, option_sort, and agnostic. The facets parameter is required; pass a comma-separated selection such as facets=make,model,price. Categorical facets return up to 20 values by default. Use facet_value_limit to request a different per-facet value limit, up to 100. Values above 100 are rejected with validation_error; they are not silently clamped. Numeric range facets use fixed buckets and are not affected by facet_value_limit. Supported facet names are make, model, inventory_type, year, trim, version, base_exterior_color, exterior_color, base_interior_color, interior_color, seating_capacity, doors, engine, state, drivetrain, assembly_location, assembly_country, transmission, fuel_type, body_type, cylinders, dealer_type, availability_status, options_packages, features, keywords, price, msrp, miles, and days_on_market. Facet totals are returned at data.total. Categorical buckets are keyed by facet name under data.facets, numeric buckets are under data.range_facets, and numeric stats are under data.stats. Facet buckets are sorted by count by default. Use metric to add one per-bucket aggregate while still returning count for sample-size context:
curl "https://api.visor.vin/v1/facets?facets=trim&make=ford&model=f-150&metric=price.p95&sort=-metric" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Supported metric measures are price, miles, msrp, days_on_market, and discount_from_msrp. Supported metric aggregates are mean, p5, p25, median, p75, and p95. Non-count metrics require exactly one categorical facet. Numeric range facets such as price, miles, msrp, and days_on_market keep their range-bucket behavior. Facet sort accepts -count, count, -metric, and metric. It defaults to -count, even when metric is supplied. Buckets with fewer than 10 matching listings return metric.value: null with null_reason: insufficient_sample.
{
  "data": {
    "total": 1234,
    "facets": {
      "make": [{ "value": "Toyota", "count": 1234 }],
      "model": [{ "value": "Camry", "count": 456 }]
    },
    "range_facets": {
      "price": {
        "buckets": [{ "min": 20000, "max": 25000, "count": 42 }],
        "interval": 5000,
        "min": 20000,
        "max": 25000
      }
    },
    "stats": {
      "price": { "min": 12000, "max": 65000, "count": 1200, "missing": 34, "mean": 31250, "median": 29995, "stddev": 8100 }
    }
  },
  "meta": { "facets": ["make", "model", "price"] }
}

VIN Lookup

curl "https://api.visor.vin/v1/vins/1HGCM82633A004352" \
  -H "Authorization: Bearer $VISOR_API_KEY"
VIN detail returns vehicle-level fields with the latest known listing nested under latest_listing. Dealer and photos are included on the latest listing by default. Request include=price_history for listing price changes and include=options for installed options in the build:
curl "https://api.visor.vin/v1/vins/1HGCM82633A004352?include=price_history,options" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Missing VINs return 404 not_found_error. With include=price_history, VIN price changes are returned at data.latest_listing.price_history. With include=options, installed options are returned at data.build.options.

Dealers

Search dealers:
curl "https://api.visor.vin/v1/dealers?state=CA,TX&type=franchise&make=honda&q=honda&limit=10" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Dealer search returns public dealer profiles with current active listing_count. Results include location summary fields, latitude/longitude, dealer type, normalized website, and represented franchise makes. Use dealer_id, state, country, type, make, and q to narrow results. The dealer_id filter accepts up to 100 comma-separated dealer IDs for direct profile lookup. The make filter matches represented franchise makes, not current inventory makes. listing_count uses VIN-level listing deduplication and ownership assignment, so it can be lower than a raw dealer feed count when duplicate VIN evidence is attributed to another dealer. Fetch one dealer:
curl "https://api.visor.vin/v1/dealers/00000000-0000-0000-0000-000000000000" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Dealer detail adds phone and structured address when available. Dealer profiles can exist with listing_count: 0. List inventory for one dealer:
curl "https://api.visor.vin/v1/dealers/00000000-0000-0000-0000-000000000000/listings?limit=10" \
  -H "Authorization: Bearer $VISOR_API_KEY"
Missing dealer detail records return 404 not_found_error. Use /v1/dealers/{dealer_id}/listings for single-dealer inventory. Use /v1/listings?dealer_id=id1,id2 for explicit multi-dealer listing search across up to 50 dealer IDs. Dealer inventory endpoints return listings attributed to that dealer after VIN-level deduplication. A VIN observed in a dealer’s feed may be omitted when Freeway believes another dealer owns that listing.

Pagination

Collection endpoints use limit and offset. The default limit is 50 and the maximum is 100. Values above 100 are rejected with validation_error; they are not silently clamped.
{
  "pagination": {
    "limit": 50,
    "offset": 0,
    "total": 1234,
    "next_offset": 50
  }
}
Use next_offset for the next page. It is null when the current page is the last page. total is counted for the same filters at request time and can change as inventory changes.

Usage And Billing Headers

Successful paid responses include:
X-Usage-Class: listing_search
X-Usage-Cost-Micros: 2000
X-Pricing-Version: 00000000-0000-0000-0000-000000000201
X-Usage-Cost-Micros is denominated in integer USD micros. 1000000 micros equals 1.00 USD. Postgres is the billing ledger authority; analytics exports may lag. Successful /v1 responses also include account-level rate-limit headers:
X-RateLimit-Tier: tier_1
X-RateLimit-Limit-10s: 20
X-RateLimit-Remaining-10s: 19
X-RateLimit-Limit-60s: 60
X-RateLimit-Remaining-60s: 59

Usage Summaries

Use /v1/usage to fetch daily billable usage buckets for the authenticated API account. This endpoint is not billable, requires the usage.read key scope, and is eventually consistent with the billing ledger.
curl "https://api.visor.vin/v1/usage?start_date=2026-05-01&end_date=2026-05-10" \
  -H "Authorization: Bearer $VISOR_API_KEY"
{
  "data": [
    { "date": "2026-05-01", "metering_class": "listing_search", "requests": 120, "charged_micros": 240000 }
  ],
  "totals": { "requests": 120, "charged_micros": 240000 },
  "meta": {
    "start_date": "2026-05-01",
    "end_date": "2026-05-10",
    "interval": "day",
    "currency": "USD",
    "source": "public_api_usage_events",
    "freshness": "Usage analytics are eventually consistent with the billing ledger."
  }
}
The default range is the last 30 UTC days ending today. The maximum range is 366 days. Use metering_class=listing_search,vehicle_detail to filter the summary to specific usage classes.

Pricing

Pre-production billing uses prepaid dollar-denominated API credits. Design partner credits are granted during onboarding; no monthly platform fee or public volume discounts apply during pre-production. High-volume usage and bulk data licensing are handled on custom terms. Prices are per successful request, not per returned row. Collection endpoints can return up to 100 records per request. Successful zero-result searches are billable. Detail 404 not_found_error responses are not billable.
API usage typeUsage classPrice/request
Listing searchlisting_search$0.002
Historical listing searchlisting_search_historical$0.004
Dealer-filtered listing searchlisting_search_dealer_filtered$0.003
Historical dealer-filtered listing searchlisting_search_dealer_filtered_historical$0.006
Dealer inventory searchdealer_inventory_search$0.003
Historical dealer inventory searchdealer_inventory_search_historical$0.006
Enriched listing searchlisting_search_enriched$0.040
Facet countsfacet_counts$0.003
Historical facet countsfacet_counts_historical$0.006
Vehicle detailvehicle_detail$0.003
Enriched vehicle detailvehicle_detail_enriched$0.008
Dealer lookupdealer_lookup$0.001
Historical pricing applies when a listing, dealer inventory, or facet request uses inventory_status=sold, sold_within_days, or snapshot_date. Dealer-filtered listing pricing applies when /v1/listings uses the dealer_id filter. Pricing remains per successful page, not per dealer ID. Enriched listing search pricing applies to listing collection requests with include=price_history and/or include=options. Pricing remains per successful page, not per listing, option, or price change event. Enriched vehicle detail pricing applies to GET /v1/vins/{vin} and GET /v1/listings/{listing_id} when include=price_history and/or include=options is requested.

Rate Limits

The beta public API enforces Usage Tier rate limits at the API account level. Limits are shared by all API keys on the account, so creating more keys does not increase throughput.
Usage TierRequests / 10 secondsRequests / 60 seconds
Tier 12060
Tier 2100100
Tier 3200600
Tier 45003000
All API accounts start in Tier 1. Automatic tier graduation uses verified paid API credits, days since first successful paid credit, and good standing. Admin or promotional beta credits do not count.
Target TierVerified paid API creditsDays since first successful paid creditReview
Tier 2$257Automatic when in good standing
Tier 3$15014Automatic when in good standing
Tier 4$50030Manual review required
Good standing means the API account is active, billing state is active or low balance, available balance is positive, and there has been no recent chargeback reversal. Your account dashboard is the source of truth for your current effective limits, including any account-specific override. Rate-limited responses use HTTP 429 and include Retry-After when available. See Usage Tiers for upgrade behavior and limit semantics.

Error Examples

400 validation_error:
{
  "error": {
    "type": "validation_error",
    "code": "unknown_query_parameter",
    "message": "Unknown query parameter: bad_filter."
  },
  "_tag": "PublicApiValidationError"
}
401 authentication_error:
{
  "error": {
    "type": "authentication_error",
    "code": "missing_api_key",
    "message": "Missing API key."
  },
  "_tag": "PublicApiAuthenticationError"
}
402 billing_error:
{
  "error": {
    "type": "billing_error",
    "code": "billing_state_blocked",
    "message": "Your API account cannot make requests in its current billing state."
  },
  "_tag": "PublicApiBillingError"
}
404 not_found_error:
{
  "error": {
    "type": "not_found_error",
    "code": "not_found",
    "message": "VIN was not found."
  },
  "_tag": "PublicApiNotFoundError"
}
429 rate_limit_error:
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded."
  },
  "_tag": "PublicApiRateLimitError"
}
503 platform_error:
{
  "error": {
    "type": "platform_error",
    "code": "metering_unavailable",
    "message": "Usage metering is unavailable for this request."
  },
  "_tag": "PublicApiPlatformError"
}