договор 2026-10-05
Справочник

Примери с код

Минимални клиенти на cURL, TypeScript, Python и PHP, плюс рецепти за страниране, условни заявки и страници с детайли.

Няма задължителен SDK. Всеки HTTP клиент, който може да изпраща заглавки и да чете JSON, ще работи. Примерите за търсене използват cURL с jq, TypeScript на сървъра, Python с requests или PHP с Guzzle. Задайте THECARAPI_KEY в съответната среда. Рецептите илюстрират модели на интеграция и използват съществуващото състояние и помощни функции на приложението ви.

cURL

bash
export THECARAPI_KEY="your_key"

curl -sS --compressed \
  -H "X-API-Key: $THECARAPI_KEY" \
  "https://api.thecarapi.com/api/search?brand=bmw&fuel=Diesel&year_from=2018&sort=price_low&limit=24" \
   | jq '.results[] | {auction_id_str, site_name, clean_make, clean_model, public_price_eur}'

TypeScript

typescript
const BASE = "https://api.thecarapi.com";

interface SearchResponse {
  success: boolean;
  results: SearchCard[];
  total: number | null;
  limit: number;
  offset: number;
  max_page: number | null;
  total_pages: number | null;
  contract_version: string;
  request_id: string;
}

interface SearchCard {
  auction_id: number;
  auction_id_str: string;
  site_name: string;
  clean_make: string;
  clean_model: string;
  registration_year: number | null;
  mileage: number | null;
  /** Цените са JSON числа, никога низове. */
  public_price_eur?: number | null; // Може да бъде пропуснато според правата за полета.
  thumbnail_url: string | null;
}

async function search(params: Record<string, string | number>): Promise<SearchResponse> {
  const url = `${BASE}/api/search?${new URLSearchParams(
    Object.entries(params).map(([k, v]) => [k, String(v)]),
  )}`;

  const res = await fetch(url, {
    headers: {
      "X-API-Key": process.env.THECARAPI_KEY!,
      "Accept-Encoding": "gzip",
    },
  });

  if (!res.ok) {
    throw new Error(`${res.status} — request ${res.headers.get("X-Request-ID")}`);
  }
  return res.json();
}

const page = await search({ brand: "bmw", fuel: "Diesel", limit: 24 });
console.log(page.results.length, "of", page.total);

Python

python
import os
import requests

BASE = "https://api.thecarapi.com"

session = requests.Session()
session.headers.update({"X-API-Key": os.environ["THECARAPI_KEY"]})
# requests договаря gzip и разкомпресира прозрачно.

response = session.get(
    f"{BASE}/api/search",
    params={"brand": "bmw", "fuel": "Diesel", "year_from": 2018, "limit": 24},
    timeout=30,
)
response.raise_for_status()
payload = response.json()

for car in payload["results"]:
    print(car["site_name"], car["auction_id_str"], car.get("public_price_eur"))

PHP

php
<?php
$client = new GuzzleHttp\Client([
    'base_uri'       => 'https://api.thecarapi.com',
    'headers'        => ['X-API-Key' => getenv('THECARAPI_KEY')],
    'decode_content' => 'gzip',   // На Guzzle това трябва да се каже изрично
    'timeout'        => 30,
]);

$response = $client->get('/api/search', [
    'query' => ['brand' => 'bmw', 'fuel' => 'Diesel', 'limit' => 24],
]);

$payload = json_decode((string) $response->getBody(), true);
foreach ($payload['results'] as $car) {
    printf("%s/%s — €%s\n", $car['site_name'], $car['auction_id_str'], $car['public_price_eur'] ?? 'unavailable');
}

Рецепта: обхождане на набор от резултати

python
def iter_results(session, **filters):
    """Връща всяка карта, като спазва политиката за дълбочина, вместо да я гадае."""
    offset, limit = 0, 100
    while True:
        r = session.get(f"{BASE}/api/search",
                        params={**filters, "limit": limit, "offset": offset,
                                "include_total": "false"},
                        timeout=30)
        r.raise_for_status()
        rows = r.json()["results"]
        if not rows:
            return
        yield from rows
        offset += limit
        # /api/search и /api/listVehicles нямат ограничение на отместването. Каталогът,
        # топ офертите и обявите спират на 5000, а твърде дълбока страница
        # отговаря с 400. Четете странирането от /api/contract.
include_total=false пропуска преброяването, което е скъпата половина на дълбокото търсене.

Рецепта: проверка на страница от търсенето без повторно изтегляне

javascript
let etag = null;
let cached = null;

async function poll(url) {
  const res = await fetch(url, {
    headers: {
      "X-API-Key": process.env.THECARAPI_KEY,
      ...(etag ? { "If-None-Match": etag } : {}),
    },
  });

  if (res.status === 304) return cached;   // нищо не се е променило, не е прехвърлено тяло

  if (!res.ok) throw new Error(`HTTP ${res.status}`);

  etag = res.headers.get("ETag");          // непрозрачен — върнете го точно
  cached = await res.json();
  return cached;
}

Рецепта: страница с детайли за автомобил с една заявка

javascript
const { auction } = await getJson(`/api/auction/${site}/${id}`);

const photos = auction.vault_gallery?.images ?? [];
const stillComing = auction.vault_gallery?.pending ?? 0;   // покажете контейнери

const price = auction.public_price_eur;                     // вече актуална, ако е възможно
const asOf = auction.live_price
  ? new Date(auction.live_price.fetched_at * 1000)          // секунди → ms
  : null;

// Само ако обновяването е изпуснало бюджета на заявката, прочетете точно още веднъж.
if (!auction.live_price && auction.live_price_pending) {
  setTimeout(() => refetch(site, id), 2000);
}
vault_gallery е същото тяло, което връща /api/auction-images/{site}/{id}, така че втората заявка не е нужна.