A wholesale vehicle listing carries somewhere between twenty and sixty photographs. At a million live vehicles that is tens of millions of images — and unlike the specification fields, users actually look at these. A listing grid where a third of the thumbnails are broken reads as a broken product, regardless of how good the underlying data is.
The naive approach is to store the source image URL alongside the listing and let the browser fetch it. It works in development. Here is why it does not survive.
Four reasons hotlinking fails
1. Source URLs expire
This is the fatal one. Auction platforms commonly stop serving media for lots that have closed — sometimes within days, sometimes after weeks, and rarely with any announcement. Live listings look fine. Your historical archive rots quietly from the oldest end, and by the time anyone notices, months of galleries are gone with no way to recover them.
2. Hotlink protection
Many sources check the Referer header or require a valid session cookie. The failure mode is genuinely nasty: images load for you (you visited the source recently), fail for your users, and behave differently across browsers depending on referrer policy. It presents as an intermittent bug and is frequently misdiagnosed for weeks.
3. File sizes built for a different use case
Source galleries are full-resolution JPEGs, commonly 2–6 MB each. Loading a twenty-four-card grid with one thumbnail each means fetching perhaps 60 MB to render a page that needs about 400 KB of imagery. On mobile that is not slow, it is unusable.
4. You have no control over availability
Your product's perceived performance becomes a function of seven third-party CDNs you have no relationship with, no SLA against, and no cache control over. One source having a slow morning becomes your incident.
What an image vault has to do
Copying images is the easy part. These are the properties that determine whether the copy is actually worth its storage cost:
- 1Copy at ingest, not on demand. Lazy copying means the first user to view an old lot triggers a fetch against a URL that may already be dead. Copy when the listing is first seen, while the source still serves it.
- 2Re-encode. WebP at sensible quality typically cuts payload substantially against source JPEG with no visible difference in a listing grid.
- 3Serve multiple sizes. A thumbnail grid and a full-screen gallery have completely different requirements, and serving one image for both wastes bandwidth in one direction or detail in the other.
- 4Stable URLs that outlive the lot. The entire point. If the served URL breaks when the auction closes, you have built an expensive cache rather than an archive.
- 5Preserve ordering and role. Which image is primary, which section of the vehicle each belongs to, exterior versus interior versus damage detail. Galleries presented in arbitrary order are noticeably worse to use.
- 6Record dimensions. Without width and height in the payload, every image causes layout shift on load, which is both a user-experience problem and a Core Web Vitals penalty.
curl -s "https://api.thecarapi.com/api/auction-images/encar/38112900" \
-H "X-API-Key: $API_KEY"site/auction_id pair as detail and price history, and return served_url alongside ordering, section and dimensions.Consuming galleries efficiently
Two patterns that account for most of the practical difference in page performance.
Do not fetch galleries in search
Search results need one thumbnail per card. Full gallery payloads for twenty-four results is a large response containing data for images the user will almost certainly never open. Use the thumbnail on the search card and fetch the gallery only when a detail view opens.
// Search card — thumbnail only, from the search response
<img
src={listing.thumbnail_url}
alt={`${listing.registration_year} ${listing.clean_make} ${listing.clean_model}`}
width={400}
height={300}
loading="lazy"
/>
// Detail view — gallery fetched on open, first image eager
const images = await fetch(
`${API}/api/auction-images/${site}/${id}`,
{ headers: { 'X-API-Key': key } }
).then(r => r.json());Write real alt text
Vehicle imagery has an unusual advantage here: you have structured data describing exactly what is in the picture. alt="2021 Kia Niro, exterior front three-quarter" costs nothing to generate, is genuinely useful to screen reader users, and is one of the few places where accessibility and image SEO point in exactly the same direction. Empty alt attributes on a page whose entire content is vehicles is a wasted signal.
The storage maths
Worth doing explicitly, because the intuition is usually wrong in both directions.
| Source JPEG | WebP, re-encoded | |
|---|---|---|
| Average image | ~2.5 MB | ~150 KB |
| One listing (30 images) | ~75 MB | ~4.5 MB |
| 1M live listings | ~75 TB | ~4.5 TB |
Storage at a few terabytes is inexpensive. The costs that actually hurt are elsewhere: the residential-proxy bandwidth to fetch the originals in the first place, and the CDN egress to serve them afterwards — which scales with your traffic rather than your catalogue, and is therefore the bill that grows exactly when things are going well. The scraping cost breakdown puts both lines in context.
Rights, briefly
Listing photographs are copyrighted works, and the position on re-hosting them is genuinely distinct from the position on extracting factual specifications. This is not legal advice and the answer varies by jurisdiction and by the terms you are operating under — but it is a real consideration, and it is one of the substantive differences between collecting images yourself and consuming them from a provider who has a commercial relationship with the sources.
For the fuller picture on what auction data contains and how to consume it, start with the auction data guide, or see the image endpoints in the reference.
Frequently asked questions
Why do auction listing images break after a few weeks?
Source platforms commonly stop serving media for lots that have closed, often within days or weeks of the auction ending. Live listings continue to work, so the problem is invisible at first and shows up as historical galleries decaying from the oldest end — with no way to recover images you did not copy while the source was still serving them.
Can I just link directly to source auction images?
Only if you never need to show a listing after it closes. Direct linking also runs into hotlink protection that checks the request referrer — which causes intermittent failures that look like a browser bug — and serves multi-megabyte originals to mobile users. For anything with a historical view, images have to be copied at ingest.
How much storage do vehicle images actually need?
Roughly 4–5 TB for a million listings at thirty images each once re-encoded to WebP, against roughly 75 TB for the source JPEGs. Storage itself is cheap; the expensive lines are the bandwidth to fetch the originals over residential proxies and the CDN egress to serve them, the latter scaling with your traffic rather than your catalogue size.
What should vehicle image alt text say?
Describe the vehicle and the view: "2021 Kia Niro, exterior front three-quarter". You already hold structured data for make, model and year, so this can be generated automatically. It is one of the few cases where the accessibility requirement and the image SEO opportunity are satisfied by exactly the same text.
Should search results include full image galleries?
No. Search cards need one thumbnail each; returning thirty image records per result multiplies the response size for data the user will almost certainly never open. Use the thumbnail in the search payload and fetch the gallery on demand when a detail view opens.
- images
- CDN
- WebP
- performance
One API, seven auction sources
Normalized search, source-aware detail, CDN image galleries, price history and archive access — all included on every plan.