เอกสาร Public API
คู่มือเชื่อมต่อระบบของคุณเข้ากับประกาศบน Homes ผ่าน API key ของคุณเอง · เวอร์ชัน v1
1. เริ่มต้นใช้งาน
Public API ใช้ API key ของคุณเองในการยืนยันตัวตน ก่อนเรียกใช้ต้องผ่าน 3 ขั้นตอนนี้
- 1ส่งคำขอใช้งานกรอกข้อมูลแอปพลิเคชันในหน้า API ของฉัน แล้วส่งให้เจ้าหน้าที่ตรวจสอบ
- 2รอการอนุมัติเจ้าหน้าที่จะตรวจสอบและกำหนดสิทธิ์ (scope) ที่คุณได้รับ
- 3สร้างคีย์แล้วเริ่มเรียกเมื่ออนุมัติแล้วให้สร้างคีย์ในหน้า API ของฉัน คีย์เต็มจะแสดงครั้งเดียวเท่านั้น
2. Base URL
https://api.homes.in.th/api/public/v1เวอร์ชันจะเปลี่ยนก็ต่อเมื่อมีการแก้ contract แบบที่ทำให้ระบบเดิมพัง โดยจะแจ้งล่วงหน้า
3. การยืนยันตัวตน
ส่งคีย์ของคุณในส่วนหัว Authorization แบบ Bearer ทุกคำขอ
Authorization: Bearer estk_live_a1b2c3d4e5f6_Xk9mQ2vR7pL4nT8sW1yZ6cB3dF5gH0jKทางเลือกสำรอง
หากระบบของคุณใช้ส่วนหัว Authorization ไปแล้ว ส่งคีย์ผ่าน X-Api-Key แทนได้
X-Api-Key: estk_live_a1b2c3d4e5f6_Xk9mQ2vR7pL4nT8sW1yZ6cB3dF5gH0jKรูปแบบของคีย์
| ส่วน | ความหมาย |
|---|---|
estk | คำนำหน้าประจำระบบ ช่วยให้เครื่องมือสแกนความลับตรวจจับได้ |
live | สภาพแวดล้อมของคีย์ |
a1b2c3d4e5f6 | รหัสประจำคีย์ ใช้อ้างอิงเวลาแจ้งปัญหาได้ |
Xk9m...H0jK | ส่วนลับ แสดงครั้งเดียวตอนสร้างเท่านั้น |
คีย์มีสิทธิ์เขียนประกาศในบัญชีของคุณ ให้เรียก API จากเซิร์ฟเวอร์ของคุณเท่านั้น และเก็บคีย์ใน environment variable ไม่ใส่ในโค้ดหรือ repository
สิทธิ์ (scope)
| Scope | ใช้ได้กับ |
|---|---|
LISTINGS_READ | GET /properties · /properties/analytics · /favorites |
LISTINGS_WRITE | POSTPATCHDELETE /properties · POST /images |
4. รูปแบบ Response
ทุก response เป็น JSON ที่มีคีย์ data และ error เสมอ มีเพียงตัวใดตัวหนึ่งที่ไม่เป็น null
{
"data": { "items": {}, "meta": {} },
"error": null
}{
"data": null,
"error": {
"code": "E93B08",
"message": "This API key does not have the required scope"
}
}| ฟิลด์ | ชนิด | คำอธิบาย |
|---|---|---|
data | object | null | ผลลัพธ์เมื่อสำเร็จ รายการแบบหน้าจะมี items และ meta |
data.meta.paginate | object | page, pageSize, total, totalPages |
error | object | null | มีค่าเมื่อคำขอล้มเหลว |
error.code | string | รหัสคงที่สำหรับเขียนเงื่อนไขในโค้ด ดูหัวข้อ 9 |
error.message | string | ข้อความอธิบายสำหรับคนอ่าน อาจเปลี่ยนได้โดยไม่แจ้ง |
ให้เขียนเงื่อนไขจาก error.code ไม่ใช่จาก message เพราะข้อความอาจถูกปรับคำได้ตลอด ส่วนรหัสจะคงที่
5. ขีดจำกัดการเรียก
นับรวมทุกคีย์ในบัญชีเดียวกัน โควต้ารายวันรีเซ็ตเวลา 07:00 น. ตามเวลาไทย (00:00 UTC) ทุก response จะมีส่วนหัวบอกโควต้าที่เหลือ
| ส่วนหัว | ตัวอย่างค่า | ความหมาย |
|---|---|---|
X-RateLimit-Limit | 60 | จำนวนครั้งสูงสุดต่อนาที |
X-RateLimit-Remaining | 57 | จำนวนครั้งที่เหลือในนาทีนี้ |
X-RateLimit-Reset | <unix-seconds> | เวลาที่โควต้าต่อนาทีจะรีเซ็ต |
X-RateLimit-Daily-Limit | 10000 | จำนวนครั้งสูงสุดต่อวัน |
X-RateLimit-Daily-Remaining | 9843 | จำนวนครั้งที่เหลือในวันนี้ |
เมื่อเกินขีดจำกัดจะได้ 429 Too Many Requests พร้อมส่วนหัว Retry-After (วินาที) ให้รอตามค่านั้นก่อนเรียกซ้ำ
6. Endpoints
ทุก path ต่อท้าย Base URL กดที่แต่ละรายการเพื่อดูพารามิเตอร์และตัวอย่าง
GET/api/public/v1/propertiesดึงประกาศของตัวเอง
คืนรายการประกาศทั้งหมดในบัญชีของคีย์ แบ่งหน้า เรียงลำดับ และค้นหาได้ ต้องมี scope LISTINGS_READ
พารามิเตอร์ (query)
| ชื่อ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
page | number | — | หน้าที่ต้องการ ค่าเริ่มต้น 1 |
limit | number | — | จำนวนต่อหน้า ค่าเริ่มต้น 10 สูงสุด 100 |
orderBy | string | — | ฟิลด์ที่ใช้เรียง ค่าเริ่มต้น createdAt |
sortBy | string | — | asc | desc ค่าเริ่มต้น desc |
searchTerm | string | — | ค้นหาจากหัวข้อและรหัสประกาศ |
propertyType | enum | — | RESIDENTIAL | COMMERCIAL | LAND |
listingType | enum | — | FOR_SALE | FOR_RENT | FOR_LEASE | FOR_NPA |
GET /api/public/v1/properties?page=1&limit=10&orderBy=createdAt&sortBy=desc&listingType=FOR_SALE
Authorization: Bearer estk_live_…{
"data": {
"items": [
{
"id": "75099be6-f7eb-4da6-b093-d1243f5c5ecf",
"shownId": "7814418701",
"title": "Noble Revolve Ratchada 1bedroom",
"propertyType": "RESIDENTIAL",
"listingType": "FOR_SALE",
"status": "PUBLISHED",
"price": "2890000",
"createdAt": "2026-10-19T08:12:44.000Z"
}
],
"meta": {
"paginate": { "page": 1, "pageSize": 10, "total": 12, "totalPages": 2 },
"countInfo": { "published": 9, "draft": 1, "pending": 1, "expired": 1 }
}
},
"error": null
}GET/api/public/v1/properties/analyticsสถิติประกาศของตัวเอง
สถิติบนเว็บแบบรวมยอดต่อประกาศในบัญชีของคีย์ ได้แก่ การแสดงผล การเข้าชม การคลิก การเริ่มแชต และการกดดูเบอร์โทร / LINE / อีเมล ต้องมี scope LISTINGS_READ
พารามิเตอร์
ไม่มีพารามิเตอร์ — คืนยอดรวมสะสมของทุกประกาศในบัญชี (ไม่แบ่งหน้า ไม่กรองช่วงวันที่)
GET /api/public/v1/properties/analytics
Authorization: Bearer estk_live_…{
"data": {
"items": [
{
"propertyId": "75099be6-f7eb-4da6-b093-d1243f5c5ecf",
"impression": 18420,
"view": 1246,
"click": 388,
"chatInitiation": 18,
"phoneView": 41,
"lineView": 27,
"emailView": 6,
"imageView": 92
}
]
},
"error": null
}GET/api/public/v1/favoritesผู้ที่ถูกใจประกาศของตัวเอง
รายชื่อบัญชีที่กดถูกใจประกาศในบัญชีของคีย์ เรียงจากล่าสุด ต้องมี scope LISTINGS_READ
พารามิเตอร์ (query)
| ชื่อ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
page | number | — | หน้าที่ต้องการ ค่าเริ่มต้น 1 |
limit | number | — | จำนวนต่อหน้า ค่าเริ่มต้น 10 สูงสุด 100 |
GET /api/public/v1/favorites?page=1&limit=10
Authorization: Bearer estk_live_…{
"data": {
"items": [
{
"id": "c3e4f5a6-7d8e-4f9a-0b1c-2d3e4f5a6b7c",
"favoritedAt": "2026-10-20T13:05:12.000Z",
"account": { "id": "a1c2e3f4-5b6d-4e7f-8a9b-0c1d2e3f4a5b", "displayName": "ผู้ใช้ Homes A", "firstName": null, "lastName": null, "countryCode": "+66", "profileImg": null },
"property": { "id": "75099be6-f7eb-4da6-b093-d1243f5c5ecf", "title": "Noble Revolve Ratchada 1bedroom" }
}
],
"meta": { "paginate": { "page": 1, "pageSize": 10, "total": 37, "totalPages": 4 } }
},
"error": null
}POST/api/public/v1/propertiesสร้างประกาศ
สร้างประกาศและเผยแพร่ทันทีเมื่อโควต้าเผยแพร่ของแพ็กเกจยังเหลือ หากโควต้าเต็มระบบจะเก็บเป็นฉบับร่างและคืนสถานะ DRAFT ต้องมี scope LISTINGS_WRITE และต้องส่งส่วนหัว Idempotency-Key ทุกครั้ง (ดูหัวข้อ 7)
ส่วนหัว
| ชื่อ | จำเป็น | คำอธิบาย |
|---|---|---|
Idempotency-Key | จำเป็น | สตริงไม่ซ้ำต่อคำขอ เช่น UUID v4 ความยาว 16–128 ตัวอักษร |
Content-Type | จำเป็น | application/json |
Body
| ฟิลด์ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
property | object | จำเป็น | ข้อมูลหลักของประกาศ |
property.title | string | จำเป็น | หัวข้อประกาศ |
property.description | string | จำเป็น | รายละเอียด |
property.propertyType | enum | จำเป็น | RESIDENTIAL | COMMERCIAL | LAND |
property.listingType | enum | จำเป็น | FOR_SALE | FOR_RENT | FOR_LEASE |
property.announcerStatus | enum | จำเป็น | OWNER | AGENT |
property.price | string | — | ราคาเป็นสตริงตัวเลข หน่วยบาท |
address | object | จำเป็น | ที่อยู่และตำแหน่ง |
address.streetAddress | string | จำเป็น | เลขที่ ซอย ถนน |
address.latitude / longitude | number | จำเป็น | พิกัดหมุด |
address.subDistrictId | number | จำเป็น | รหัสแขวง/ตำบล |
address.postalCodeId | number | จำเป็น | รหัสไปรษณีย์ (id) |
propertyImage | object | — | { "images": [{ "imageUrl", "fileName", "order" }] } จากผลของ POST /images |
residential | commercial | land | object | — | รายละเอียดตามประเภททรัพย์ ส่งเพียงก้อนเดียวที่ตรงกับ propertyType |
POST /api/public/v1/properties
Authorization: Bearer estk_live_…
Idempotency-Key: 7f3c1b2e-9a4d-4e8f-b6c0-2d5a8e1f9c7b
Content-Type: application/json
{
"property": {
"title": "คอนโด 1 ห้องนอน สุขุมวิท 21 ใกล้ MRT สุขุมวิท",
"description": "ห้องมุม ชั้น 12 วิวเมือง ตกแต่งครบ พร้อมเข้าอยู่",
"propertyType": "RESIDENTIAL",
"listingType": "FOR_SALE",
"announcerStatus": "AGENT",
"price": "4500000",
"propertyCondition": "SECOND_HAND",
"ownership": "FREEHOLD"
},
"address": {
"streetAddress": "ซอยสุขุมวิท 21 (อโศก) ถนนสุขุมวิท",
"latitude": 13.7372,
"longitude": 100.5611,
"subDistrictId": 100402,
"postalCodeId": 10110
},
"residential": {
"residentialType": "CONDO",
"usableArea": 35,
"floor": 12,
"bedroom": 1,
"bathroom": 1,
"parking": 1
},
"propertyImage": {
"images": [
{ "imageUrl": "https://cdn.homes.in.th/u/8f2c…/living.jpg", "fileName": "living.jpg", "order": 1 }
]
}
}{
"data": {
"id": "4f2b1c7e-9d3a-4e6b-8c21-5a7d9e0f1b23",
"shownId": "7814418712",
"status": "PUBLISHED",
"createdAt": "2026-10-21T04:31:09.000Z"
},
"error": null
}PATCH/api/public/v1/properties/{id}แก้ไขประกาศ
แก้ไขเฉพาะฟิลด์ที่ส่งมา ฟิลด์ที่ไม่ส่งจะคงค่าเดิม ประกาศต้องเป็นของบัญชีคีย์เท่านั้น มิฉะนั้นจะได้ 403 E30B15 ต้องมี scope LISTINGS_WRITE
พารามิเตอร์ (path)
| ชื่อ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
id | string (UUID) | จำเป็น | รหัสประกาศ (id จาก GET /properties — ไม่ใช่ shownId) |
Body (บางส่วน)
| ฟิลด์ | ชนิด | คำอธิบาย |
|---|---|---|
property · address · residential… | object | โครงสร้างเดียวกับตอนสร้าง ส่งเฉพาะที่ต้องการแก้ |
propertyImage.createImages | array | รูปใหม่ที่จะเพิ่ม [{ imageUrl, fileName, order }] |
propertyImage.updateImages | array | แก้ลำดับรูปเดิม [{ id, order }] |
propertyImage.removeIds | array | รหัสรูปที่จะลบ |
PATCH /api/public/v1/properties/4f2b1c7e-9d3a-4e6b-8c21-5a7d9e0f1b23
Authorization: Bearer estk_live_…
Content-Type: application/json
{
"property": { "price": "4390000" },
"propertyImage": {
"removeIds": ["img_01JAD7R1…"],
"updateImages": [{ "id": "img_01JAD7R2…", "order": 1 }]
}
}{
"data": { "id": "4f2b1c7e-9d3a-4e6b-8c21-5a7d9e0f1b23", "shownId": "7814418712", "status": "PUBLISHED", "updatedAt": "2026-10-21T05:02:51.000Z" },
"error": null
}DELETE/api/public/v1/properties/{id}ลบประกาศ
ลบประกาศออกจากบัญชี การลบย้อนกลับไม่ได้ สำเร็จจะได้ 204 No Content โดยไม่มี body ต้องมี scope LISTINGS_WRITE
พารามิเตอร์ (path)
| ชื่อ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
id | string | จำเป็น | รหัสประกาศ |
DELETE /api/public/v1/properties/4f2b1c7e-9d3a-4e6b-8c21-5a7d9e0f1b23
Authorization: Bearer estk_live_…(ไม่มีเนื้อหาใน response)POST/api/public/v1/imagesอัปโหลดรูปภาพ
อัปโหลดรูปก่อน แล้วนำ imageUrl ที่ได้ไปใส่ใน propertyImage ตอนสร้างหรือแก้ไขประกาศ ต้องมี scope LISTINGS_WRITE
Body (multipart/form-data)
| ฟิลด์ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
files | file[] | จำเป็น | สูงสุด 20 ไฟล์ต่อครั้ง ชนิด JPEG, PNG, WebP ไฟล์ละไม่เกิน 10 MB |
POST /api/public/v1/images
Authorization: Bearer estk_live_…
Content-Type: multipart/form-data; boundary=----homes
------homes
Content-Disposition: form-data; name="files"; filename="living.jpg"
Content-Type: image/jpeg
(binary)
------homes--{
"data": [
{ "imageUrl": "https://cdn.homes.in.th/u/8f2c…/living.jpg", "fileName": "living.jpg" },
{ "imageUrl": "https://cdn.homes.in.th/u/8f2c…/bedroom.jpg", "fileName": "bedroom.jpg" }
],
"error": null
}7. การเรียกซ้ำของ POST
การสร้างประกาศต้องส่งส่วนหัว Idempotency-Key เพื่อป้องกันประกาศซ้ำเมื่อระบบของคุณ retry ระบบจะจำคีย์พร้อม body ไว้ 24 ชั่วโมง
| สถานการณ์ | ผลลัพธ์ |
|---|---|
| เรียกครั้งแรกด้วยคีย์ใหม่ | สร้างประกาศตามปกติ คืน 201 |
| เรียกซ้ำด้วยคีย์เดิมและ body เดิม | คืนผลลัพธ์เดิม (ประกาศเดิม) ไม่สร้างซ้ำ |
| เรียกพร้อมกันด้วยคีย์เดิมขณะคำขอแรกยังไม่เสร็จ | 409 E93B24 ให้รอแล้วเรียกใหม่ |
| คีย์เดิมแต่ body ต่างกัน | 409 E93B23 ต้องใช้คีย์ใหม่ |
| ไม่ส่งส่วนหัว | 400 E93B22 |
| เรียกซ้ำหลังคำขอแรกล้มเหลว | คืน error เดิมซ้ำ (ไม่ประมวลผลใหม่) หากต้องการลองใหม่ให้ใช้คีย์ใหม่ |
| อายุของคีย์ | เก็บไว้ 24 ชั่วโมง หลังจากนั้นถือเป็นคำขอใหม่ |
8. ทดลองยิง API
วางคีย์ของคุณแล้วกดส่งได้จากหน้านี้เลย คำขอถูกส่งจากเบราว์เซอร์ไปยัง https://api.homes.in.th/api/public/v1 โดยตรง (ในเครื่องพัฒนาผ่าน proxy /api) ระบบไม่เก็บคีย์ของคุณ และหมดเวลาใน 15 วินาที
คำขอที่ส่งจากหน้านี้มีผลกับประกาศจริงในบัญชีของคุณ การสร้าง แก้ไข หรือลบจะเกิดขึ้นทันที
| ส่วนหัว | ค่า |
|---|
9. รหัสข้อผิดพลาด
| รหัส | HTTP | ความหมาย |
|---|---|---|
E93B01 | 401 | ไม่พบส่วนหัว Authorization หรือ X-Api-Key |
E93B02 | 401 | รูปแบบคีย์ไม่ถูกต้อง |
E93B03 | 401 | ไม่พบคีย์นี้ในระบบ |
E93B07 | 401 | คีย์หมดอายุหรือถูกเพิกถอนแล้ว |
E93B04 | 403 | คำขอใช้งาน API ยังไม่ได้รับอนุมัติ |
E93B05 | 403 | คำขอใช้งาน API ถูกระงับ |
E93B08 | 403 | คีย์ไม่มี scope ที่จำเป็น |
E93B09 | 429 | เกินขีดจำกัดต่อนาที ดู Retry-After |
E93B10 | 429 | เกินขีดจำกัดต่อวัน รีเซ็ต 07:00 น. |
E93B22 | 400 | ไม่ส่งส่วนหัว Idempotency-Key |
E93B23 | 409 | Idempotency-Key ซ้ำแต่ body ต่างกัน |
E93B24 | 409 | คำขอด้วย Idempotency-Key เดิมกำลังประมวลผลอยู่ |
VALIDATION_FAILED | 400 | ข้อมูลใน body ไม่ผ่านการตรวจสอบ ดูรายละเอียดใน error.details |
E30B03 | 404 | ไม่พบประกาศ |
E30B08 | 400 | ประเภททรัพย์กับก้อนข้อมูล (residential/commercial/land) ไม่ตรงกัน |
E30B09 | 400 | ที่อยู่ไม่ครบหรือรหัสพื้นที่ไม่ถูกต้อง |
E30B17 | 400 | รูปภาพไม่ถูกต้องหรืออ้างอิง imageUrl ที่ไม่ได้อัปโหลดผ่าน /images |
E30B15 | 403 | ประกาศนี้ไม่ใช่ของบัญชีคีย์ |
E40B03 | 403 | โควต้าเผยแพร่ของแพ็กเกจเต็ม |
E40B04 | 403 | โควต้าจำนวนประกาศของแพ็กเกจเต็ม |
10. ตัวอย่างโค้ด
ตัวอย่างอ่านคีย์จาก environment variable ชื่อ HOMES_API_KEY แล้วดึงรายการประกาศและสร้างประกาศใหม่พร้อม Idempotency-Key
curl -s "https://api.homes.in.th/api/public/v1/properties?page=1&limit=10&listingType=FOR_SALE" \
-H "Authorization: Bearer $HOMES_API_KEY"curl -s -X POST "https://api.homes.in.th/api/public/v1/properties" \
-H "Authorization: Bearer $HOMES_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"property": {
"title": "คอนโด 1 ห้องนอน สุขุมวิท 21 ใกล้ MRT สุขุมวิท",
"description": "ห้องมุม ชั้น 12 วิวเมือง ตกแต่งครบ พร้อมเข้าอยู่",
"propertyType": "RESIDENTIAL",
"listingType": "FOR_SALE",
"announcerStatus": "AGENT",
"price": "4500000"
},
"address": {
"streetAddress": "ซอยสุขุมวิท 21 (อโศก) ถนนสุขุมวิท",
"latitude": 13.7372, "longitude": 100.5611,
"subDistrictId": 100402, "postalCodeId": 10110
},
"residential": { "residentialType": "CONDO", "usableArea": 35, "floor": 12, "bedroom": 1, "bathroom": 1 }
}'import { randomUUID } from "node:crypto";
const BASE = "https://api.homes.in.th/api/public/v1";
const KEY = process.env.HOMES_API_KEY;
if (!KEY) throw new Error("HOMES_API_KEY is not set");
async function call(method, path, { body, headers } = {}) {
const res = await fetch(BASE + path, {
method,
headers: {
Authorization: `Bearer ${KEY}`,
...(body ? { "Content-Type": "application/json" } : {}),
...headers,
},
body: body ? JSON.stringify(body) : undefined,
});
if (res.status === 204) return null;
const json = await res.json();
if (json.error) {
// ตัดสินใจจาก error.code เสมอ ไม่ใช่ message
throw Object.assign(new Error(json.error.message), { code: json.error.code, status: res.status });
}
return json.data;
}
// 1) ดึงรายการประกาศ
const list = await call("GET", "/properties?page=1&limit=10&listingType=FOR_SALE");
console.log(list.meta.paginate.total, "listings");
// 2) สร้างประกาศ (Idempotency-Key ใช้ซ้ำได้เมื่อ retry คำขอเดิม)
const idem = randomUUID();
const created = await call("POST", "/properties", {
headers: { "Idempotency-Key": idem },
body: {
property: {
title: "คอนโด 1 ห้องนอน สุขุมวิท 21 ใกล้ MRT สุขุมวิท",
description: "ห้องมุม ชั้น 12 วิวเมือง ตกแต่งครบ พร้อมเข้าอยู่",
propertyType: "RESIDENTIAL",
listingType: "FOR_SALE",
announcerStatus: "AGENT",
price: "4500000",
},
address: {
streetAddress: "ซอยสุขุมวิท 21 (อโศก) ถนนสุขุมวิท",
latitude: 13.7372, longitude: 100.5611,
subDistrictId: 100402, postalCodeId: 10110,
},
residential: { residentialType: "CONDO", usableArea: 35, floor: 12, bedroom: 1, bathroom: 1 },
},
});
console.log("created", created.shownId, created.status);<?php
$base = 'https://api.homes.in.th/api/public/v1';
$key = getenv('HOMES_API_KEY') ?: throw new RuntimeException('HOMES_API_KEY is not set');
function homes(string $method, string $path, ?array $body = null, array $headers = []): ?array {
global $base, $key;
$h = array_merge(["Authorization: Bearer $key"], $headers);
if ($body !== null) $h[] = 'Content-Type: application/json';
$ch = curl_init($base . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $h,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_POSTFIELDS => $body !== null ? json_encode($body, JSON_UNESCAPED_UNICODE) : null,
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
if ($status === 204) return null;
$json = json_decode($raw, true);
if (!empty($json['error'])) {
// เขียนเงื่อนไขจาก error.code
throw new RuntimeException($json['error']['code'] . ': ' . $json['error']['message'], $status);
}
return $json['data'];
}
// 1) ดึงรายการประกาศ
$list = homes('GET', '/properties?page=1&limit=10&listingType=FOR_SALE');
echo $list['meta']['paginate']['total'], " listings\n";
// 2) สร้างประกาศพร้อม Idempotency-Key
$idem = bin2hex(random_bytes(16));
$created = homes('POST', '/properties', [
'property' => [
'title' => 'คอนโด 1 ห้องนอน สุขุมวิท 21 ใกล้ MRT สุขุมวิท',
'description' => 'ห้องมุม ชั้น 12 วิวเมือง ตกแต่งครบ พร้อมเข้าอยู่',
'propertyType' => 'RESIDENTIAL',
'listingType' => 'FOR_SALE',
'announcerStatus' => 'AGENT',
'price' => '4500000',
],
'address' => [
'streetAddress' => 'ซอยสุขุมวิท 21 (อโศก) ถนนสุขุมวิท',
'latitude' => 13.7372, 'longitude' => 100.5611,
'subDistrictId' => 100402, 'postalCodeId' => 10110,
],
'residential' => ['residentialType' => 'CONDO', 'usableArea' => 35, 'floor' => 12, 'bedroom' => 1, 'bathroom' => 1],
], ["Idempotency-Key: $idem"]);
echo 'created ', $created['shownId'], ' ', $created['status'], "\n";