DEV Community

TheCarApi
TheCarApi

Posted on

The First API Call Is the Easy Part: 7 Lessons from Building on Live Vehicle Data

The first API call usually works.

You add a key, send a request, receive JSON, and render a row of cars. It feels as if the integration is nearly finished.

Then production introduces the parts the demo never showed you: a live price that should not have been cached, an auction feed that omits mileage, a gallery URL that expires, a retry loop that amplifies an outage, or an offset that becomes slower with every page.

Vehicle inventory is unusually good at exposing fragile assumptions. It changes constantly, comes from incompatible sources, contains legitimately missing fields, and mixes slow-changing specifications with time-sensitive prices.

Here are seven lessons worth handling before your integration faces real users.

  1. Keep the key on the server

TheCarAPI accepts an API key in the X-API-Key header or as a bearer token:

curl "https://api.thecarapi.com/api/search?limit=5" \
  -H "X-API-Key: $THECARAPI_KEY"
Enter fullscreen mode Exit fullscreen mode

Do not place that key in browser JavaScript. A secret shipped to a browser is no longer a secret. Route requests through your backend, where you can protect credentials, cache responses, enforce your own user limits, and change providers without shipping a frontend release.

Also fail fast on 401 and 403. Authentication failures are configuration problems, not transient network errors. Retrying them will not repair the key and can trigger lockout protection.

  1. Persist a source-qualified identity

A numeric auction ID is not globally unique. Two auction platforms can assign the same number to unrelated vehicles.

The durable identity in TheCarAPI is the pair:

site_name + auction_id
Enter fullscreen mode Exit fullscreen mode

For example:

openlane/11125938
Enter fullscreen mode Exit fullscreen mode

Persist both fields. Use the pair in database keys, cache keys, analytics events, bookmarks, and internal URLs. Treating auction_id alone as globally unique creates collisions that may remain invisible until a second source is added.

This is a broader integration lesson: if data originates in multiple systems, identity must retain its namespace.

  1. Cache by data type, not by endpoint

A vehicle detail response can contain specifications, condition information, images, and a current price. Those fields do not age at the same speed.

A practical starting point looks like this:

Data Starting TTL
Makes, models, fuels, countries 24 hours
Facet counts 5–15 minutes
Search results 1–5 minutes
Vehicle specification About 1 hour
Image galleries Days
Price history About 15 minutes
Live bid or current actionable price Do not cache

The exact values depend on your application. The important decision is separating data by volatility.

TheCarAPI itself returns caching and request-tracing headers on relevant responses. Log them. A low cache-hit rate can reveal an unstable query parameter long before infrastructure graphs tell you why traffic increased.

  1. Do not walk endlessly through deep offsets

Offset pagination works well for user-facing pages. It becomes increasingly expensive when used to export a complete inventory.

If you need a large reconciliation job, partition the query by a stable dimension such as year, brand, country, or price band. Keep every partition shallow.

for year in range(2015, 2027):
    offset = 0

    while True:
        page = search(
            brand="bmw",
            year_from=year,
            year_to=year,
            offset=offset,
            limit=100,
            include_total=False,
        )

        if not page["results"]:
            break

        yield from page["results"]
        offset += 100
Enter fullscreen mode Exit fullscreen mode

If the interface only needs “load more,” skip the total count. Counting a large filtered result is often more expensive than fetching the next page.

  1. Retry only failures that can recover

An indiscriminate retry policy turns small incidents into larger ones.

Retry 408, 429, and temporary 5xx responses. Honour Retry-After when present, add exponential backoff and jitter, and cap the number of attempts.

Do not retry malformed requests, invalid authentication, permission failures, or missing vehicles.

const retryable = new Set([408, 429, 500, 502, 503, 504]);

async function fetchJson(url, attempts = 4) {
  for (let attempt = 0; attempt < attempts; attempt++) {
    const response = await fetch(url, {
      headers: { "X-API-Key": process.env.THECARAPI_KEY }
    });

    if (response.ok) return response.json();
    if (!retryable.has(response.status)) {
      throw new Error(`${response.status}: ${await response.text()}`);
    }

    const retryAfter = Number(response.headers.get("retry-after")) * 1000;
    const delay = retryAfter || (2 ** attempt * 250 + Math.random() * 250);
    await new Promise(resolve => setTimeout(resolve, delay));
  }

  throw new Error("Retry budget exhausted");
}
Enter fullscreen mode Exit fullscreen mode

Without jitter, every failed client retries at almost the same time. That thundering herd can turn a short interruption into a sustained outage.

  1. Missing does not mean broken

Different auction sources publish different levels of detail. A missing VIN, horsepower figure, equipment list, or damage field may simply mean the source did not provide it.

Build the UI around sparse data:

• Treat null differently from zero.
• Use placeholders for missing images without failing the page.
• Hide optional charts when their data is unavailable.
• Never present a failed search as an empty result set.
• Monitor field fill rates by source, not only HTTP errors.

That last point catches silent degradation. If a field was populated on 95% of one source yesterday and 40% today, the pipeline is probably broken even if every request returns 200.

  1. Keep wholesale and retail data conceptually separate

An auction price and a retail asking price answer different questions.

TheCarAPI exposes auction inventory through its auction search surface and European classifieds through a separate retail-market surface covering listings from hundreds of origin portals. The separation matters: classifieds do not have bids, auction end dates, or the same detail structure.

Use retail listings as context for wholesale inventory, not as interchangeable records. A useful product may compare them, but its data model should preserve what each number represents.

A production checklist

Before launch, verify that:

• API keys exist only on the server.
• Every vehicle is keyed by source and auction ID.
• Cache lifetimes match field volatility.
• Export jobs partition large result sets.
• Retries cover only recoverable statuses and include jitter.
• Missing fields are expected and handled.
• Request IDs are logged.
• Empty results are distinguishable from failed requests.
• Wholesale prices and retail asking prices remain separate concepts.

The interesting part of an API integration is rarely the first request. It is the behaviour after thousands of requests, an upstream change, and a traffic spike.

If you are working on an automotive marketplace, dealer tool, export workflow, or pricing product, you can explore TheCarAPI through the documentation or the public Postman workspace.

What failure mode has caused the most trouble in your own third-party data integrations?

Top comments (0)