Vehicle history
Every event recorded for a VIN, oldest first: the first listing, each price change, status changes and removal, with the dealer site it was seen on. meta.sources reports the status of each history source. A page with a failing source has meta.partial: true and no next_cursor: retry it.
GEThttps://api.motorsdynamic.com/api/v2/vehicles/{vin}/timeline
Plan and price
| Plan | Access | Price |
|---|---|---|
| Free | Yes | included in the 1,000 monthly calls |
| Growth | Yes | $0.0015 per call |
| Scale | Yes | $0.0009 per call |
| Enterprise | Yes | contract |
A call is billed when it answers 2xx. See pricing.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
vin | path | string | yes | |
sources | query | string | no | Comma-separated subset of v2, md_legacy, foureyes. |
since | query | string (date-time) | no | |
until | query | string (date-time) | no | |
limit | query | integer | no | |
cursor | query | string | no | page.next_cursor from the previous page, unchanged. |
Example request
curl "https://api.motorsdynamic.com/api/v2/vehicles/1FTFW1E50NFA00001/timeline?sources=v2%2Cmd_legacy&limit=2" \
-H "x-api-key: $MD_API_KEY"const res = await fetch("https://api.motorsdynamic.com/api/v2/vehicles/1FTFW1E50NFA00001/timeline?sources=v2%2Cmd_legacy&limit=2", {
headers: { "x-api-key": process.env.MD_API_KEY },
});
if (!res.ok) throw new Error(`${res.status} ${(await res.json()).error}`);
const data = await res.json();
console.log(data);import os, requests
res = requests.get(
"https://api.motorsdynamic.com/api/v2/vehicles/1FTFW1E50NFA00001/timeline?sources=v2%2Cmd_legacy&limit=2",
headers={"x-api-key": os.environ["MD_API_KEY"]},
timeout=10,
)
res.raise_for_status()
print(res.json())Example response
{
"data": [
{
"event_id": "10000000-0000-5000-8000-000000000001",
"domain_id": 101,
"vin": "1FTFW1E50NFA00001",
"source_key": "1FTFW1E50NFA00001",
"event_type": "listed",
"observed_at": "2026-06-01T12:00:00.500000Z",
"prev": null,
"next": {
"price": 45000
},
"entity_version": null,
"episode": null,
"freshness": {
"observed_at": "2026-06-01T12:00:00.500000Z"
},
"provenance": {
"source": "md_legacy",
"adapter_version": "md_legacy-2026-09-24",
"run_id": null,
"quality": "ok"
}
},
{
"event_id": "00000000-0000-4000-8000-000000000002",
"domain_id": 101,
"vin": "1FTFW1E50NFA00001",
"source_key": "1FTFW1E50NFA00001",
"event_type": "price_changed",
"observed_at": "2026-09-21T00:00:00.000000Z",
"prev": {
"price": 41000
},
"next": {
"price": 40000
},
"entity_version": 3,
"episode": 1,
"freshness": {
"observed_at": "2026-09-21T00:00:00.000000Z"
},
"provenance": {
"source": "v2",
"adapter_version": "dealer-inspire@3",
"run_id": 77,
"quality": null
}
}
],
"page": {
"limit": 2,
"has_more": false,
"next_cursor": null
},
"meta": {
"vin": "1FTFW1E50NFA00001",
"sources": {
"v2": "ok",
"md_legacy": "ok"
},
"partial": false
}
}
Response fields
| Field | Type | Description |
|---|---|---|
data[] | array of object | |
data[].event_id | string (uuid) | |
data[].domain_id | integer or null | |
data[].vin | string or null | |
data[].source_key | string or null | |
data[].event_type | listed or price_changed or price_basis_changed or mileage_changed or photos_changed or status_changed or correction | |
data[].observed_at | string | |
data[].prev | object | State before the event (object or null). |
data[].next | object | State after the event (object or null). |
data[].entity_version | integer or null | |
data[].episode | integer or null | |
data[].freshness | object | |
data[].freshness.observed_at | string | |
data[].provenance | object | |
data[].provenance.source | v2 or md_legacy or foureyes | |
data[].provenance.adapter_version | string or null | |
data[].provenance.run_id | integer or null | |
data[].provenance.quality | string or null | Comma list of archive caveats, for example mass_removal_hour. |
page | object | |
page.limit | integer | |
page.has_more | boolean | |
page.next_cursor | string or null | |
meta | object | |
meta.vin | string | |
meta.partial | boolean | |
meta.sources | object |
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_parameter | A parameter is missing or malformed. The message names it. |
| 401 | invalid_api_key | The key is missing, revoked or mistyped. |
| 403 | not_entitled | The key may not read this dealer or source. |
| 429 | rate_limited or quota_exceeded | Slow down (see Retry-After), or the Free monthly calls are used up. |
| 504 | query_timeout | The query passed its 4 second budget. Narrow the filters. Not billed. |
Every error has the same body. Full list: errors.