[← API v2 Overview](https://motordesk.com/api-docs/v2/)

# Listings

A read-only, advert-only feed of your live for-sale stock, built for syndicating to your own website. Each listing carries only public advert data - never cost, funding, purchase, reserve or customer information.

## Listings vs Vehicles

Use [`/vehicles`](https://motordesk.com/api-docs/v2/vehicles/) to **manage** your stock (create, update, price, publish), and `/listings` to **publish** it. The listing is a curated, stable subset of the vehicle object, scoped to what should appear in an advert, so it is safe to hand to third-parties without exposing your business data - even where a key would otherwise be over-granted.

- **Only website-published stock.** A listing exists only for vehicles your published website index lists: live for-sale and reserved stock, plus recently-sold stock within your sold-retention window.
- **Advert data only.** A fixed, defined field set - no cost, funding, purchase, reserve or customer fields can appear.
- **Stable shape.** Defined fields are always present, with `null` where the vehicle has no value, so your parsing never breaks.

## Marketplaces & Service Providers

Looking to integrate a marketplace or third-party service? This endpoint is linked to the website sales channel, using this means no independent control for your specific sales channel and no promotion of your service within MotorDesk. Please [**contact us**](https://motordesk.com/contact/) to discuss your integration.

## Access

Listings require the single `listings:read` scope. Issue a key with only that scope for a syndication partner: it can read listings and nothing else.

## The Listing Object

A listing uses the same field names and structure as a vehicle, grouped under `data`. `data.vehicle`, `data.history`, `data.option` and `data.spec` mirror the vehicle response; `data.stock` carries only advertised pricing and identity; `data.finance` carries the advertised representative finance example (headline monthly plus the full breakdown).

```json
{
  "id": 84213,
  "tag": "AB12CDEF",
  "country": "UK",
  "status": { "id": 2, "name": "for-sale", "label": "For Sale" },
  "registration": "EO68NRJ",
  "url_full": "https://example-motors.co.uk/vehicles/AB12CDEF/bmw-3-series/",
  "data": {
    "vehicle": { "type": "Car", "make": "BMW", "model": "3 Series", "derivative": "320i M Sport 4dr Auto", "fuel": "Petrol", "transmission": "Automatic", "mileage": 38450, "colour": "Black", "...": "..." },
    "history": { "year": 2020, "condition": "Used", "service_history": "Full", "...": null },
    "stock":   { "price_website": "18995.00", "price_rrp": "42000.00", "vin": "WBA8E9105HK000000" },
    "finance": { "example_monthly": "507.75", "example": { "cash_price": "20000.00", "total_deposit": "2000.00", "monthly_payment": "507.75", "total_monthly_payments": 25, "final_payment": "7856.00", "term": 27, "representative_apr": 10.9, "...": "..." } },
    "option":  { "option_custom": [], "attention": "Full Service History", "feature": {} },
    "spec":    { "engine": {}, "fuel": {}, "size": {} }
  },
  "media": [ { "url": "...", "type": "image", "position": 0 } ],
  "updated": 1781303332,
  "hash": "9b1d4c7a8f2e4a1b"
}
```

The full field list is in the reference below. `status.name` tells you whether a listing is `for-sale`, `reserved`, `sold` or `complete`.

## Syndicating Stock

The recommended pattern is **snapshot once, then react to events, and reconcile periodically** - far cheaper than re-polling the whole feed:

1. **Initial snapshot.** Page through `GET /listings` (cursor pagination) to build your copy.
2. **React to changes.** Subscribe to the [vehicle webhooks](https://motordesk.com/api-docs/v2/webhooks/) (`vehicle.created`, `vehicle.updated`, `vehicle.sold`, `vehicle.deleted`). On a change, fetch `GET /listings/{id}`; on sold/deleted, remove it.
3. **Reconcile.** Periodically (e.g. daily) call `GET /listings/manifest` and diff it against your copy to self-heal any missed events.

## Delta Sync

To pull only what changed since your last sync, pass `updated_since` (a Unix timestamp; `updated_before` is also supported) with cursor pagination. Each listing's `updated` field is your high-water mark.

```
GET /2.0/listings?updated_since=1781300000
```

A delta only returns changed records - it cannot tell you what was *removed*. Use the manifest (or the sold/deleted webhooks) for removals.

## Manifest & Reconciliation

`GET /listings/manifest` returns a lightweight `{ id, updated, hash }` entry for every live listing. Diff it against your store: ids you don't have are new, a changed `hash` means refetch, and ids absent from the manifest have left stock. The same `hash` appears on the full listing, so you can compare without recomputing.

The manifest supports conditional requests: it returns an `ETag`, and a subsequent request sending `If-None-Match` gets a `304 Not Modified` when nothing has changed since your last sync, so routine polls are nearly free.

## Endpoints & Fields

## The Listing Object

Generated from the OpenAPI schema. Always matches the live API.

**Fields (69)**

| Attribute | Type | Required | Description | Example |
| --- | --- | --- | --- | --- |
| `id` | integer | - | Vehicle id. | `84213` |
| `tag` | string or null | - |  | `AB12CDEF` |
| `country` | string or null | - |  | `UK` |
| `status` | object | - | Type-aware status, as GET /vehicles returns it. |  |
| `status.id` | integer | - |  | `2` |
| `status.name` | string | - | Status slug (e.g. for-sale, reserved, sold, complete). | `for-sale` |
| `status.label` | string | - |  | `For Sale` |
| `registration` | string or null | - |  | `EO68NRJ` |
| `url_full` | string or null | - | Public listing (VDP) URL on the dealer website. | `https://example-motors.co.uk/vehicles/AB12CDEF/bmw-3-series/` |
| `data` | object | - | Advert data, grouped as GET /vehicles groups it. Listed fields are always present (null when the vehicle has no value). |  |
| `data.vehicle` | object | - | Vehicle attributes. |  |
| `data.vehicle.type` | string or null | - | Vehicle class. | `Car` |
| `data.vehicle.make` | string or null | - |  | `BMW` |
| `data.vehicle.model` | string or null | - |  | `3 Series` |
| `data.vehicle.generation` | string or null | - |  | `Saloon (2019 - 2023)` |
| `data.vehicle.derivative` | string or null | - |  | `320i M Sport 4dr Auto` |
| `data.vehicle.trim` | string or null | - |  | `M Sport` |
| `data.vehicle.body` | string or null | - |  | `Saloon` |
| `data.vehicle.fuel` | string or null | - |  | `Petrol` |
| `data.vehicle.transmission` | string or null | - |  | `Automatic` |
| `data.vehicle.drivetrain` | string or null | - |  | `RWD` |
| `data.vehicle.doors` | integer or null | - |  | `4` |
| `data.vehicle.seats` | integer or null | - |  | `5` |
| `data.vehicle.colour` | string or null | - |  | `Black` |
| `data.vehicle.colour_name` | string or null | - | Manufacturer colour name. | `Sapphire Black` |
| `data.vehicle.engine_size` | string or null | - |  | `2.0` |
| `data.vehicle.mileage` | integer or null | - |  | `38450` |
| `data.vehicle.registered` | string or null | - | First registration date (YYYY-MM-DD). | `2020-03-01` |
| `data.vehicle.driver_position` | string or null | - |  | `RHD` |
| `data.vehicle.interior_upholstery` | string or null | - |  | `Leather` |
| `data.vehicle.interior_colour` | string or null | - |  | `Black` |
| `data.vehicle.exterior_finish` | string or null | - |  | `Metallic` |
| `data.vehicle.bedroom_layout` | string or null | - | Bedroom layout (caravans/motorhomes). |  |
| `data.vehicle.end_layout` | string or null | - | End layout (caravans/motorhomes). |  |
| `data.vehicle.bedroom` | integer or null | - | Bedrooms (caravans/motorhomes). | `2` |
| `data.vehicle.berth` | integer or null | - | Berths (caravans/motorhomes). | `4` |
| `data.vehicle.seat_belt` | integer or null | - | Belted seats (caravans/motorhomes). | `4` |
| `data.vehicle.wheelchair` | string or null | - | Wheelchair accessible (Yes/No). | `No` |
| `data.history` | object | - |  |  |
| `data.history.year` | integer or null | - |  | `2020` |
| `data.history.condition` | string or null | - |  | `Used` |
| `data.history.keys` | integer or null | - | Number of keys supplied. | `2` |
| `data.history.service_history` | string or null | - |  | `Full` |
| `data.stock` | object | - |  |  |
| `data.stock.price_website` | string or null | - | Advertised website price (2dp decimal string), or null for POA. | `18995.00` |
| `data.stock.price_rrp` | string or null | - | Manufacturer RRP (2dp decimal string). | `42000.00` |
| `data.stock.vin` | string or null | - |  | `WBA8E9105HK000000` |
| `data.finance` | object | - | Representative finance, as GET /vehicles data.finance returns it. Always present; example_monthly and example are null when the vehicle has no finance example. |  |
| `data.finance.example_monthly` | string or null | - | Advertised representative monthly payment (2dp decimal string); null when finance is not enabled. | `507.75` |
| `data.finance.example` | object or null | - | Representative finance example as shown on the advert; null when none has been generated. Figures are illustrative and subject to status. |  |
| `data.finance.example.cash_price` | string or null | - | Cash price. | `20000.00` |
| `data.finance.example.total_deposit` | string or null | - | Total deposit. | `2000.00` |
| `data.finance.example.total_credit` | string or null | - | Total amount of credit. | `18000.00` |
| `data.finance.example.first_payment` | string or null | - | First payment. | `507.75` |
| `data.finance.example.monthly_payment` | string or null | - | Regular monthly payment. | `507.75` |
| `data.finance.example.total_monthly_payments` | integer or null | - | Number of monthly payments. | `25` |
| `data.finance.example.final_payment` | string or null | - | Final payment. | `7856.00` |
| `data.finance.example.term` | integer or null | - | Agreement duration in months. | `27` |
| `data.finance.example.admin_fee` | string or null | - | Admin fee. | `0.00` |
| `data.finance.example.option_purchase_fee` | string or null | - | Option to purchase fee. | `10.00` |
| `data.finance.example.interest_charge` | string or null | - | Total interest charges. | `3057.50` |
| `data.finance.example.total_payable` | string or null | - | Total amount payable. | `23057.50` |
| `data.finance.example.fixed_interest_rate` | number or null | - | Annual fixed interest rate (percent). | `10.36` |
| `data.finance.example.representative_apr` | number or null | - | Representative APR (percent). | `10.9` |
| `data.option` | object | - | Full option section, as GET /vehicles data.option (option_custom, attention, description, website, feature). |  |
| `data.spec` | object | - | Full spec section, as GET /vehicles data.spec (performance, engine, battery, fuel, size, insurance, other). |  |
| `media` | array of object | - | Ordered advert media, as GET /vehicles returns it. |  |
| `updated` | integer | - | Unix timestamp last updated (use for delta sync). | `1781303332` |
| `hash` | string | - | Change token, identical to the manifest entry hash for this listing. | `9b1d4c7a8f2e4a1b` |

**Example object**

```json
{
    "id": 84213,
    "tag": "AB12CDEF",
    "country": "UK",
    "status": {
        "id": 2,
        "name": "for-sale",
        "label": "For Sale"
    },
    "registration": "EO68NRJ",
    "url_full": "https://example-motors.co.uk/vehicles/AB12CDEF/bmw-3-series/",
    "data": {
        "vehicle": {
            "type": "Car",
            "make": "BMW",
            "model": "3 Series",
            "generation": "Saloon (2019 - 2023)",
            "derivative": "320i M Sport 4dr Auto",
            "trim": "M Sport",
            "body": "Saloon",
            "fuel": "Petrol",
            "transmission": "Automatic",
            "drivetrain": "RWD",
            "doors": 4,
            "seats": 5,
            "colour": "Black",
            "colour_name": "Sapphire Black",
            "engine_size": "2.0",
            "mileage": 38450,
            "registered": "2020-03-01",
            "driver_position": "RHD",
            "interior_upholstery": "Leather",
            "interior_colour": "Black",
            "exterior_finish": "Metallic",
            "bedroom_layout": "",
            "end_layout": "",
            "bedroom": 2,
            "berth": 4,
            "seat_belt": 4,
            "wheelchair": "No"
        },
        "history": {
            "year": 2020,
            "condition": "Used",
            "keys": 2,
            "service_history": "Full"
        },
        "stock": {
            "price_website": "18995.00",
            "price_rrp": "42000.00",
            "vin": "WBA8E9105HK000000"
        },
        "finance": {
            "example_monthly": "507.75",
            "example": {
                "cash_price": "20000.00",
                "total_deposit": "2000.00",
                "total_credit": "18000.00",
                "first_payment": "507.75",
                "monthly_payment": "507.75",
                "total_monthly_payments": 25,
                "final_payment": "7856.00",
                "term": 27,
                "admin_fee": "0.00",
                "option_purchase_fee": "10.00",
                "interest_charge": "3057.50",
                "total_payable": "23057.50",
                "fixed_interest_rate": 10.36,
                "representative_apr": 10.9
            }
        },
        "option": {},
        "spec": {}
    },
    "media": [
        {}
    ],
    "updated": 1781303332,
    "hash": "9b1d4c7a8f2e4a1b"
}
```

## Endpoints

### GET List Stock Listings

`GET /2.0/listings` · scope `listings:read`

Public advert view of for-sale, reserved and recently-sold stock for syndication. Supports delta sync (updated_since + cursor). Excludes all business, cost, funding and customer data by construction.

**Parameters (16)**

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `page` | query | integer | - | Page number, starting at 1 (offset pagination). |  |
| `per_page` | query | integer | - | Results per page (maximum 500). |  |
| `cursor` | query | string | - | Cursor pagination: pass the previous page's meta.pagination.next_cursor to continue from that point instead of page. Stable while the set changes underneath, so preferred for delta sync; a null next_cursor means the end. |  |
| `make` | query | string | - | Exact match, or use % for wildcard matching (requires search:wildcard, minimum 3 characters). | `BMW` |
| `model` | query | string | - | Exact match, or use % for wildcard matching (requires search:wildcard). | `3 Series` |
| `fuel` | query | string | - | Exact match, or use % for wildcard matching (requires search:wildcard). | `Petrol` |
| `transmission` | query | string | - | Exact match, or use % for wildcard matching (requires search:wildcard). | `Automatic` |
| `body` | query | string | - | Exact match, or use % for wildcard matching (requires search:wildcard). | `Saloon` |
| `colour` | query | string | - | Exact match, or use % for wildcard matching (requires search:wildcard). | `Black` |
| `year` | query | string | - | Exact match, or a two-element array [from, to] for a range (e.g. year[]=2020&year[]=2023). An empty bound is open-ended. |  |
| `mileage` | query | string | - | Exact match, or a two-element array [from, to] for a range. An empty bound is open-ended. |  |
| `price` | query | string | - | Advertised website price. Exact match, or a two-element array [from, to] for a range. An empty bound is open-ended. |  |
| `updated_since` | query | integer | - | Unix timestamp; only listings changed at or after this time (delta sync). |  |
| `updated_before` | query | integer | - | Unix timestamp; only listings changed before this time. |  |
| `cursor` | query | string | - | Keyset pagination cursor from a previous response's meta.pagination.next_cursor. When supplied, page/total are not returned. |  |
| `fields` | query | string | - | Comma-separated dot-paths to return only those fields, e.g. id,data.stock.price_channel. |  |

**Responses**

| Status | Description |
| --- | --- |
| `200` OK | Paginated listings |

Failures use the standard [error responses](https://motordesk.com/api-docs/v2/errors/) (4xx/5xx) with the shared error envelope.

```json
{
    "success": true,
    "data": [
        {
            "id": 84213,
            "tag": "AB12CDEF",
            "country": "UK",
            "status": {
                "id": 2,
                "name": "for-sale",
                "label": "For Sale"
            },
            "registration": "EO68NRJ",
            "url_full": "https://example-motors.co.uk/vehicles/AB12CDEF/bmw-3-series/",
            "data": {
                "vehicle": {
                    "type": "Car",
                    "make": "BMW",
                    "model": "3 Series",
                    "generation": "Saloon (2019 - 2023)",
                    "derivative": "320i M Sport 4dr Auto",
                    "trim": "M Sport",
                    "body": "Saloon",
                    "fuel": "Petrol",
                    "transmission": "Automatic",
                    "drivetrain": "RWD",
                    "doors": 4,
                    "seats": 5,
                    "colour": "Black",
                    "colour_name": "Sapphire Black",
                    "engine_size": "2.0",
                    "mileage": 38450,
                    "registered": "2020-03-01",
                    "driver_position": "RHD",
                    "interior_upholstery": "Leather",
                    "interior_colour": "Black",
                    "exterior_finish": "Metallic",
                    "bedroom_layout": "",
                    "end_layout": "",
                    "bedroom": 2,
                    "berth": 4,
                    "seat_belt": 4,
                    "wheelchair": "No"
                },
                "history": {
                    "year": 2020,
                    "condition": "Used",
                    "keys": 2,
                    "service_history": "Full"
                },
                "stock": {
                    "price_website": "18995.00",
                    "price_rrp": "42000.00",
                    "vin": "WBA8E9105HK000000"
                },
                "finance": {
                    "example_monthly": "507.75",
                    "example": {
                        "cash_price": "20000.00",
                        "total_deposit": "2000.00",
                        "total_credit": "18000.00",
                        "first_payment": "507.75",
                        "monthly_payment": "507.75",
                        "total_monthly_payments": 25,
                        "final_payment": "7856.00",
                        "term": 27,
                        "admin_fee": "0.00",
                        "option_purchase_fee": "10.00",
                        "interest_charge": "3057.50",
                        "total_payable": "23057.50",
                        "fixed_interest_rate": 10.36,
                        "representative_apr": 10.9
                    }
                },
                "option": {},
                "spec": {}
            },
            "media": [
                {}
            ],
            "updated": 1781303332,
            "hash": "9b1d4c7a8f2e4a1b"
        }
    ],
    "meta": {
        "pagination": {
            "page": 0,
            "per_page": 0,
            "total": 0,
            "total_pages": 0,
            "next_cursor": ""
        }
    }
}
```

### GET Get a Stock Listing

`GET /2.0/listings/{id}` · scope `listings:read`

Retrieve a single listing by vehicle id.

**Parameters (2)**

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `id` | path | string | Yes | Resource identifier in the path. |  |
| `fields` | query | string | - | Comma-separated dot-paths to return only those fields, e.g. id,data.stock.price_channel. |  |

**Responses**

| Status | Description |
| --- | --- |
| `200` OK | Get a Stock Listing |

Failures use the standard [error responses](https://motordesk.com/api-docs/v2/errors/) (4xx/5xx) with the shared error envelope.

```json
{
    "success": true,
    "data": {
        "id": 84213,
        "tag": "AB12CDEF",
        "country": "UK",
        "status": {
            "id": 2,
            "name": "for-sale",
            "label": "For Sale"
        },
        "registration": "EO68NRJ",
        "url_full": "https://example-motors.co.uk/vehicles/AB12CDEF/bmw-3-series/",
        "data": {
            "vehicle": {
                "type": "Car",
                "make": "BMW",
                "model": "3 Series",
                "generation": "Saloon (2019 - 2023)",
                "derivative": "320i M Sport 4dr Auto",
                "trim": "M Sport",
                "body": "Saloon",
                "fuel": "Petrol",
                "transmission": "Automatic",
                "drivetrain": "RWD",
                "doors": 4,
                "seats": 5,
                "colour": "Black",
                "colour_name": "Sapphire Black",
                "engine_size": "2.0",
                "mileage": 38450,
                "registered": "2020-03-01",
                "driver_position": "RHD",
                "interior_upholstery": "Leather",
                "interior_colour": "Black",
                "exterior_finish": "Metallic",
                "bedroom_layout": "",
                "end_layout": "",
                "bedroom": 2,
                "berth": 4,
                "seat_belt": 4,
                "wheelchair": "No"
            },
            "history": {
                "year": 2020,
                "condition": "Used",
                "keys": 2,
                "service_history": "Full"
            },
            "stock": {
                "price_website": "18995.00",
                "price_rrp": "42000.00",
                "vin": "WBA8E9105HK000000"
            },
            "finance": {
                "example_monthly": "507.75",
                "example": {
                    "cash_price": "20000.00",
                    "total_deposit": "2000.00",
                    "total_credit": "18000.00",
                    "first_payment": "507.75",
                    "monthly_payment": "507.75",
                    "total_monthly_payments": 25,
                    "final_payment": "7856.00",
                    "term": 27,
                    "admin_fee": "0.00",
                    "option_purchase_fee": "10.00",
                    "interest_charge": "3057.50",
                    "total_payable": "23057.50",
                    "fixed_interest_rate": 10.36,
                    "representative_apr": 10.9
                }
            },
            "option": {},
            "spec": {}
        },
        "media": [
            {}
        ],
        "updated": 1781303332,
        "hash": "9b1d4c7a8f2e4a1b"
    }
}
```

### Manifest

### GET Stock Listing Manifest

`GET /2.0/listings/manifest` · scope `listings:read`

Lightweight {id, updated, hash} for every live listing, for cheap reconciliation: diff against your copy to find adds, changes and removals (absent ids are gone).

**Parameters (2)**

| Parameter | In | Type | Required | Description | Example |
| --- | --- | --- | --- | --- | --- |
| `cursor` | query | string | - | Keyset pagination cursor from a previous response's meta.pagination.next_cursor. When supplied, page/total are not returned. |  |
| `fields` | query | string | - | Comma-separated dot-paths to return only those fields, e.g. id,data.stock.price_channel. |  |

**Responses**

| Status | Description |
| --- | --- |
| `200` OK | Stock Listing Manifest |

Failures use the standard [error responses](https://motordesk.com/api-docs/v2/errors/) (4xx/5xx) with the shared error envelope.

```json
{
    "success": true,
    "data": [
        {
            "id": 84213,
            "updated": 1781303332,
            "hash": "9b1d4c7a8f2e4a1b"
        }
    ],
    "meta": {
        "pagination": {
            "page": 0,
            "per_page": 0,
            "total": 0,
            "total_pages": 0,
            "next_cursor": ""
        }
    }
}
```
