CarsXE versions the API **in the URL path**, so a version you build against keeps behaving the way it did when you shipped. This page explains the versioning rules, what's new in v2, and exactly what to change when you migrate.

---

## How versioning works

There are three URL patterns, reflecting when each endpoint family was introduced:

| Pattern | Example | What lives there |
| --- | --- | --- |
| Root paths | `https://api.carsxe.com/specs` | The original v1 APIs (Specifications, Market Value, Plate Decoder, History, Images, and others). |
| `/v1/...` | `https://api.carsxe.com/v1/recalls-batch/submit` | Newer additions to the v1 generation (Recalls, Recalls Batch, Lien & Theft, VIN OCR, Year Make Model, International VIN Decoder). |
| `/v2/...` | `https://api.carsxe.com/v2/platedecoder` | The current generation. New capabilities ship here. |

A few things you can rely on:

- **Versions run side by side.** Shipping v2 does not turn off v1. There is currently no retirement date for any v1 endpoint; if one is ever scheduled, it will be announced well in advance.
- **Responses can gain fields.** Within a version we may add new fields to responses as data coverage improves. Build clients that ignore unknown fields — never fail on properties you don't recognize.
- **Breaking changes get a new path.** Changed parameter semantics or response shapes only ever ship under a new version prefix.

<Note>
  Migrating is opt-in and per-endpoint. You can move Plate Decoder to v2 today and leave Market Value on v1 — there is
  no flag day.
</Note>

---

## OpenAPI specification

A machine-readable OpenAPI 3.1 spec covering every public endpoint (v1 and v2) is available at:

<PortableCode value={{
 language: "bash",
 code: `curl -O https://api.carsxe.com/openapi.yaml`
}} />

Import it into Postman, Insomnia, or your code generator of choice to get typed clients and a request collection without writing anything by hand.

---

## What's new in v2

### Plate Decoder

[v1 docs](/docs/v1/plate-decoder) · [v2 docs](/docs/v2/plate-decoder)

| | v1 | v2 |
| --- | --- | --- |
| URL | `GET /platedecoder` | `GET /v2/platedecoder` |
| Required params | `plate`, `state` (US) | `plate`, `country` |
| Region params | `state`, `country` | `state` where applicable, plus country-specific params (e.g. `district` for Pakistan) |
| Coverage | Core countries | Expanded country coverage, documented per country with examples |
| VIN guarantee | — | `require_vin=true` (Spain) always returns a VIN. **Billed 2× and counts 2× toward usage.** |
| Response shape | Flat fields (`Description`, `CarMake`, `CarModel`, …) | Restructured per-country response — see the per-country examples in the v2 docs |

**Migration steps:**

1. Change the URL from `/platedecoder` to `/v2/platedecoder`.
2. Always pass `country` (ISO 3166-1 alpha-2, e.g. `US`, `ES`, `PK`). In v1, `country` was optional and defaulted to US behavior.
3. Update your response parsing — field names and nesting differ. Compare your target countries' example responses on the [v2 page](/docs/v2/plate-decoder) against what you parse today.
4. If you need a VIN for Spanish plates, add `require_vin=true` and account for the 2× billing.

### Market Value

[v1 docs](/docs/v1/market-value) · [v2 docs](/docs/v2/market-value)

| | v1 | v2 |
| --- | --- | --- |
| URL | `GET /marketvalue` | `GET /v2/marketvalue` |
| Required params | `vin` | `vin` |
| Optional params | `format` | `state`, `mileage`, `condition` (`excellent`, `clean`, `average`, `rough`) |
| Valuation model | Single retail/trade-in figures | Wholesale, retail, and trade-in breakdowns per condition tier, with base, mileage, regional, and add/deduct adjustments |
| Response shape | `retail`, `tradeIn`, `loanValue`, `msrp`, `auctionValues`, … | `whole_*`, `retail_*`, `trade_in_*` objects per condition — see [response attributes](/docs/v2/market-value) |

**Migration steps:**

1. Change the URL from `/marketvalue` to `/v2/marketvalue`.
2. Pass `mileage` and `condition` when you have them — v2 valuations adjust for both, which is the main accuracy win.
3. Map your fields: where v1 gave a single `retail` figure, v2 gives `retail_xclean` / `retail_clean` / `retail_avg` / `retail_rough` objects. Pick the tier matching the vehicle's condition (or use `condition` to let the API adjust).
4. Drop the `format` parameter — v2 responds in JSON only.

---

## Error-handling differences

v2 endpoints use precise HTTP status codes for every failure mode. A few **legacy v1 routes** return `500` for what are really validation errors (for example, missing `make`/`model` on the Images API), so v1 clients should check the `success` field in the body rather than relying on status codes alone. See the [error reference](/docs/errors) for the complete catalog.

Quota errors (`429`) behave identically on both versions and include the `usage` object — see [Rate Limits & Quotas](/docs/rate-limits).

---

## Migration checklist

- [ ] Swap URLs to the `/v2/` paths for the endpoints you're migrating
- [ ] Pass `country` on every Plate Decoder request
- [ ] Update response parsing against the v2 example responses for your markets
- [ ] Send `mileage` and `condition` to Market Value where available
- [ ] Make response parsing tolerant of unknown fields
- [ ] Re-run your integration tests against the v2 endpoints before switching production traffic
