How I built a car dealership with AI — the CARFAST.cz architecture

I built a complete car dealership without a single framework and without npm install.
No Laravel. No Symfony. No React. No Webpack. Vanilla PHP 8.3, MySQL, plain CSS and JavaScript. Deploy? Upload the files over FTP and it's live.
And it works. CARFAST.cz is a Czech platform for importing and selling verified cars from the EU — and under the hood, it's the most interesting project I've built with Claude.
Why a custom stack?
Three reasons:
Deployment simplicity. No build step, no CI/CD pipeline, no npm run build that takes 3 minutes. I upload the files → it runs. On any PHP hosting for 50 CZK a month.
Full control. Every line of code is mine. No magical ORM, no dependency hell, no "why did this break after npm update." When something isn't working, I know exactly where to look.
Development speed with AI. Paradoxically — a custom framework + Claude is faster than a framework with documentation. Why? Because Claude understands my code better than any third-party abstraction. I say "add a new controller for the import calculator" and Claude sees what the other controllers look like, what structure the routes use, how the view system works. Zero guesswork.
Architecture
index.php → Router → Controller → Model → View
A custom MVC framework with a PSR-4 autoloader. A request comes in, the Router matches the URL to a controller action, which reaches into the model for data and renders a template inside a layout.
60+ routes with one trick that took me longest: automatic translation of URL segments into 4 languages.
/vozy/detail/skoda-octavia (Czech)
/cars/detail/skoda-octavia (English)
/autos/detail/skoda-octavia (German)
/auto/detail/skoda-octavia (Dutch)
It's not just a /cs/vozy prefix — the Router actually translates whole segments. Cleaner URLs, better SEO, more natural for users.
Security handled from the start:
- CSRF auto-validation on every POST request
- PDO prepared statements everywhere (no SQL concatenation, ever)
htmlspecialchars()on every output- Bcrypt passwords + brute-force protection for admin login
Where Claude does the heavy lifting
This is the interesting part. Claude isn't just a "website chatbot" — it's integrated directly into the app's core via a central ClaudeService.
One wrapper, five use cases
ClaudeService is a singleton that wraps all communication with the Anthropic API:
- Rate limiting — sliding window, max 30 requests per minute
- Cost tracking — every call logs input/output tokens + price in USD into the
ai_usage_logtable - Retry logic — exponential backoff on transient errors
I know exactly how much AI costs per month. Every cent is tracked.
1. Importing cars from unknown portals
The user pastes a listing URL or the whole text → Claude extracts structured data: price, year of manufacture, kilometers, fuel, equipment, condition... Preview in admin → you confirm → saved in the database.
This is a killer feature. Instead of manually retyping 30 fields from a listing, Claude handles it in 3 seconds. And he understands even poorly formatted listings from Bazoš.
2. Batch description generation
Claude writes an SEO-friendly Czech description from the car's parameters. DeepL then automatically translates it into English, German, and Dutch.
Before: 20 minutes per description × 4 languages = 80 minutes per car. Now: 10 seconds of generation + 5 seconds of translation = 15 seconds per car.
3. AI Watchdog
This is the one I enjoy most. Claude semantically matches new listings against the configured criteria. Each listing gets a score of 0.0–1.0, and at ≥0.75 a notification comes in.
Looking for an Octavia under 200 thousand km for under 300 thousand CZK? The watchdog finds relevant offers before you even get to refresh Sauto.
4. Admin assistant
A conversational AI right inside the admin panel. It has context — it knows how many cars are on offer, how many leads are unresolved, what the daily traffic looks like. You ask "how many cars did we add this week?" and you get an answer without clicking through the dashboard.
5. FAQ translation
A Claude + DeepL chain for generating answers in 4 languages. You write the FAQ in Czech → Claude improves the phrasing → DeepL translates → done in all languages.
Scraping from 7 portals
Custom scrapers for:
- mobile.de (Germany, Cloudflare protection)
- AutoScout24 (EU)
- 2dehands.be (Belgium)
- Sauto.cz (Czechia)
- TipCars (Czechia)
- AAA Auto (Czechia)
- Bazoš.cz (Czechia)
Each portal has its own scraper, because each has different HTML, different API endpoints, and different protection.
Defense against blocking:
- Rotation of 6 user agents (mobile + tablet UAs)
- Exponential backoff retry (1s, 3s, 9s)
- Proxy via ScraperAPI for Cloudflare-protected sites (mobile.de is hardcore)
- Graceful degradation — partial data is better than none
Batch import: Paste up to 20 URLs at once → the system recognizes the portal by domain → scrape → preview → save + automatic translation into all languages.
4 languages everywhere
The translation system combines three approaches:
- UI texts —
t('key')function, translations in the database - Entity translations —
te('group', 'key', id)for car descriptions, FAQ, blog - URL translation — Router translates whole URL segments
Language detection: URL prefix → cookie → Accept-Language header → default (Czech).
Cache: Every translation is hashed (SHA256) and stored in the translation_cache table. The same text isn't translated twice — it saves DeepL API calls.
Page Builder with 18 templates
Pages are composed of sections stored as JSON in the database:
hero_search, hero_simple, cars_featured, categories, car_banner, counters, services, steps, cta, testimonials, faq, newsletter, blog_preview, contact_form, calculator, cars_listing, blog_listing, text, legal
Drag-and-drop ordering in admin. Each section has its own JSON schema for content. Translations overlay via the database — one JSON content, N languages.
The result: the marketing team (if I had one) can build landing pages without a developer.
Admin panel — 16 sections
Dashboard, Cars, Import, Watchdog, Buyouts, Blog, Pages, Navigation, Settings, Backups, Translations, Leads, Newsletter, Content, FAQ, AI Assistant.
CRM pipeline: new → contacted → offer → negotiation → conversion. Kanban board directly in admin.
Car buyouts: Inspection checklist, offers, documents — the whole workflow.
Backups: Database export + uploads as a ZIP, one-click restore.
Numbers
| Metric | Value | |---------|---------| | Routes | 60+ | | Portal scrapers | 7 | | Page builder templates | 18 | | Languages | 4 | | Email templates | 7 | | AI use cases | 5 | | Admin sections | 16 | | External dependencies | 0 |
Zero external dependencies. No Composer. No npm. No Webpack. The whole application is self-contained PHP.
What I learned
1. Zero dependencies is freedom. No breaking change in a library, no security patch that breaks the build. You control the whole stack.
2. AI cost tracking is a must. Without logging, I'd have no clue how much Claude costs. With logging, I know precisely: importing one car = ~$0.03. A watchdog scan of 100 listings = ~$0.15. Monthly AI costs: under $20.
3. Cascading systems work. Email: Brevo API → PHP mail(). Config: DB → file → .env. Import: scraper → AI fallback. Always have a plan B.
4. Claude understands custom code better than frameworks. Because my code is straightforward PHP without magic. Claude doesn't have to search third-party documentation — he sees the router, controller, model, and knows what to do.
What's next
CARFAST.cz is ready for modular expansion — each dealership = its own instance on its own domain. Shared core, custom branding, own domain.
On the roadmap:
- Chatbot module for customers (Claude answers questions about cars)
- Automatic price analysis (comparison against the market)
- Mobile app (PWA)
And all of it without npm install. Because why not.