Quick answer: Cars.bg publishes no public API, so structured used-car data requires scraping the public listings. The Cars.bg Bulgaria Car Scraper on Apify handles browser fingerprint rotation, residential proxy rotation, and retries for you — returning clean typed rows with dual EUR and BGN pricing at $2.05 per 1,000 results ($0.05 actor-start + $0.002 per result).
Bulgaria's used-car market has a quirk that sets it apart from most of Western Europe: prices appear in both euros and Bulgarian lev (BGN) on every single listing. Bulgaria is an EU member but retains the lev — pegged at a fixed rate of 1.9558 BGN per EUR — and the marketplace convention is to show both. If you are building a pricing model or running arbitrage across EU markets, you need both currency fields in your dataset.
Cars.bg is Bulgaria's largest automotive marketplace with no public API and no structured data export. Scraping it is not trivial: the site uses a modern JavaScript frontend with an internal API, and its detail pages are guarded more aggressively than the listing cards — requiring residential proxy rotation and careful session management to retrieve reliably.
This guide covers how to pull Cars.bg data programmatically using the Cars.bg Bulgaria Car Scraper, a managed Apify Actor that handles the infrastructure.
Does Cars.bg Have an API? 🔎
No. Cars.bg offers no developer API, no data export, and no documented structured feed. The site's listing data is rendered via an internal API that the frontend calls with session-scoped parameters — not something you can call stably from external code.
One important distinction from other scrapers in this fleet: Cars.bg's detail pages are guarded more aggressively than its listing cards. With the default enrichment-off setting, you can pull make, model, year, price (EUR + BGN), mileage, fuel type, location, seller type, and photos from listing cards alone — which is the fastest and most reliable path. Detail-page enrichment (gearbox, body type, engine spec, colour, full description) is available but requires a larger proxy pool to avoid rate-limiting.
What Data You Can Extract
The Actor returns one row per listing. Fields from the listing card are always present (enrichment off is the default). Fields marked "enrichment-only" require enrichDetails: true.
| Field | Description |
|---|---|
listing_id |
Cars.bg listing reference ID |
listing_url |
Absolute URL to the listing detail page |
title |
Make + model from listing title |
make |
Manufacturer (e.g. Mazda) |
model |
Model (e.g. 6) |
year |
Manufacture year |
price |
Integer price in EUR |
currency |
Always EUR
|
price_bgn |
Price in Bulgarian lev (BGN) as shown on the listing |
mileage_km |
Odometer in kilometres |
fuel_type |
Дизел / Бензин / Електрически (Bulgarian Cyrillic) |
transmission |
Автоматични / Ръчни скорости — enrichment-only |
engine_power_hp |
Horsepower (к.с.) — enrichment-only |
engine_size_cc |
Displacement in cc — enrichment-only |
body_type |
Комби / Седан / Джип, etc. — enrichment-only |
color |
Exterior colour — enrichment-only |
first_registration |
First-registration month/year, e.g. "Март 2009" — enrichment-only |
location |
Seller city |
seller_type |
dealer or private
|
seller_name |
Seller display name — enrichment-only |
photo_urls |
List of photo URLs |
description |
Free-text seller description — enrichment-only |
posted_date |
Relative posting time from listing card (e.g. "днес, 01:44") |
scraped_at |
ISO-8601 UTC timestamp of scrape |
A note on language: Cars.bg is a Bulgarian-language site. Fuel type, gearbox, and body type values are in Bulgarian Cyrillic. The Actor maps all field names to English column names — your schema is consistent — but the values are preserved as-is. Дизел stays Дизел. This is correct behaviour: silent translation introduces errors for uncommon values.
A Realistic Output Row
Here is a sample result using the exact field names from the Actor's Pydantic ResultRow model, with enrichment enabled:
{
"listing_id": "68eb6bba3c8c0e169c027093",
"listing_url": "https://www.cars.bg/offer/68eb6bba3c8c0e169c027093",
"title": "Mazda 6 2,2MZR-CD",
"make": "Mazda",
"model": "6 2,2MZR-CD",
"year": 2009,
"price": 2850,
"currency": "EUR",
"price_bgn": 5574,
"mileage_km": 308000,
"fuel_type": "Дизел",
"transmission": "Ръчни скорости",
"engine_power_hp": 185,
"engine_size_cc": 2200,
"body_type": "Комби",
"color": "Светло сив металик",
"first_registration": "Март 2009",
"location": "Монтана",
"region": null,
"seller_type": "private",
"seller_name": "Aleksandr",
"photo_urls": ["https://g1-bg.cars.bg/2025-10-12_1/68eb68ec14f9749b22054f64b.jpg"],
"description": "Продавам Mazda 6 комби (GH), 2009 г. ...",
"posted_date": "днес, 01:44",
"scraped_at": "2026-06-02T10:00:00+00:00"
}
Notice both price (2,850 EUR) and price_bgn (5,574 BGN) — both come directly from the listing card, so they are available even without detail enrichment.
How We Handle the Blocks
Cars.bg is a modern JavaScript marketplace. Its listing cards and detail pages behave differently under automated traffic — the site's defences are more aggressive on detail pages than on search results. Here is what the Actor does:
-
Browser fingerprint rotation. We use
curl-cffito impersonate real Chrome, Firefox, and Safari TLS handshakes. The server sees a browser, not Python. - Residential proxy rotation via Apify Proxy. We rotate residential exit IPs and fresh session IDs on every block signal. Datacenter IPs are a reliable path to a 403 on Cars.bg detail pages.
-
Exponential backoff with Retry-After. We retry on 408, 429, and 5xx — up to five attempts per page, honouring the server's own
Retry-Afterheader. - Rate-limit-aware pacing. When the target slows us, we match its pace rather than pushing through.
- Loud failure, never silent. A hard block produces a non-zero exit with a descriptive status message. Empty datasets with green statuses are not acceptable.
The default configuration has enrichDetails: false — not because the detail pages are off-limits, but because detail enrichment requires a larger proxy pool to run reliably, and we want the default run to succeed predictably.
Running It with Python 🐍
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("DevilScrapes/carsbg-bulgaria-cars").call(
run_input={
"searchUrl": "https://www.cars.bg/carslist.php?subtype=1&brand=16",
"maxResults": 200,
"enrichDetails": False,
"proxyConfiguration": {
"useApifyProxy": True,
"apifyProxyGroups": ["RESIDENTIAL"]
}
}
)
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())
print(f"Scraped {len(items)} listings")
for item in items[:3]:
print(item["make"], item["model"], item["year"],
item["price"], "EUR /", item["price_bgn"], "BGN")
The searchUrl is optional. Leave it empty to scrape the default used-cars feed, or visit cars.bg, apply your filters (brand, body type, price band, year range), and paste the resulting URL. The Actor follows pagination from there.
Use Cases
EUR and BGN price analytics. Track asking prices in both currencies across the Bulgarian market. Since the lev is pegged to the euro, the BGN field is a sanity check for data quality rather than a currency risk surface — but having both is useful when joining against Bulgarian financial data sources that use one or the other.
Cross-border EU arbitrage. Bulgaria is one of the more affordable used-car markets in the EU. Compare Cars.bg EUR prices against German, French, or Romanian equivalents to identify import opportunities. The mileage_km field is already normalised to kilometres across the fleet.
Dealer intelligence. Filter by seller_type = dealer and location to track inventory in specific Bulgarian cities. Sofia, Plovdiv, and Varna account for most of the dealer volume.
Market research. Aggregate fuel_type across the full dataset to measure diesel vs. petrol vs. electric adoption in the Bulgarian market — one of the later EU adopters of EV technology.
Lead generation. Build a directory of Bulgarian dealers from seller_name and location. Diff successive runs to score dealers by listing volume and price-cut frequency.
FAQ ❓
How much does it cost?
Pay-Per-Event pricing: $0.05 for actor-start (one-off per run) plus $0.002 per result row. 1,000 results costs $2.05 total. Every new Apify account gets $5 of free credit — no credit card required to try.
Should I enable detail enrichment?
With enrichDetails: false (the default), you get make, model, year, price (EUR + BGN), mileage, fuel type, location, seller type, and photos from the listing card alone. That is enough for most pricing and market research use cases. Enable enrichDetails: true to add gearbox, engine displacement, engine power, body type, colour, first-registration month, full description, and seller name — but note that Cars.bg guards detail pages more aggressively, so enrichment performs best on larger proxy tiers.
Is scraping Cars.bg legal?
Cars.bg's terms restrict automated access. You are responsible for ensuring your use complies with applicable law and the site's terms. The Actor scrapes only public listing data visible to any unauthenticated browser. We do not bypass authentication or access private user data.
What currency does Cars.bg use?
Cars.bg shows each price in both EUR and BGN. The price field is the integer EUR amount and currency is always EUR. The price_bgn field carries the BGN amount shown alongside it on the listing card.
Get Started
The Actor is live on the Apify Store. Every new Apify account starts with $5 of free credit — enough for roughly 2,400 enriched listings or around 2,400 listing-card-only rows per dollar.
Cars.bg Bulgaria Car Scraper on Apify Store
Questions or edge cases? Use the Actor's Issues tab on Apify Console. We ship fixes weekly and read every report.
Built by Devil Scrapes — a fleet of opinionated public-data Actors. Honest pricing, real engineering, zero fine print.
Top comments (0)