# VietMap Search & Geocoding APIs — Agent Integration Guide
> **APIs covered:** Autocomplete v4 · Search v4 (forward geocode) · Reverse v4 · Place v4 · Search-batch (async CSV→XLSX) · Reverse-batch (sync JSON list)
> **Auth:** API key required — register at https://maps.vietmap.vn/console/register
> **⚠️ Security:** All API calls MUST be made from your **backend server**. NEVER expose your API key in frontend/client-side code.
> **💬 Need help?** Contact VietMap via Zalo OA: https://zalo.me/3189066936017422854
## Quick Summary
Everything about **"find this address / POI"** and **"what's at these coordinates"** in Vietnam. Single-call interactive APIs and bulk batch APIs.
| API | Input | Output | Use when |
|------------------|------------------------------|----------------------------|----------|
| **Autocomplete v4** | Partial text + focus | Suggestions (with `ref_id`) | Type-ahead (user still typing) |
| **Search v4** | Full address string | Ranked results (with `ref_id`) | Lookup a complete address (non-interactive) |
| **Reverse v4** | `lat,lng` | Addresses at that point | "What is this location?" |
| **Place v4** | `ref_id` | `lat/lng` + structured fields | User selects a suggestion → need coordinates |
| **Search-batch** | CSV, up to 10k rows (async) | XLSX with lat/lng | One-off migrations, ETL, enriching datasets |
| **Reverse-batch**| JSON list of `{lon,lat}` (sync) | JSON list of addresses | Near-real-time bulk lookup (a few hundred points) |
### Recommended interactive flow
```
Type-ahead: Autocomplete v4 (as user types) → Place v4 (on selection) → lat/lng
Lookup: Search v4 (full address) → Place v4 with ref_id → lat/lng
Coord→Addr: Reverse v4 (has lat/lng in each result)
```
### Autocomplete best practices
**Always pass `focus` = user's current coordinates.** This is the single most impactful parameter — it biases ranking toward nearby places. Without it, two identically named places in different cities have arbitrary ordering.
- Mobile: pass device GPS `lat,lng`.
- Web: pass map center or browser Geolocation result.
- Fallback: center of the city you expect the user to be in.
**Debounce `text` at 200–300 ms.** Call on every keystroke, but debounced. Minimum 2 characters — single characters produce low-quality results. Cancel in-flight requests before sending a new one (`AbortController` on web).
**Use `display_type=6`.** Returns old 3-level format as primary + new 2-level in `data_new`. One call covers both formats.
**Call Place v4 once, on selection only.** A typing user generates 10–20 autocomplete calls; only the result the user taps needs a Place v4 resolution. Each Place v4 = 1 transaction.
**Show `display` to the user; pass `ref_id` verbatim to Place v4.** Never decode or modify `ref_id` — it is an opaque token.
---
## 1. Autocomplete v4 (type-ahead)
### Endpoint
```
GET https://maps.vietmap.vn/api/autocomplete/v4?apikey={apikey}&text={text}&focus={lat,lng}&display_type=6
```
### Core Parameters
| Parameter | Type | Required | Description |
|---------------|---------|-------------|-------------|
| apikey | string | yes | Your VietMap API key |
| text | string | yes | User input (partial address/POI) |
| focus | string | recommended | `lat,lng` — biases ranking by proximity |
| display_type | number | recommended | **Use `6`** — old format primary, new format in `data_new` |
Advanced parameters
| Parameter | Type | Description |
|---------------|---------|-------------|
| cityId | number | Filter by city ID |
| distId | number | Filter by district ID |
| wardId | number | Filter by ward ID |
| circle_center | string | Center of circular search area `lat,lng` |
| circle_radius | number | Circle radius (meters) |
| cats | string | POI category code, e.g. `1002-1` |
| layers | string | `POI`, `ADDRESS`, `VILLAGE`, `WARD`, `DIST`, `CITY`, `STREET` |
| admin_new | boolean | Use new administrative boundaries for ID filters |
### Request
```
GET https://maps.vietmap.vn/api/autocomplete/v4?apikey={apikey}&text=197 tran phu&focus=10.75887508,106.67538868&display_type=6
```
### Response — `Array`
```json
[
{
"ref_id": "geocode:RAkPcicmZ3d-NQhac2kADHYlbFA...",
"distance": 0.069,
"address": "Phường 4,Quận 5,Thành Phố Hồ Chí Minh",
"name": "197 Trần Phú",
"display": "197 Trần Phú Phường 4,Quận 5,Thành Phố Hồ Chí Minh",
"boundaries": [
{ "type": 2, "id": 656652, "name": "4", "prefix": "Phường", "full_name": "Phường 4" },
{ "type": 1, "id": 1292, "name": "5", "prefix": "Quận", "full_name": "Quận 5" },
{ "type": 0, "id": 12, "name": "Hồ Chí Minh", "prefix": "Thành Phố", "full_name": "Thành Phố Hồ Chí Minh" }
],
"categories": [],
"entry_points": [],
"data_new": {
"ref_id": "geocode:RAkPcicmZ3d-NQhac2...",
"address": "Phường Chợ Quán,Thành Phố Hồ Chí Minh",
"name": "197 Trần Phú",
"display": "197 Trần Phú Phường Chợ Quán,Thành Phố Hồ Chí Minh",
"boundaries": [
{ "type": 2, "id": 18700, "name": "Chợ Quán", "prefix": "Phường", "full_name": "Phường Chợ Quán" },
{ "type": 0, "id": 12, "name": "Hồ Chí Minh","prefix": "Thành Phố", "full_name": "Thành Phố Hồ Chí Minh" }
]
},
"data_old": null
}
]
```
### Response Fields
| Field | Type | Description |
|--------------|--------------|-------------|
| ref_id | string | Opaque ID — pass to Place v4 to get lat/lng |
| distance | number | Distance from focus in **kilometers** |
| address | string | Admin address (ward/district/city) |
| name | string | Location / POI name |
| display | string | Full text (name + address) — use as the user-facing label |
| boundaries | Boundary[] | Admin hierarchy |
| categories | string[] | POI category codes |
| entry_points | EntryPoint[] | Entry points for complex POIs |
| data_new | object\|null | New-format variant (when `display_type=6`) |
| data_old | object\|null | Old-format variant (when `display_type=5`) |
**Boundary**: `type` (0=city, 1=district, 2=ward), `id`, `name`, `prefix`, `full_name`.
---
## 2. Search v4 (forward geocode, full string)
Same response shape as Autocomplete v4. Use for non-interactive lookups of complete address strings (DB imports, deep links, etc.).
### Endpoint
```
GET https://maps.vietmap.vn/api/search/v4?apikey={apikey}&text={text}&focus={lat,lng}&display_type=6
```
### Parameters
Same `apikey` / `text` / `focus` / `display_type` as Autocomplete, with the same advanced set (`layers`, `cityId`, `distId`, `wardId`, `circle_center/radius`, `cats`, `admin_new`).
### Request
```
GET https://maps.vietmap.vn/api/search/v4?apikey={apikey}&text=197 Trần Phú, phường 4, quận 5, thành phố Hồ Chí Minh&focus=10.75887508,106.67538868&display_type=6
```
### Response
Array of results with the same fields as Autocomplete v4 (`ref_id`, `display`, `boundaries`, `data_new` / `data_old`, …).
> Autocomplete is optimized for partial input while the user types. Search v4 is for full strings. Same API surface — different tuning.
---
## 3. Reverse v4 (coordinates → address)
### Endpoint
```
GET https://maps.vietmap.vn/api/reverse/v4?apikey={apikey}&lat={lat}&lng={lng}&display_type=6
```
### Parameters
| Parameter | Type | Required | Description |
|--------------|--------|-------------|-------------|
| apikey | string | yes | Your VietMap API key |
| lat | number | yes | Latitude |
| lng | number | yes | Longitude |
| display_type | number | recommended | **Use `6`** — old format primary, new format in `data_new` |
### Response — `Array`
```json
[
{
"lat": 10.759221,
"lng": 106.675901,
"ref_id": "vm:ADDRESS:MM03541B04565B07001C...",
"distance": 0.0000025,
"address": "Phường 4,Quận 5,Thành Phố Hồ Chí Minh",
"name": "197 Trần Phú",
"display": "197 Trần Phú Phường 4,Quận 5,Thành Phố Hồ Chí Minh",
"boundaries": [...],
"data_old": null,
"data_new": { ... }
}
]
```
> **Note:** Reverse v4 results include `lat`/`lng` directly (Search/Autocomplete v4 do not).
---
## 4. Place v4 (ref_id → lat/lng + details)
### Endpoint
```
GET https://maps.vietmap.vn/api/place/v4?apikey={apikey}&refid={ref_id}
```
### Parameters
| Parameter | Type | Required | Description |
|-----------|--------|----------|-------------|
| apikey | string | yes | Your VietMap API key |
| refid | string | yes | Opaque ID from Autocomplete v4 or Search v4. **Pass verbatim — do not decode/modify.** |
### Request
```
GET https://maps.vietmap.vn/api/place/v4?apikey={apikey}&refid=auto:RAkPcicmZ3d-NQhac2kADHYlbFAkBiEeAQAkCV0EXwdF_KakgoWOrg0FFWZfh4jFXA1kXfbZFlNRGFUYCAJXAF8BUQBVCAZUC18EA1VMAwUYXwJQBBQFBQVTHVFacANBQlU
```
### Response
```json
{
"display": "197 Đường Trần Phú,Phường Chợ Quán,Thành Phố Hồ Chí Minh",
"name": "",
"hs_num": "197",
"street": "Đường Trần Phú",
"address": "197 Đường Trần Phú",
"city_id": 12,
"city": "Thành Phố Hồ Chí Minh",
"district_id": 0,
"district": "",
"ward_id": 18700,
"ward": "Phường Chợ Quán",
"lat": 10.759222947,
"lng": 106.675902691
}
```
| Field | Type | Description |
|-------------|--------|-------------|
| display | string | Full formatted address — use as user-facing label |
| name | string | POI name (empty for plain addresses) |
| hs_num | string | House / building number |
| street | string | Street name |
| address | string | Street-level address portion |
| city / city_id | string/int | City name + ID |
| district / district_id | string/int | District name + ID. **Empty/0 in new format** (ref_id from `display_type=6`) |
| ward / ward_id | string/int | Ward name + ID |
| lat, lng | number | WGS84 coordinates |
---
## 5. `display_type` cheat-sheet (Autocomplete / Search / Reverse v4)
| Value | Label | Description |
|------:|-------|-------------|
| 1 | New Format | New 2-level admin format (ward, city) only |
| 2 | Old Format | Old 3-level admin format (ward, district, city) only |
| 3 | Input Format | Auto-detect from user input *(Search v4 only)* |
| 4 | Two objects | Both formats as separate items *(Reverse v4 only)* |
| 5 | Both, New primary | New format primary, old in `data_old` |
| **6** | **Both, Old primary** | **Old format primary, new in `data_new` (recommended)** |
---
## 6. Search-batch (CSV → XLSX, async, ≤ 10,000 rows)
Forward geocoding in bulk. **3-step flow:** upload → poll status → download.
### Step 1 — Upload
```
POST https://maps.vietmap.vn/api/search/batch-input?apikey={apikey}
Content-Type: multipart/form-data
```
| Field | Type | Required | Description |
|--------|-------------|----------|-------------|
| file | file (CSV) | yes | CSV file, **tab-delimited (`\t`)**, header row + up to 10,000 data rows |
| apikey | URL param | yes | Your VietMap API key |
**CSV format**
- Tab separator (not comma)
- First row = header, e.g. `text\tfocus`
- `text` = address/POI to geocode
- `focus` (optional) = `lat,lng` hint to bias ranking (can be empty per row)
Example:
```csv
text focus
133C Đ.Nguyễn Đình Chính Phường 8 q.Phú Nhuận HCM 10,106
2 Đ.Chiến Thắng f. 9 q Phú Nhuận HCM
89 Đ.Phùng Văn Cung p 2 Phú Nhuận HCM
```
Immediate response:
```json
{
"code": "IN_PROGRESS",
"message": "Thành công",
"data": { "data": "8bce0854-30c0-43f6-8043-792690f8d537", "count": 16 }
}
```
- `data.data` — **job-id**, used in steps 2 & 3
- `data.count` — rows accepted
### Step 2 — Poll
```
GET https://maps.vietmap.vn/api/search/batch-status/{job-id}?apikey={apikey}
```
```json
{
"code": "OK",
"message": "Thành công",
"data": {
"data": "8bce0854-30c0-43f6-8043-792690f8d537",
"status": { "done": 16, "count": 16, "status": "SUCCESS" }
}
}
```
Poll every 2–5 seconds. Stop when `status.status === "SUCCESS"`. Treat any non-`SUCCESS`/non-`IN_PROGRESS` state as a failure and surface it.
### Step 3 — Download
```
GET https://maps.vietmap.vn/api/search/batch-download/{job-id}?apikey={apikey}
```
Response: binary **XLSX**. Columns:
| Input | Display | Lat | Lng |
|-------|---------|-----|-----|
| 133C Đ.Nguyễn Đình Chính Phường 8 q.Phú Nhuận HCM | HAD Apartment & Office 133C Nguyễn Đình Chính Phường 8,Quận Phú Nhuận,Thành Phố Hồ Chí Minh | 10.79555553 | 106.6770714 |
| 2 Đ.Chiến Thắng f. 9 q Phú Nhuận HCM | Coffee Hoài Bắc 2 Chiến Thắng Phường 9,Quận Phú Nhuận,Thành Phố Hồ Chí Minh | 10.80011158 | 106.6762948 |
Parse with `exceljs` (Node) or `openpyxl` (Python). Empty `Lat/Lng` = no match for that input row.
---
## 7. Reverse-batch (JSON → JSON, sync)
Reverse geocode a list of coordinates in one round-trip.
### Endpoint
```
POST https://maps.vietmap.vn/api/geocode-fleet/reverse-batch?apikey={apikey}
Content-Type: application/json
```
### Body
```json
[
{ "lon": 107.42193008289291, "lat": 10.91527176296465 },
{ "lon": 107.4267435202076, "lat": 10.889979077535088 }
]
```
> ⚠️ Key is **`lon`** (not `lng`) for longitude. Other VietMap APIs use `lng`.
### Response
```json
[
{
"admin": { "id": 2583, "names": ["T. Đồng Nai", "X. Xuân Lộc"], "inside": true },
"address": { "address": "6 Quốc Lộ 1A", "admins": ["T. Đồng Nai", "X. Xuân Lộc"], "distance": 0.00022955 },
"network": { "id": 61657, "name": "Quốc Lộ 1A", "distance": 1.1586e-5 }
}
]
```
| Object | Fields | Description |
|------------|--------|-------------|
| admin | `id`, `names[]`, `inside` | Admin region the point falls in; `inside=true` if inside that polygon |
| address | `address`, `admins[]`, `distance` | Closest address record |
| network | `id`, `name`, `distance` | Nearest road segment |
Results preserve input order. Keep request size reasonable (a few hundred points per call) — chunk larger batches.
---
## Integration Flow
### Type-ahead UI
```
1. User types → frontend sends text to YOUR backend
2. Backend calls Autocomplete v4 with text + focus + display_type=6
3. Backend returns results to frontend → display results list (use `display`)
4. User selects result → frontend sends ref_id to YOUR backend
5. Backend calls Place v4 with ref_id → returns lat/lng
6. Frontend uses lat/lng for map marker / routing
```
### Coordinates → address on a map click
```
1. User clicks on the map → frontend sends lat/lng to YOUR backend
2. Backend calls Reverse v4 with lat/lng + display_type=6
3. Return the first result's `display` to the frontend
```
### Bulk migration
```
Search-batch (async):
Upload CSV (≤10k) → poll status every 2–5s → download XLSX → persist (Input, Display, Lat, Lng)
Surface empty Lat/Lng as data-quality review items
Reverse-batch (sync):
Backend collects coordinates → POST list → receive addresses in one response
Zip by input order to update your records
```
---
## cURL
```bash
# Autocomplete
curl "https://maps.vietmap.vn/api/autocomplete/v4?apikey=YOUR_KEY&text=197%20tran%20phu&focus=10.75887508,106.67538868&display_type=6"
# Search v4 (full string)
curl "https://maps.vietmap.vn/api/search/v4?apikey=YOUR_KEY&text=197%20Tr%E1%BA%A7n%20Ph%C3%BA%2C%20ph%C6%B0%E1%BB%9Dng%204%2C%20qu%E1%BA%ADn%205&focus=10.75887508,106.67538868&display_type=6"
# Reverse v4
curl "https://maps.vietmap.vn/api/reverse/v4?apikey=YOUR_KEY&lat=10.759221&lng=106.675901&display_type=6"
# Place v4 (ref_id from Autocomplete/Search)
curl "https://maps.vietmap.vn/api/place/v4?apikey=YOUR_KEY&refid=PASTE_REF_ID_VERBATIM"
# Search-batch — 1. upload
curl "https://maps.vietmap.vn/api/search/batch-input?apikey=YOUR_API_KEY" \
--form 'file=@"/path/to/input.csv"'
# Search-batch — 2. poll
curl "https://maps.vietmap.vn/api/search/batch-status/JOB_ID?apikey=YOUR_API_KEY"
# Search-batch — 3. download XLSX
curl "https://maps.vietmap.vn/api/search/batch-download/JOB_ID?apikey=YOUR_API_KEY" -o output.xlsx
# Reverse-batch (sync)
curl "https://maps.vietmap.vn/api/geocode-fleet/reverse-batch?apikey=YOUR_API_KEY" \
-H 'Content-Type: application/json' \
-d '[{"lon":107.4219,"lat":10.9152},{"lon":107.4267,"lat":10.8899}]'
```
---
## Common Pitfalls
- **Do not URL-decode `ref_id`.** Pass verbatim. The token is opaque.
- **Do not cache `ref_id` long-term.** VietMap does not guarantee permanence across data refreshes. Resolve to `lat/lng` and store those.
- **Empty `district` in Place v4?** Expected when the upstream `ref_id` came from `display_type=6` (new 2-level boundaries). Show `ward` + `city`; ignore empty `district`.
- **One Place v4 call per selection — not per suggestion.** A typing user can generate 10–20 autocomplete calls; only the final selection needs Place v4.
- **Search-batch CSV is tab-delimited.** Commas will fail silently. Repeating `.csv` extension with commas is the #1 support ticket.
- **Don't busy-poll Search-batch status.** 2–5s interval is enough; polling every 100ms wastes transactions.
- **Reverse-batch uses `lon`, not `lng`**, for longitude. Swapping returns nonsense geographies.
- **Blank batch rows = no match**, not an API error. Treat as data-quality feedback.
- **`display_type=6` is almost always what you want** — it gives you both old 3-level and new 2-level formats in one response, so you can render either without a second call or a migrate-address call.