DuLieu.dev API

Vietnamese Economic Data — REST API Documentation

Authentication

All API requests require an API key. Sign in with Google at /settings to create one.

Plans

Free(Free)— Basic access to Vietnamese economic data
Pro($9/mo)— Full access including grocery price data
Unlimited(Contact us)— No limits — for internal use and partners
FeatureFreeProUnlimited
Economic data (exchange, gold, fuel, coffee...)
Interest rates (40+ banks)
CPI, inflation, economic indicators
Oil & coffee international futures
AI agent daily summary
Grocery prices (40K+ products)
COICOP classification & unit pricing
CSV export
Daily API calls10010,000Unlimited

API Key (Header)

Authorization: Bearer dl_your_api_key_here

API Key (Query)

GET /api/exchange?api_key=dl_your_api_key_here

Rate Limit Headers

X-RateLimit-Plan: free | pro | unlimited
X-RateLimit-Limit: 100 (or "unlimited")
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1714003200

Base URL

https://dulieu.dev

Response Format

{
  "data": [...],
  "meta": {
    "source": "domain.com",
    "count": 42,
    "updated_at": "2026-04-23T14:00:00.000Z"
  }
}

Endpoints

78 endpoints
GET/api/exchange

Currency exchange rates (VND). Sources: Vietcombank, CafeF.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)

Response

{ "data": [{ "date": "2026-04-23", "currency": "USD", "buy": 25000, "sell": 25500, "transfer": 25200, "source": "vietcombank.com.vn", "bank": "VCB" }], "meta": { "source": "vietcombank.com.vn", "count": 9, "updated_at": "..." } }
GET/api/gold

Gold prices by type (SJC, rings, jewelry). Sources: DOJI, CafeF.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)

Response

{ "data": [{ "date": "2026-04-23", "type": "SJC 1L-10L", "buy_price": 92000000, "sell_price": 94000000, "unit": "tael", "source": "giavang.doji.vn" }], "meta": { "source": "giavang.doji.vn", "count": 8 } }
GET/api/silver

Silver prices (11 types). Source: CafeF.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)

Response

{ "data": [{ "date": "2026-04-23", "type": "Bạc miếng SJC", "buy_price": 1200000, "sell_price": 1400000, "unit": "tael" }], "meta": { "source": "cafef.vn" } }
GET/api/weather

Weather data for 70+ Vietnamese cities (all province capitals + tourist destinations).

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)
citystringFilter by city name (partial match)

Response

{ "data": [{ "date": "2026-05-21", "city": "Hà Nội", "temperature": 28.5, "humidity": 65, "conditions": "mây thưa", "geo_location_id": 71 }] }
GET/api/weather/forecast

5-day weather forecast for 70+ Vietnamese cities. Source: OpenWeatherMap.

Query Parameters

datestringFilter by target_date (YYYY-MM-DD)
fromstringStart target_date (YYYY-MM-DD)
tostringEnd target_date (YYYY-MM-DD)
citystringFilter by city name (partial match)

Response

{ "data": [{ "forecast_date": "2026-05-21", "target_date": "2026-05-22", "city": "Đà Nẵng", "temp_min": 26.0, "temp_max": 33.0, "humidity": 70, "conditions": "mây thưa" }] }
GET/api/weather/summary

Today's weather summary for all cities.

Response

{ "data": [{ "city": "Hà Nội", "temperature": 28.5, "temp_unit": "°C", "humidity": 65, "humidity_unit": "%", "conditions": "mây thưa" }] }
GET/api/fuel

Fuel prices (6 types, zone 1 & 2). Source: webgia.com.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)

Response

{ "data": [{ "date": "2026-04-23", "fuel_type": "RON 95-V", "zone": 1, "price": 23800 }] }
GET/api/coffee

Coffee prices by region. Source: webgia.com.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)

Response

{ "data": [{ "date": "2026-04-23", "region": "Đắk Lắk", "price": 125000, "unit": "VND/kg" }] }
GET/api/interest

Bank deposit & lending rates (28+ banks, 8 terms). Sources: webgia, VCB, BIDV, CafeF.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)

Response

{ "data": [{ "date": "2026-04-23", "bank": "Vietcombank", "term_months": 12, "deposit_rate": 4.7, "lending_rate": null }] }
GET/api/indicators

Economic indicators (CPI, inflation, IIP). Source: nso.gov.vn.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)

Response

{ "data": [{ "indicator": "CPI", "value": 103.2, "period": "2026-03", "yoy_change": 3.2 }] }
GET/api/lottery

Vietnam lottery results (north/central/south, 41 provinces). Sources: CafeF + Minh Ngọc.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)
sourcestringFilter by source (cafef.vn or minhngoc.net.vn)

Response

{ "data": [{ "date": "2026-04-23", "region": "south", "province": "TP.HCM", "prize": "Giải Đặc biệt", "numbers": "123456" }] }
GET/api/pepper

Pepper prices — domestic (5 regions) + international (India Kochi). Source: giatieu.com.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)

Response

{ "data": [{ "date": "2026-05-20", "region": "Đắk Lắk", "price": 148000, "unit": "VND/kg" }] }
GET/api/holidays/vn

Vietnamese public holidays (Bộ luật Lao động 2019, Art. 112). Computed from lunar calendar + annual govt decree overrides. Includes nghỉ bù (observed days), hoán đổi (swap days), and Ngày Văn hóa VN (from 2026). 11 statutory days/year (12 from 2026).

Query Parameters

yearnumberYear (1900–2100, default: current year)
datestringCheck if a specific date is a holiday (YYYY-MM-DD)
fromstringStart date for range query (YYYY-MM-DD)
tostringEnd date for range query (YYYY-MM-DD)

Response

{ "year": 2026, "count": 14, "holidays": [{ "date": "2026-01-01", "name": "Tết Dương lịch", "nameEn": "New Year's Day", "type": "solar" }, { "date": "2026-02-17", "name": "Tết Nguyên Đán (Mùng 1)", "nameEn": "Tết (Lunar New Year, Day 1)", "type": "lunar" }] }
GET/api/commodities

All commodity prices — crude oil (WTI, Brent), international coffee futures.

Query Parameters

datestringSingle date (YYYY-MM-DD)
fromstringStart date (YYYY-MM-DD)
tostringEnd date (YYYY-MM-DD)
symbolstringFilter by symbol (e.g. WTI, BRENT, ROBUSTA)

Response

{ "data": [{ "date": "2026-05-20", "symbol": "WTI", "name": "WTI Crude Oil", "price": 72.50, "unit": "USD/barrel" }] }
GET/api/groceries

Grocery prices (56K+ products, COICOP classified, unit pricing). Pro plan required.

Query Parameters

storestringFilter by store (e.g. lottemart, cooponline)
categorystringFilter by category
limitnumberMax results (default: 500)

Response

{ "data": [{ "store": "lottemart", "name": "Sữa TH True Milk 1L", "price": 32000, "coicop": "01.1.4", "unit_price_per_l": 32000 }] }
GET/api/groceries/summary

Grocery category averages with COICOP codes. FREE — aggregate only, no product rows (the public dashboard reads it anonymously).

Response

{ "data": [{ "category": "Sữa", "coicop": "01.1.4", "products": 450, "avg_price": 85000, "stores": 4 }] }
GET/api/groceries/stores

Store product counts and latest crawl dates. FREE — aggregate only.

Response

{ "data": [{ "store": "lottemart", "products": 26610, "latest_date": "2026-05-20" }] }
GET/api/groceries/catalog

Full deduplicated grocery catalog — the latest price per product (source_id) across ALL crawl dates, not just the latest crawl slice. For sync consumers that need every product at its current price (the plain /groceries with date=latest only returns the newest crawl run). Pro plan required.

Query Parameters

storestringFilter by store (e.g. kingfoodmart, lottemart)
categorystringFilter by category
limitnumberMax results (default: 200000)

Response

{ "data": [{ "store": "kingfoodmart", "source_id": "kingfoodmart:8938512909042", "name": "Sữa TH True Milk 1L", "price": 32000, "date": "2026-06-14" }] }
GET/api/groceries/search

Live product-name search (suggestions / autocomplete). Case-insensitive ILIKE on name, deduplicated to the latest price per product, `_raw` stripped — query live instead of syncing the whole catalog into a stale local cache. Pro plan required.

Query Parameters

qstringSearch text (matched against product name; required)
storestringFilter by store
categorystringFilter by category
limitnumberMax results (default: 50)

Response

{ "data": [{ "store": "lottemart", "source_id": "lottemart:8938512909042", "name": "Sữa TH True Milk 1L", "price": 32000, "date": "2026-06-14" }] }
GET/api/groceries/freshness

Per-store crawl freshness — catalog_size, latest_date, days_stale, and a stale flag (≥2 days). Lets sync consumers detect when a store silently stopped updating. Pro plan required.

Response

{ "data": [{ "store": "kingfoodmart", "latest_date": "2026-06-14", "catalog_size": 11609, "crawl_days": 18, "days_stale": 0, "stale": false }] }
GET/api/drugs

Drug & pharmacy prices (12K+ products, COICOP 06 classified). Pharmacity, Long Chau.

Query Parameters

storestringFilter by store (pharmacity, longchau)
limitnumberMax results (default: 500)

Response

{ "data": [{ "store": "pharmacity", "name": "Panadol Extra", "price": 52000, "is_drug": 1, "is_prescription": 0, "coicop": "06.1.1" }] }
GET/api/drugs/summary

Drug category averages with COICOP 06 codes and drug/Rx counts.

Response

{ "data": [{ "category": "Thuốc", "coicop": "06.1.1", "products": 5431, "drugs": 5431, "prescription": 4309 }] }
GET/api/drugs/stores

Pharmacy product counts and latest crawl dates.

Response

{ "data": [{ "store": "pharmacity", "products": 7610, "latest_date": "2026-05-20" }] }
GET/api/drugs/catalog

Full deduplicated drug catalog — latest price per product across all crawl dates (see /groceries/catalog). Pro plan required.

Query Parameters

storestringFilter by store (pharmacity, longchau)
limitnumberMax results (default: 200000)

Response

{ "data": [{ "store": "longchau", "source_id": "longchau:00000450", "name": "Panadol Extra", "price": 52000, "date": "2026-06-14" }] }
GET/api/drugs/search

Live drug-name search (suggestions / autocomplete). ILIKE on name, deduplicated to latest price per product, `_raw` stripped (see /groceries/search). Pro plan required.

Query Parameters

qstringSearch text (matched against product name; required)
storestringFilter by store (pharmacity, longchau)
limitnumberMax results (default: 50)

Response

{ "data": [{ "store": "pharmacity", "source_id": "pharmacity:P03029", "name": "Panadol Cold & Flu", "price": 14400, "date": "2026-06-14" }] }
GET/api/geo

Vietnamese administrative geography (provinces, districts, wards).

Query Parameters

provincestringProvince code or name (partial match)

Response

{ "data": [{ "province_code": "79", "province_name": "TP. Hồ Chí Minh", "district_code": null }] }
GET/api/summary

Daily summary of all data domains. Ideal for AI agent daily briefing. Includes exchange, gold, silver, weather, fuel, coffee, pepper, interest, indicators, MST company-registry KPI, and real-estate top provinces. Supports ?at=T (record-time as-of): each source resolves to its latest crawl date ≤ T with carry-forward fallback (lagged/gappy sources never return empty); response echoes as_of + a resolved{} map of the date each series fell back to.

Query Parameters

atstringRecord-time as-of date (YYYY-MM-DD) or 'now' (default). Carry-forward: returns the latest data ≤ this date.

Response

{ "date": "2026-05-20", "as_of": "2026-05-20", "resolved": { "gold": "2026-05-19", "exchange": "2026-05-20" }, "last_crawl": "...", "exchange": [...], "gold": [...], "weather": [...], "fuel": [...], "coffee": [...], "interest": [...], "indicators": [...], "mst": { "total": 54059, "active": 36000, "ceased": 1200, "added_7d": 36 }, "realestate": [{ "province": "ho-chi-minh", "listings": 1234, "median_price_per_m2_sale": 95000000 }] }
GET/api/snapshot

Record-time cross-source metric snapshot. Returns the nearest stored snapshot with taken_at ≤ T (the as-of record time); falls back to a live summary computation when no stored snapshot exists yet. The `cached` flag indicates which path served the response. Single-source-of-truth for reproducible point-in-time metric reads.

Query Parameters

atstringRecord-time as-of (ISO timestamp/date) or 'now' (default). Returns the most recent snapshot at or before this time.

Response

{ "cached": true, "taken_at": "2026-05-20T07:00:00Z", "as_of_date": "2026-05-20", "kind": "summary", "data": { "exchange": [...], "gold": [...], ... } }
GET/api/stats

API usage statistics — by key, by user, by endpoint, by day. Admin only.

Query Parameters

daysnumberLookback period (default: 7)

Response

{ "overview": { "total": 1234, "unique_keys": 5 }, "by_key": [...], "by_user": [...], "by_endpoint": [...] }
GET/api/crawl-logs

Crawler execution history.

Query Parameters

limitnumberMax results (default: 20)

Response

{ "data": [{ "crawler": "exchange-vietcombank", "status": "success", "records_inserted": 9, "duration_ms": 1200 }] }
GET/api/crawl/progress

Realtime progress of in-flight streaming crawls (BullMQ workers publish live counts to Redis).

Response

{ "count": 1, "data": [{ "crawler": "electronics-cellphones", "status": "running", "phase": "products", "done": 12340, "total": 58853, "ok": 12100, "fail": 240, "inserted": 12100, "pct": 21, "updated_at": "..." }] }
GET/api/bakery

Cake & bakery prices from Vietnamese bakeries (givralbakery.com.vn, cailonuong.com). Carries cake diameter, weight, tier count, flavour and occasion. NOTE: `price` is nullable — a large share of the Givral catalogue is quoted on request ("Liên hệ") rather than listed, and NULL records that rather than hiding it.

Query Parameters

storestringFilter by store (givral, cailonuong)
occasionstringsinh nhật, trung thu, cưới, giáng sinh, valentine…
flavourstringsocola, tiramisu, matcha, phô mai, dâu…
categorystringFilter by category (partial match)
pricednumberSet to 1 to return only rows with a published price
limitnumberMax results (default: 500)

Response

{ "data": [{ "store": "cailonuong", "name": "Túi Hương Sen", "price": 345000, "weight_g": 250, "occasion": "trung thu", "category": "Trung thu" }] }
GET/api/bakery/summary

Cake market summary grouped by occasion — counts, price range, average diameter, and how many products are priced vs quoted on request. Occasion, not flavour, is what drives cake demand and pricing.

Response

{ "data": [{ "occasion": "sinh nhật", "products": 155, "priced": 120, "unpriced": 35, "stores": 2, "avg_price": 420000, "avg_size_cm": 18.5 }] }
GET/api/bakery/catalog

Full deduplicated cake catalog — latest price per product across all crawl dates (see /groceries/catalog).

Query Parameters

storestringFilter by store
occasionstringFilter by occasion
flavourstringFilter by flavour
limitnumberMax results (default: 50000)

Response

{ "data": [{ "store": "givral", "source_id": "givral:52347", "name": "ĐẬU XANH KHÔNG TRỨNG 150g", "price": 107000, "weight_g": 150 }] }
GET/api/bakery/search

Live cake-name search (suggestions / autocomplete). ILIKE on name, deduplicated to latest price per product.

Query Parameters

qstringSearch text (matched against product name; required)
storestringFilter by store
occasionstringFilter by occasion
limitnumberMax results (default: 50)

Response

{ "data": [{ "store": "givral", "name": "Bánh kem socola 20cm", "price": 450000, "size_cm": 20 }] }
GET/api/cosmetics

Perfume & cosmetics prices from Vietnamese fragrance retailers (namperfume.net, orchard.vn, thegioinuochoa.com.vn). One row per sellable SIZE, not per product, with bottle volume, concentration (EDP/EDT/Extrait/…), EAN barcode and VND-per-ml.

Query Parameters

storestringFilter by store (namperfume, orchard, thegioinuochoa)
brandstringFilter by brand (Dior, Chanel, Tom Ford, …)
concentrationstringEDP, EDT, EDC, Parfum, Extrait, Cologne
genderstringnam, nu, unisex
barcodestringExact EAN-13/UPC lookup
categorystringFilter by category (partial match)
limitnumberMax results (default: 500)

Response

{ "data": [{ "store": "thegioinuochoa", "name": "Nuớc hoa Bvlgari Man in Black EDP 100ML", "brand": "Bvlgari", "price": 4650000, "volume_ml": 100, "concentration": "EDP", "gender": "nam", "price_per_ml": 46500, "barcode": "783320974427" }] }
GET/api/cosmetics/summary

Fragrance market summary grouped by concentration — product counts, brand diversity, price range, average VND-per-ml and average discount. Concentration, not category, is what sets a fragrance's price tier.

Response

{ "data": [{ "concentration": "EDP", "products": 1634, "brands": 210, "avg_price": 3300000, "avg_price_per_ml": 41000, "avg_discount_pct": 18.4 }] }
GET/api/cosmetics/compare

Cross-retailer price comparison: products whose EAN barcode appears at more than one store, ranked by the spread between cheapest and dearest. This is what carrying three retailers is for.

Query Parameters

brandstringFilter by brand
limitnumberMax results (default: 200)

Response

{ "data": [{ "barcode": "3348901250153", "brand": "Dior", "volume_ml": 100, "stores": 2, "min_price": 3990000, "max_price": 4650000, "spread": 660000, "spread_pct": 14.2, "offers": [{ "store": "namperfume", "price": 3990000 }] }] }
GET/api/cosmetics/catalog

Full deduplicated perfume catalog — latest price per product across all crawl dates (see /groceries/catalog).

Query Parameters

storestringFilter by store
brandstringFilter by brand
concentrationstringEDP, EDT, EDC, Parfum, Extrait, Cologne
genderstringnam, nu, unisex
limitnumberMax results (default: 50000)

Response

{ "data": [{ "store": "namperfume", "source_id": "namperfume:110100204977", "name": "Yves Saint Laurent Myslf EDT Intense - 100ml", "price": 4200000, "date": "2026-08-09" }] }
GET/api/cosmetics/search

Live perfume-name search (suggestions / autocomplete). ILIKE on name, deduplicated to latest price per product.

Query Parameters

qstringSearch text (matched against product name; required)
storestringFilter by store
brandstringFilter by brand
concentrationstringEDP, EDT, EDC, Parfum, Extrait, Cologne
limitnumberMax results (default: 50)

Response

{ "data": [{ "store": "orchard", "name": "Tom Ford Oud Wood EDP Chiết (Lựa chọn: 10 ml)", "price": 1250000, "volume_ml": 10, "price_per_ml": 125000 }] }
GET/api/electronics

Electronics prices from major Vietnamese retailers — phones, laptops, appliances and PC/components (TGDD, CellphoneS, FPT Shop, Hoang Ha Mobile, Di Dong Viet, Nguyen Kim, Dien May Xanh, GearVN, Tan Doanh, …). 500K+ products with ratings, reviews, variants. Filter by store.

Query Parameters

storestringFilter by store (thegioididong, cellphones, fptshop, hoanghamobile, didongviet)
brandstringFilter by brand
categorystringFilter by category (partial match)
limitnumberMax results (default: 500)

Response

{ "data": [{ "store": "thegioididong", "name": "iPhone 15 Pro Max", "price": 29990000, "brand": "Apple", "rating": 4.5, "in_stock": 1 }] }
GET/api/electronics/summary

Electronics store-level summary — product counts, avg prices, brand/category diversity, ratings.

Response

{ "data": [{ "store": "thegioididong", "products": 9000, "avg_price": 8500000, "categories": 15, "brands": 120, "avg_rating": 4.2 }] }
GET/api/electronics/stores

Electronics store product counts and latest crawl dates.

Response

{ "data": [{ "store": "cellphones", "products": 58000, "crawl_days": 15, "latest_date": "2026-05-20" }] }
GET/api/electronics/catalog

Full deduplicated electronics catalog — latest price per product across all crawl dates (see /groceries/catalog).

Query Parameters

storestringFilter by store (thegioididong, cellphones, fptshop, …)
brandstringFilter by brand
categorystringFilter by category (partial match)
limitnumberMax results (default: 200000)

Response

{ "data": [{ "store": "fptshop", "source_id": "fptshop:0000056", "name": "iPhone 15 Pro Max", "price": 29990000, "date": "2026-06-14" }] }
GET/api/electronics/search

Live product-name search (suggestions / autocomplete). ILIKE on name, deduplicated to latest price per product, `_raw` stripped (see /groceries/search).

Query Parameters

qstringSearch text (matched against product name; required)
storestringFilter by store
brandstringFilter by brand
categorystringFilter by category (partial match)
limitnumberMax results (default: 50)

Response

{ "data": [{ "store": "fptshop", "source_id": "fptshop:00677914", "name": "iPhone 15 Pro Max", "price": 29990000, "date": "2026-06-14" }] }
GET/api/realestate

Real-estate listings from batdongsan.com.vn + guland.vn + nhatot.com — sale & rent, all property types (apartment, house, villa, street-front, land, project land, warehouse, office, …). First geo focus: Hồ Chí Minh, Lâm Đồng, Đà Nẵng, Hà Nội, Quảng Ngãi. Daily newest-first snapshot. price_unit ∈ {total, per_month, per_m2, per_m2_month, negotiable}.

Query Parameters

transactionstringban (sale) | cho-thue (rent)
typestringProperty type slug (e.g. can-ho-chung-cu, nha-rieng, nha-mat-pho, dat, van-phong). Alias: property_type
provincestringProvince key (ho-chi-minh, lam-dong, da-nang, ha-noi, quang-ngai)
districtstringFilter by district (partial match)
sourcestringSource filter (guland.vn | batdongsan.com.vn)
qstringKeyword search over title, district, location (accent-insensitive)
min_pricenumberMin price_value in VND
max_pricenumberMax price_value in VND
min_areanumberMin area in m²
max_areanumberMax area in m²
has_imagestringSet to 1 to return only listings with images
date / from / tostringDate range filter (YYYY-MM-DD)
limitnumberMax results (default: 500)
offsetnumberPagination offset (default: 0)

Response

{ "data": [{ "listing_id": "45785289", "transaction": "ban", "property_type": "nha-rieng", "province": "ho-chi-minh", "district": "Quận 7", "title": "...", "price_value": 9900000000, "price_unit": "total", "area": 75, "price_per_m2": 132000000, "url": "...", "image": "...", "cached_image": "...", "images": ["https://cdn.chotot.com/...1.jpg", "https://cdn.chotot.com/...2.jpg"] }], "meta": { "total": 12345 } }
GET/api/realestate/by-id/:source_id

Single listing lookup by source_id. Returns the latest crawled snapshot of the listing with images array. source_id format: {source}:{identifying_fields} (URL-encode the value).

Query Parameters

source_idstringThe listing's source_id (URL path param, URL-encoded)

Response

{ "data": { "source_id": "guland.vn:ban:12345", "transaction": "ban", "property_type": "nha-rieng", "province": "ho-chi-minh", "title": "...", "price_value": 5000000000, "url": "...", "image": "...", "cached_image": "...", "images": ["https://bizcdn.guland.vn/...1.jpg", "https://bizcdn.guland.vn/...2.jpg"] } }
GET/api/realestate/suggest

Location typeahead / autocomplete for real-estate search. Accent-insensitive prefix/substring match over province, district, and ward names with listing counts. Backed by a cached rollup (refreshed every 10 minutes), <100ms response. Results ranked: prefix matches first, then by listing_count descending.

Query Parameters

qstringSearch prefix/substring (accent-insensitive, e.g. 'ha noi', 'quan 7', 'binh thanh')
limitnumberMax results (default 20, max 100)

Response

{ "data": [{ "type": "province", "name": "Hà Nội", "province": "Hà Nội", "slug": "ha-noi", "listing_count": 533603 }, { "type": "district", "name": "Quận 7", "province": "Hồ Chí Minh", "district": "Quận 7", "slug": "quan-7", "listing_count": 12450 }], "meta": { "count": 15 } }
GET/api/realestate/facets

Distinct filter values with counts for real-estate listings. Returns provinces, property_types, sources, and transactions. When province is specified, also returns districts and wards arrays for dependent dropdowns. Supports cross-filtering — pass any combination of filters to get counts scoped to the selection.

Query Parameters

transactionstringban (sale) | cho-thue (rent)
typestringProperty type slug
provincestringProvince key (e.g. ha-noi, ho-chi-minh). When set, response includes districts and wards arrays.
sourcestringSource filter (guland.vn | batdongsan.com.vn | nhatot.com)
date / from / tostringDate range filter (YYYY-MM-DD)

Response

{ "data": { "provinces": [{ "province": "ha-noi", "count": 533603 }], "property_types": [{ "property_type": "nha-o", "type_name": "Nhà ở", "count": 211060 }], "sources": [{ "source": "guland.vn", "count": 737081 }], "transactions": [{ "transaction": "ban", "count": 1044712 }], "districts": [{ "district": "Quận 7", "count": 12450 }], "wards": [{ "ward": "Tân Kiểng", "count": 3200 }] } }
GET/api/realestate/sources/stats

Per-source organic-activity stats for ranking BĐS sources: all-time distinct listings (total), freshness (last_crawled), NEW-listing counts by first-seen window (added_24h/7d/<days>d — the 'new posts per N days' analog) + per_day rate, and sale/rent + property-type skew on the latest crawl-date snapshot. Read-only aggregate.

Query Parameters

daysnumberFirst-seen window in days for added_<days>d + per_day (default 30, max 365)

Response

{ "data": [{ "source": "guland.vn", "total": 230472, "rows": 942816, "last_crawled": "2026-06-13", "last_fetched_at": "2026-06-13T03:34:01Z", "added_24h": 114, "added_7d": 215705, "added_30d": 230472, "per_day": 7682.4, "by_transaction": { "ban": 30000, "cho-thue": 1200 }, "by_property_type": [{ "property_type": "nha-rieng", "type_name": "Nhà ở", "count": 12000 }] }], "meta": { "window_days": 30 } }
GET/api/realestate/summary

Real-estate market snapshot grouped by province × transaction × property type. Median price & price/m² are the headline (robust to outlier/garbage rows); avg_* are trimmed to a sane band. Covers all 63 provinces.

Query Parameters

provincestringFilter by province key

Response

{ "data": [{ "province": "ho-chi-minh", "transaction": "ban", "property_type": "can-ho-chung-cu", "listings": 100, "median_price": 4200000000, "median_price_per_m2": 95000000, "avg_price": 4500000000, "avg_price_per_m2": 65000000, "avg_area": 72 }] }
GET/api/realestate/stores

Real-estate listing counts & latest crawl date by province × transaction.

Response

{ "data": [{ "province": "da-nang", "transaction": "cho-thue", "listings": 200, "crawl_days": 5, "latest_date": "2026-06-04" }] }
GET/api/realestate/trends

Daily real-estate market trend — listings count, avg & median price/m², avg price & area over time. Filter by province/transaction/type.

Query Parameters

daysnumberLookback window (default: 30)
provincestringProvince key filter
transactionstringban | cho-thue
typestringProperty type slug

Response

{ "data": [{ "date": "2026-06-04", "listings": 78, "avg_price": 1741372077, "avg_price_per_m2": 13745484, "median_price_per_m2": 11110000, "avg_area": 456 }] }
GET/api/realestate/movers

Listings whose price changed between their two most recent crawl snapshots — sorted by absolute change. Populates once ≥2 crawl days exist.

Query Parameters

provincestringProvince key filter
limitnumberMax results (default: 50)

Response

{ "data": [{ "listing_id": "...", "prev_price": 4500000000, "curr_price": 4200000000, "pct_change": -6.7, "prev_date": "2026-06-04", "curr_date": "2026-06-05", "url": "..." }] }
GET/api/realestate/planning

Guland planning/zoning map (quy hoạch) tile layers per province — land-use planning 2021-2030 (land-2030), general planning (qh-chung) & base land layer. Each layer carries an {z}/{x}/{y} XYZ tile URL template (renderable in Leaflet/Mapbox), max zoom, source year & GSO admin codes.

Query Parameters

provincestringProvince key filter (ho-chi-minh, lam-dong, da-nang, ha-noi, quang-ngai)

Response

{ "data": [{ "province": "ho-chi-minh", "province_code": "79", "layer_key": "land-2030", "layer_name": "Quy hoạch sử dụng đất 2021-2030", "layer_type": "xyz", "tile_url_template": "https://l5cfglaebpobj.vcdn.cloud/tp-ho-chi-minh-2030/{z}/{x}/{y}.png", "max_zoom": 18, "source_year": 2030 }] }
GET/api/realestate/benchmarks

Area-level price benchmarks — median & avg price-per-m², avg price & area aggregated from crawled listings per source × province × district × transaction × property type, per crawl date.

Query Parameters

provincestringProvince key filter
transactionstringban | cho-thue
sourcestringSource filter (guland.vn | batdongsan.com.vn)
limitnumberMax results (default: 200)

Response

{ "data": [{ "source": "guland.vn", "province": "ho-chi-minh", "district": null, "transaction": "ban", "listings": 44, "median_price_per_m2": 11575000, "avg_price_per_m2": 25963707, "avg_area": 120 }] }
GET/api/realestate/estimate

Comps-based price estimate for a specific property. Finds the most specific tier of comparable crawled listings (project → street → ward → district → province) that has enough samples, takes the median price/m², and scales by area. Returns the estimate, a low–high band (p25–p75), the tier used, sample size, the comps & a confidence level. Descriptive only — floor, alley width & lot depth are NOT modeled.

Query Parameters

provincestringProvince key (default ho-chi-minh)
districtstringe.g. "quan 7" / "Quận 7" / "bình thạnh"
wardstringWard name (partial match)
streetstringStreet, e.g. "Nguyễn Thị Thập"
projectstringProject, e.g. "Cosmo City"
typestringapt | house | land | raw property_type slug
transactionstringban (default) | cho-thue
areanumberArea in m² (required for a total price)
refreshstring1 → also fetch a targeted realtime source query (nhatot keyword) + upsert before estimating, so project/street tiers reflect the freshest listings

Response

{ "data": [{ "used_tier": { "tier": "street", "label": "Street “Nguyễn Thị Thập”", "n": 8, "median_ppm2": 68000000 }, "median_ppm2": 68000000, "estimate_vnd": 4760000000, "confidence": "medium", "comps": [], "notes": [], "live": { "queries": ["Cosmo City"], "fetched": 102, "upserted": 100 } }] }
GET/api/realestate/fetch

On-demand realtime fetch + upsert (no estimate). Searches the nhatot Chợ Tốt gateway by keyword (project → street → district) and upserts arriving listings/agents into the DB so they're available to subsequent cached reads & estimates. Explicit opt-in action (like the MST live lookup); 60s per-query throttle, bounded pages.

Query Parameters

provincestringProvince key (default ho-chi-minh)
districtstringe.g. "Quận 7"
streetstringe.g. "Nguyễn Thị Thập"
projectstringe.g. "Cosmo City"
max_pagesnumberPages per keyword (50 ads/page, default 2)

Response

{ "data": [{ "queries": ["Cosmo City", "Nguyễn Thị Thập"], "fetched": 102, "upserted": 100, "agents_upserted": 67, "skipped_throttled": [] }] }
GET/api/realestate/land-prices

Official government land-price schedule (Bảng giá đất 2026) from guland.vn — per street/segment × land type (Đất ở, Đất thương mại dịch vụ, …), priced by 4 position tiers (Vị trí 1-4) in VNĐ/m². Latest snapshot per street. Covers every ward & street (HCMC ≈ 35K rows).

Query Parameters

provincestringProvince key filter (ho-chi-minh, lam-dong, da-nang, ha-noi, quang-ngai)
wardstringWard filter (partial match)
land_typestringLand type filter (partial match, e.g. 'Đất ở')
qstringStreet name search (partial match)
limitnumberMax results (default: 200)

Response

{ "data": [{ "province": "ho-chi-minh", "ward": "Phường Bến Thành", "street": "LÊ LỢI", "segment": "TRỌN ĐƯỜNG", "land_type": "Đất ở", "price_vt1": 687200000, "price_vt2": 343600000, "price_vt3": 274880000, "price_vt4": 219904000 }] }
GET/api/realestate/agents

Real-estate sellers — agencies, brokers & individual sellers (nhatot.com + guland.vn) with their active-listing counts (live_ads), ratings, verified/company flags, shop name & address. Ordered by activity.

Query Parameters

sourcestringSource filter (nhatot.com)
provincestringProvince key filter (ho-chi-minh, lam-dong, da-nang, ha-noi, quang-ngai)
qstringName / shop search (partial match)
companiesstring'1' → only agencies/companies
limitnumberMax results (default: 100)

Response

{ "data": [{ "source": "nhatot.com", "agent_id": "314697", "name": "HUỲNH LONG SƠN", "shop_name": "HUỲNH LONG SƠN", "address": "Thảo Điền Quận 2", "is_company": true, "is_verified": false, "live_ads": 145, "rating": 4.5 }] }
GET/api/geo/resolve

Resolve a Vietnamese location — a name (e.g. 'Q7, Nguyễn Thị Thập', 'HCM Tân Thuận') or a Google Maps URL/short-url — to structured admin units (province/district/ward), coordinates, and a batdongsan.com.vn geo mapping. Provider order: local DB → goong.io → geocode.maps.co, cached.

Query Parameters

qstringLocation name or Google Maps URL (required)

Response

{ "data": [{ "query": "Q7, Nguyễn Thị Thập, HCM", "province": "Hồ Chí Minh", "district": "Quận 7", "ward": "Phường Tân Thuận", "lat": 10.738, "lng": 106.723, "province_key": "ho-chi-minh", "batdongsan": { "sale_url": "https://batdongsan.com.vn/nha-dat-ban-tp-hcm", "district_slug": "quan-7" }, "confidence": "high", "cached": false }] }
GET/api/geo/forward

Forward geocoding (address → coordinates). Google Geocoding API-compatible response format. Provider order: local DB (zero API calls for 34 provinces + 127 Hà Nội districts) → goong.io → geocode.maps.co. Permanently cached — repeat queries cost zero.

Query Parameters

addressstringAddress or location name (required). Supports Vietnamese abbreviations: Q7→Quận 7, HCM→Hồ Chí Minh, HN→Hà Nội

Response

{ "status": "OK", "results": [{ "formatted_address": "Quận 7, Hồ Chí Minh, Việt Nam", "geometry": { "location": { "lat": 10.7329, "lng": 106.7196 }, "location_type": "ROOFTOP" }, "address_components": [{ "long_name": "Quận 7", "short_name": "7", "types": ["administrative_area_level_2"] }], "provider": "local+goong", "cached": false }], "api_calls": 1, "providers_called": ["local", "goong"] }
GET/api/geo/reverse

Reverse geocoding (coordinates → address). Google Geocoding API-compatible response format. Provider order: local DB → goong.io → geocode.maps.co. Permanently cached (rounded to ~1m precision).

Query Parameters

latlngstringLatitude,Longitude (required). Example: 10.7329,106.7196

Response

{ "status": "OK", "results": [{ "formatted_address": "Tân Phú, Quận 7, Hồ Chí Minh, Việt Nam", "geometry": { "location": { "lat": 10.7329, "lng": 106.7196 }, "location_type": "ROOFTOP" }, "address_components": [...], "provider": "goong", "cached": false }], "api_calls": 1, "providers_called": ["goong"] }
GET/api/geo/usage

Provider API call usage counters since server start. Tracks goong.io, here.com, and geocode.maps.co call counts.

Response

{ "status": "OK", "usage": { "goong": 15, "here": 2, "maps.co": 3 } }
GET/api/geo/stats

Persistent geo API usage analytics: total calls, cache hit rate, API calls saved, provider breakdown, top uncached queries. Used to identify optimization opportunities and track cost savings.

Query Parameters

daysnumberLookback period in days (default: 7)

Response

{ "status": "OK", "total": 150, "cached": 120, "uncached": 30, "cache_hit_rate": 80.0, "api_calls_total": 45, "api_calls_saved": 120, "avg_latency_ms": 12, "by_provider": [...], "top_uncached_queries": [...] }
GET/api/realestate/geo

Resolve a location (name or Google Maps URL) AND return matching real-estate listings in that province/district. Combines /api/geo/resolve with a listings query.

Query Parameters

qstringLocation name or Google Maps URL (required)
transactionstringban (sale) | cho-thue (rent)
limitnumberMax listings (default: 50)

Response

{ "geo": { "province": "Hồ Chí Minh", "district": "Quận 7", "province_key": "ho-chi-minh" }, "data": [{ "listing_id": "...", "price_value": 4500000000, "area": 72 }] }
GET/api/realestate/nearby

Find real-estate listings within a radius of a point. Uses PostgreSQL earthdistance for great-circle distance. Returns listings sorted by distance with distance_m field. Max radius 50km, max 500 results.

Query Parameters

latnumberLatitude of center point (required, 7-24 for Vietnam)
lngnumberLongitude of center point (required, 100-115 for Vietnam)
radiusnumberSearch radius in km (default: 2, max: 50)
transactionstringban (sale) | cho-thue (rent)
limitnumberMax results (default: 50, max: 500)

Response

{ "center": { "lat": 10.7329, "lng": 106.7196 }, "radius_km": 2, "data": [{ "listing_id": "...", "price_value": 4500000000, "area": 72, "lat": 10.731, "lng": 106.721, "distance_m": 245 }], "meta": { "count": 12 } }
GET/api/provinces/summary

Vietnam 2025 province/ward merge summary. 63 → 34 provinces, 3,321 wards. Before/after metrics: Population, Area, Density, GRDP, Budget.

Response

{ "slug": "vn-2025-province-merge", "total_before": 63, "total_after": 34, "metrics": [...], "before": {...}, "after": {...} }
GET/api/geo/vnsdi-provinces

Official VNSDI 34-province boundaries (Resolution 202/2025/QH15, post-merger, WGS84) as a simplified GeoJSON FeatureCollection. The VN-territorial-compliant boundary set powering the dashboard maps.

Response

{ "type": "FeatureCollection", "meta": { "source": "VNSDI", "basis": "Resolution 202/2025/QH15" }, "features": [{ "properties": { "code": "0001", "name": "Thủ đô Hà Nội" }, "geometry": {...} }] }
GET/api/places

Verified points of interest (bank ATMs/branches, fuel stations, retail chains, hospitals, businesses) for map consumption. One row per real-world place, keyed on source_id, updated in place. Feeds maps.vapp.vn. Current: banks 7,111 + Petrolimex fuel 2,857 + MOH hospitals 1,248 = 11,216 POIs.

Query Parameters

categorystringbank | fuel | pharmacy | grocery | fnb | hospital | post | business …
sourcestringSource slug (e.g. bidv)
provincestringProvince name (ILIKE)
with_gpsboolean1 = only places with lat/lng
geo_levelnumberMin geo precision (1=district, 2=ward, 3=exact)
bboxstringminLng,minLat,maxLng,maxLat viewport filter
sincestringISO timestamp — only places refreshed since (delta sync on fetched_at)
cursorstringKeyset pagination cursor (last source_id)
limitnumberDefault 500, max 2000

Response

{ "data": [{ "source_id": "bidv:123", "source": "bidv", "category": "bank", "subcategory": "atm", "name": "…", "province": "…", "ward": "…", "lat": 21.0, "lng": 105.8, "geo_level": 3, "phone": "…", "hours": "…", "tax_code": null, "map_link": "…", "first_seen": "2026-07-07" }], "meta": { "count": 500, "cursor": "bidv:999" } }
GET/api/places/stats

POI coverage: totals plus per-category and per-source counts with GPS coverage % and last_crawled. For map layer toggles + coverage dashboards.

Response

{ "data": { "totals": { "total": 3194, "with_gps": 3194 }, "by_category": [{ "category": "bank", "total": 3194, "with_gps": 3194 }], "by_source": [{ "source": "bidv", "category": "bank", "total": 3194, "with_gps": 3194, "last_crawled": "…" }] } }
GET/api/provinces/map

Province GeoJSON with merge metadata. Returns Leaflet-ready official VNSDI boundary GeoJSON + per-province metrics for choropleth coloring.

Response

{ "data": [{ "id": "An Giang", "name": "An Giang", "merged_from": "...", "metrics": {...} }], "geoJSON": {...}, "metricExtents": {...}, "boundary_source": "VNSDI (NQ 202/2025/QH15)" }
GET/api/provinces/wards

Search merged wards (3,321 total). Supports search, province filter, pagination.

Query Parameters

qstringSearch by ward name (new or old)
provincestringFilter by province name (exact)
per_pagenumberResults per page (default: 50, max: 5000)

Response

{ "data": [{ "new_name": "Phường 1", "province": "TP HCM", "old_names": "Phường 1, Phường 2" }], "meta": { "total_wards": 3321 } }
GET/api/blog

Data-driven blog posts — original cross-source analysis generated from our own crawled metrics (AI-generated posts flagged via ai_generated). Newest first.

Query Parameters

categorystringFilter by category slug (e.g. diem-tin-ngay, vang-ty-gia)
limitnumberMax results (default: 50, max: 200)

Response

{ "data": [{ "slug": "...", "title": "...", "summary": "...", "ai_generated": true, "cadence": "daily", "category": "diem-tin-ngay", "published_at": "..." }], "meta": { "count": 12, "updated_at": "..." } }
GET/api/blog/categories

Blog categories (metric domains: daily brief, gold & FX, energy, agriculture, real estate, retail, macro) and post counts.

Response

{ "data": [{ "slug": "diem-tin-ngay", "name_vi": "Điểm Tin Ngày", "cadence": "daily", "post_count": 12 }] }
GET/api/blog/:slug

Single blog post including markdown body and the cross-source metric snapshot it was built from (for client-side charts).

Response

{ "data": { "slug": "...", "title": "...", "body_md": "...", "ai_generated": true, "ai_model": "...", "cadence": "daily" } }
GET/api/mst/:code

Vietnamese company lookup by tax ID (mã số thuế). Cached company_info; ?refresh=true forces a live enrich.

Query Parameters

refreshbooleanForce a live re-fetch instead of cached (default false)

Response

{ "data": { "tax_code": "0312866443", "company_name": "...", "status": "NNT đang hoạt động", "address": "...", "representative": "...", "founded_date": "...", "tax_office": "..." }, "meta": { "cached": true, "fetched_at": "..." } }
GET/api/mst/stats

Aggregate analytics over the company corpus (54k+ businesses): counts by status (business health), entity type, province, HCMC tax area, formation year, daily enrichment velocity, and field coverage. Filterable; unfiltered result cached (5 min).

Query Parameters

provincestringFilter by province slug: hcm|hanoi|danang|binhduong|dongnai|haiphong|cantho
orgstringFilter by entity type: dn (doanh nghiệp) | ho (hộ kinh doanh)
founded_fromdateEarliest founding date, YYYY-MM-DD (e.g. 2015-01-01)
founded_todateLatest founding date, YYYY-MM-DD (e.g. 2015-12-31)

Response

{ "data": { "kpi": { "total": 54023, "active": 14337, "ceased": 20645, "added_7d": 54023 }, "by_status": [{ "label": "Không ở địa chỉ ĐK", "n": 17183 }], "by_org_type": [...], "by_province": [...], "by_hcm_area": [...], "formation_by_year": [...], "velocity": [...], "coverage": {...}, "recent": [...] }, "meta": { "updated_at": "..." } }

API Key Management

GET/api/keys

List your API keys. Requires authentication (session cookie).

Response

{ "data": [{ "id": "uuid", "name": "my-agent", "key_prefix": "dl_a1b2c3d4", "created_at": "..." }] }
POST/api/keys

Create a new API key. The raw key is only shown once.

Query Parameters

namestringLabel for this key (required)

Response

{ "data": { "id": "uuid", "name": "my-agent", "key_prefix": "dl_a1b2c3d4", "key": "dl_a1b2c3d4e5f6..." } }
POST/api/keys/:id/rotate

Rotate an API key — generates a new secret while keeping the same ID. All usage stats are preserved.

Response

{ "data": { "id": "uuid", "name": "my-agent", "key_prefix": "dl_e7f8g9h0", "key": "dl_e7f8g9h0i1j2..." } }
GET/api/keys/stats

Your API usage stats — overview, per-key breakdown, top endpoints (30d).

Query Parameters

daysnumberLookback period (default: 30)

Response

{ "data": { "overview": { "total_calls": 450, "avg_ms": 12, "keys_count": 2 }, "by_key": [...], "by_endpoint": [...] } }
DELETE/api/keys/:id

Revoke an API key. Cannot be undone.

Response

{ "data": { "revoked": true } }

Admin Endpoints

Require session cookie from a user with role=admin.

GET/api/admin/users

List all users with their roles and plans.

Response

{ "data": [{ "user_id": "...", "role": "admin", "plan": "unlimited", "email": "...", "name": "..." }] }
PUT/api/admin/users/:id/plan

Set a user's plan. Only admins can change plans.

Query Parameters

planstringOne of: free, pro, unlimited (in JSON body)

Response

{ "data": { "user_id": "...", "plan": "pro" } }

Quick Start (for LLMs / Agents)

# 1. Sign in at /settings and create an API key
# 2. Use the key in all requests:

# Get today's summary (best for daily briefing)
curl -H "Authorization: Bearer dl_YOUR_KEY" https://dulieu.dev/api/summary

# Get exchange rates for a date range
curl -H "Authorization: Bearer dl_YOUR_KEY" "https://dulieu.dev/api/exchange?from=2026-05-01&to=2026-05-20"

# Or use query parameter
curl "https://dulieu.dev/api/gold?date=2026-05-20&api_key=dl_YOUR_KEY"

# Free plan: 100 calls/day. Upgrade to Pro for grocery data + higher limits.