---
title: "Live prices"
description: "Every source is priced live. How the continuous feed works, the extra at-request bid re-read on open-bidding auctions, and how to read `live_price` and `live_price_pending`."
canonical: "https://thecarapi.com/docs/live-prices"
contract_version: "2026-08-19"
api_base: "https://api.thecarapi.com"
source: "https://thecarapi.com/docs/live-prices.md"
---

# Live prices

Every source is priced live. How the continuous feed works, the extra at-request bid re-read on open-bidding auctions, and how to read `live_price` and `live_price_pending`.

**Prices are live across every source.** All seven auction sources are refreshed continuously rather than on a slow batch schedule, so the price on a search card, a facet count, a top-offer row or a detail response is a current figure and not yesterday's. There is no stale tier and no premium "real-time" upgrade — live pricing is the baseline for everything.

On top of that baseline, one category of listing gets a **second** refresh: a *running* auction with open bidding. A bid-type car can move between any two refreshes, however frequent, so when you open one of those the API re-reads its current bid from the auction house **while serving your request** and hands you the figure as of that instant. That extra step is what the `live_price` block reports.

|  | The live feed | The at-request bid re-read |
| --- | --- | --- |
| What it is | Every listing on every source, continuously refreshed | The current bid, fetched from the auction house during your request |
| Covers | All seven sources, every listing | Running auctions on sources with open bidding |
| Where you see it | Everywhere — search, facets, top offers, market data, both detail endpoints | The two detail endpoints |
| How you can tell | It is simply the price on the response | The response carries a `live_price` block |
| Reused for | Until the next refresh | About two minutes (`live_prices.ttl_seconds`) |

> **An absent `live_price` block does not mean a stale price** — It means this listing did not need the second refresh — it is not a running open-bidding auction, or its bid has not been published. The price you are holding still came from the live feed. This is the single most common misreading of the API.

**There is no separate real-time endpoint, and no parameter to switch on.** Ask for a car's details the way you already do, and if it qualifies for the bid re-read you get the current figure automatically. The only difference you can observe is whether the response carries a `live_price` block.

> **Where the bid re-read applies** — `GET /api/auction/{site}/{id}` and `GET|POST /api/car-details`, when the response is served from stored auction data — the normal case. Search results never carry a `live_price` block, because re-reading an auction house once per row would make a 100-row page unservable; they are still served from the live feed.

## Which sources get the bid re-read

All seven are live-priced from the continuous feed. This table is about the *second* refresh — which sources additionally have a per-car bid we can re-read at request time.

| Source | Bid re-read | Why |
| --- | --- | --- |
| `openlane` | Yes | Timed online auction with open bidding. |
| `ecarstrade` | Yes | Timed online auction; the current bid is only published over the auction house's socket. |
| `auto1`, `encar`, `copart`, `schadeautos`, `japanauction` | No | Live-priced from the feed like every source, but they run no open per-car bidding, so there is no separate bid to re-read. |

Do not hardcode that list — read `live_prices.sites` from `GET /api/contract`.

## Reading the response

When a price was refreshed, the detail body gains:

```json
"live_price": {
  "price": 25900,
  "currency": "EUR",
  "source": "openlane",
  "fetched_at": 1786659750
}
```

`price` is the raw bid at the auction house. `fetched_at` is a Unix timestamp in seconds, recorded when the auction house answered — use it, not your own clock, to render an "as of" indicator. At the same time the **existing price fields are overwritten** with figures recomputed from that bid, so a client that ignores `live_price` entirely still shows the right number.

| Field | Meaning |
| --- | --- |
| `current_price` | The raw current bid. Equals `live_price.price`. |
| `current_final` | Bid plus fees and taxes. |
| `current_tax`, `current_tax_delivery` | The recomputed tax components. |
| `public_price_eur` | The headline price, recomputed. |
| `buy_now_price`, `buynow_final` | Not touched — a buy-now figure does not move with bidding. |

> **Where the block sits differs by endpoint** — `/api/auction/{site}/{id}` wraps the vehicle in `auction`, so the prices and the live block sit inside it. `/api/car-details` has no such wrapper — its body is source-shaped — so the same keys sit at the top level. One trap worth naming: `/api/car-details`'s `auction` key is **not** the vehicle record. For `openlane` it is an image container that shares a name and nothing else.

## Price fields are JSON numbers

Price fields are always numbers. Before contract `2026-08-18` they were serialized as **strings** on a normal read but as **numbers** whenever a live price had been refreshed — the same field, two types, switching on something a client could neither control nor predict.

```diff
- "current_price": "25600.00"
+ "current_price": 25600.0
```

Nothing to do if you parse with `Number(...)`, `float(...)` or into a dynamic type, or if your client already tolerated the live-price case. A statically typed client that declares these as strings has to retype them as numbers.

```text
start_price          current_price        current_final
current_tax          current_tax_delivery buy_now_price
buynow_final         buynow_tax           buynow_tax_delivery
final_price          public_price_eur     current_bgn
buynow_bgn
```

_The fields affected by the string → number change._

`/api/car-details` returns these fields as well, at the top level of the response. On `/api/auction/{site}/{id}` they sit inside `auction`, as before.

## Telling "no live price" from "cannot do live prices"

The `live_price` block is **absent** whenever the price was not refreshed, and the reasons are deliberately indistinguishable from one another in a detail response:

- The source has no live auctions at all — five of the seven never do.
- The auction has already ended.
- The car has no bids yet. Common on eCarsTrade, where a car with no current bid publishes no price at all.
- **The auction is blind.** Most eCarsTrade auctions are: every bid is private, so no price is published anywhere and none is inferred. `current_price` stays `null` and that is the correct, final answer — not a missing value to retry for.
- The refresh allowance for this source in the current minute was already spent.
- This deployment has live prices switched off.

**Its absence never means the price is stale.** Every listing is live-priced from the continuous feed whether or not this block appears; the block only reports the *additional* at-request bid re-read, which most listings do not qualify for. To tell "this deployment cannot do the re-read" from "this car does not need one right now", read `live_prices.enabled` from `/api/contract` once at startup — on a build from before the feature the whole `live_prices` block is missing, which is the only way to distinguish the two.

## live_price_pending: the refresh that arrived late

The refresh runs inside your request under a time budget. When the auction house is slower than that, the response goes out with the stored price **and the refresh keeps running** — a second or two later it completes and is cached. Nothing is lost; it just missed your response, and the body says so with `live_price_pending: true`.

| Response carries | What you are holding | What to do |
| --- | --- | --- |
| `live_price` | The current bid | Nothing. Render it. |
| `live_price_pending: true` | The feed price; the bid re-read is still running | Repeat the same request once after ~2s. The repeat is served from cache. |
| Neither | The live feed price, and that is final for now | Nothing. This is the normal case. |

One extra read is enough — if it still carries no `live_price`, the upstream call failed and the car is in cooldown; stop rather than retrying. A pending response is sent with `Cache-Control: no-store` and `X-Live-Price: pending`, so a browser, CDN or shared proxy cannot answer your follow-up with the very body that missed the price.

```javascript
async function vehicleWithLivePrice(site, id) {
  const first = await getJson(`/api/auction/${site}/${id}`);
  if (first.auction.live_price || !first.auction.live_price_pending) return first;

  // The refresh missed the budget but is still running. Exactly one more read.
  await new Promise((r) => setTimeout(r, 2000));
  return getJson(`/api/auction/${site}/${id}`);
}
```

_Read once more, then stop. A loop here only burns quota against a cooldown._

## Polling

A refreshed price is reused for `live_prices.ttl_seconds` (default 120), and `Cache-Control: max-age` on the response drops to match. **Polling faster than the TTL returns the same figure** and gains you nothing. This is per page open, not a subscription: the refresh runs once when the car is read cold and does not keep tracking the bid while you hold the page. Issue it when a vehicle is opened, not on an interval.

## How much movement to expect

Worth calibrating before you build anything around this: **a live price usually equals the standard one.** Measured against auctions closing the same day, most running lots had not moved since the last refresh. When they had:

| Source | Proportion that had moved | Typical gap | Why |
| --- | --- | --- | --- |
| `openlane` | Roughly one in six | €100–€300 | Bids step in fixed increments, and movement concentrates in the hours before close. |
| `ecarstrade` | Most of them | €1,800–€5,500 | Bid cars there carry no price on the listing at all, so the standard figure can drift much further before it is corrected. |

Treat `live_price` as a **correction, not a stream.** It is the right thing to read when a user opens a vehicle. It is the wrong thing to poll in a loop, and it will not let you watch a bid climb.

## Which prices get the second refresh

Only the detail path performs it. `/api/search`, `/api/top-offers`, the market intelligence endpoints and every price-based sort or filter serve the live feed price without the extra per-request call — re-reading an auction house once per row would make a 100-row page unservable.

> **A detail page can show a higher price than the card you clicked** — On a running auction, the at-request re-read can catch a bid placed seconds after the feed last saw it. That is not an inconsistency to report — it is the newer number. Do not build "price changed since you clicked" warnings on this difference, and do not expect a car re-read above your search's `price_to` to disappear from that search.

Everything here is read only. This API observes auction prices; it never places, raises or withdraws a bid on your behalf.
