Divar uses a server-driven widget API (JSON over HTTPS, protobuf type annotations embedded via @type).
All responses are lists of typed widget_type objects. Base URL: https://api.divar.ir.
Every filter field uses a typed wrapper. Wrong type = HTTP 400. Use this table exactly:
| Field | Wrapper | Example |
|---|---|---|
| ------- | --------- | --------- |
category | str | { "str": { "value": "residential-rent" } } |
sort | str | { "str": { "value": "sort_date" } } |
districts | repeated_string | { "repeated_string": { "value": ["920", "82"] } } |
rooms | repeated_string | { "repeated_string": { "value": ["یک", "دو"] } } |
rent | number_range | { "number_range": { "minimum": "20000000", "maximum": "60000000" } } |
credit | number_range | { "number_range": { "minimum": "300000000", "maximum": "600000000" } } |
size | number_range | { "number_range": { "minimum": "80", "maximum": "120" } } |
balcony | boolean | { "boolean": { "value": true } } |
parking | boolean | { "boolean": { "value": true } } |
> number_range min/max values are strings, not numbers.
> boolean value is a JS boolean (true/false), not a string.
The API slug differs from the divar.ir URL path segment. Never use the URL segment as the slug.
API slug (category.str.value) | URL path | Persian |
|---|---|---|
| --------------------------------- | ---------- | --------- |
residential-rent | rent-residential | اجاره مسکونی |
apartment-sell | apartment-sell | فروش آپارتمان |
apartment-rent | rent-apartment | اجاره آپارتمان |
buy-residential | buy-residential | فروش مسکونی |
real-estate | real-estate | همه ملک |
buy-commercial-property | buy-commercial-property | فروش اداری/تجاری |
rent-commercial-property | rent-commercial-property | اجاره اداری/تجاری |
rent-temporary | rent-temporary | اجاره کوتاهمدت |
When in doubt, intercept a real browser request via DevTools to read the actual category.str.value.
POST /v8/postlist/w/search
Verified working payload (residential rental, Tehran, tested 2026-04-01 → returned 24 listings):
{
"city_ids": ["1"],
"source_view": "SEARCH",
"disable_recommendation": false,
"search_data": {
"form_data": {
"data": {
"category": { "str": { "value": "residential-rent" } },
"districts": { "repeated_string": { "value": ["139","143","145","4162","75","78","82","920","921"] } },
"rooms": { "repeated_string": { "value": ["دو","یک"] } },
"rent": { "number_range": { "minimum": "20000000", "maximum": "60000000" } },
"credit": { "number_range": { "minimum": "300000000", "maximum": "600000000" } },
"size": { "number_range": { "minimum": "80", "maximum": "120" } },
"balcony": { "boolean": { "value": true } },
"parking": { "boolean": { "value": true } }
}
},
"server_payload": {
"@type": "type.googleapis.com/widgets.SearchData.ServerPayload",
"additional_form_data": {
"data": { "sort": { "str": { "value": "sort_date" } } }
}
}
}
}
Response: { "list_top_widgets": [...], "list_widgets": [...] }
Filter to widget_type === "POST_ROW" to get listings. Each POST_ROW has:
data.title — listing title (Persian)data.middle_description_text — price stringdata.bottom_description_text — relative time ("دقایقی پیش")data.action.payload.token — unique post ID (use for detail/contact endpoints)data.action.payload.web_info.district_persian — district namedata.image_count — number of photosCursor-based. On page 1, no pagination_data. On page 2+, add:
{
"pagination_data": {
"@type": "type.googleapis.com/post_list.PaginationData",
"last_post_date": "<ISO8601 from previous last post>",
"page": 2,
"layer_page": 2,
"search_uid": "<uuid — keep constant for the session>",
"cumulative_widgets_count": 24
}
}
viewed_tokens is an optional gzip+base64 bloom filter for server-side deduplication — safe to omit.
GET /v8/posts-v2/web/{token}
Returns sections (BREADCRUMB, HEADER, IMAGES, DETAILS) each containing widget arrays.
Common detail widget types: KEY_VALUE (room count, floor, etc.), DESCRIPTION, IMAGE_GALLERY, MAP.
POST /v8/postcontact/web/contact_info_v2/{token}
Body: {}. Returns widget_list with UNEXPANDABLE_ROW containing phone number.
Returns RBAC: access denied without authentication.
token cookie + did (device ID) cookie, set by divar.ir on login.Authorization: Basic — same JWT value as the token cookie.divar.ir.Authorization header explicitly.When a user pastes a URL like:
https://divar.ir/s/tehran/rent-residential/almahdi?balcony=true&credit=300000000-600000000&districts=920%2C82&rent=20000000-60000000&rooms=%D8%AF%D9%88%2C%DB%8C%DA%A9&size=80-120
Map query params to form_data fields:
| URL param | form_data field | Wrapper |
|---|---|---|
| ----------- | ---------------- | --------- |
URL path segment (e.g. rent-residential) | category → look up API slug | str |
districts=920,82,... | districts | repeated_string (split on ,) |
rooms=دو,یک | rooms | repeated_string (split on ,) |
rent=min-max | rent | number_range (split on -) |
credit=min-max | credit | number_range (split on -) |
size=min-max | size | number_range (split on -) |
balcony=true | balcony | boolean |
parking=true | parking | boolean |
map_bbox=... | not in form_data — belongs in /v8/mapview/viewport camera_info.bbox | — |
map_place_hash=... | not in form_data | — |
For full API reference (all endpoints, widget system, map/geo, autocomplete, filters):
→ read references/api-reference.md
共 1 个版本