DEV Community

Cover image for รูปแบบการออกแบบ API จาก Polymarket: ตลาดคาดการณ์ที่ใหญ่ที่สุดในโลก
Thanawat Wongchai
Thanawat Wongchai

Posted on • Originally published at apidog.com

รูปแบบการออกแบบ API จาก Polymarket: ตลาดคาดการณ์ที่ใหญ่ที่สุดในโลก

ตลาดการทำนายเป็นหนึ่งในโดเมนที่ท้าทายที่สุดสำหรับการออกแบบ API เพราะต้องรองรับตราสารทางการเงินที่มีวันหมดอายุ ราคาความน่าจะเป็นแบบเรียลไทม์ เหตุการณ์หลายผลลัพธ์ที่มีความสัมพันธ์ด้านเงินทุนซับซ้อน รวมถึงผู้ใช้ทั้งมนุษย์และบอต arbitrage ทุกการตัดสินใจด้านสถาปัตยกรรมจึงถูกทดสอบทันทีภายใต้โหลดและความเสี่ยงจริง

ลองใช้ Apidog วันนี้

Polymarket ซึ่งเป็นแพลตฟอร์มตลาดการทำนายขนาดใหญ่ตามปริมาณการซื้อขาย มีระบบนิเวศ API ที่น่าสนใจสำหรับนักพัฒนา ไม่ใช่เพียง CRUD API บนฐานข้อมูล แต่เป็นการออกแบบที่จัดการความตึงเครียดระหว่างข้อมูลเปิดกับความปลอดภัย ข้อมูลเรียลไทม์กับข้อมูลย้อนหลัง และโมเดลการเงินดั้งเดิมกับพื้นฐานของคริปโต

บทความนี้สรุป 8 รูปแบบที่นำไปใช้ได้เมื่อคุณกำลังออกแบบ API สำหรับระบบซื้อขาย ตลาด หรือแพลตฟอร์มที่มีข้อมูลเรียลไทม์


รูปแบบที่ 1: แบ่งชั้น API ตามโดเมน

Polymarket แยก API ออกเป็น 3 ชุด โดยแต่ละชุดมีขอบเขตโดเมนชัดเจน:

  • Gamma API (gamma-api.polymarket.com) — ค้นหาตลาด เหตุการณ์ แท็ก และข้อมูลสำหรับการค้นหา
  • CLOB API (clob.polymarket.com) — สมุดคำสั่งซื้อ ราคา และการส่งคำสั่งซื้อ
  • Data API (data-api.polymarket.com) — ตำแหน่งผู้ใช้ การซื้อขาย การวิเคราะห์ และลีดเดอร์บอร์ด

การแบ่งนี้ไม่ใช่แค่เรื่องการตั้งชื่อ แต่แต่ละ API มีรูปแบบการใช้งานต่างกัน:

API ผู้ใช้หลัก การยืนยันตัวตน ลักษณะข้อมูล
Gamma UI, ระบบค้นหา, หน้ารวมตลาด สาธารณะ อ่านเป็นหลัก
CLOB เทรดเดอร์, market maker, บอต อ่านสาธารณะ / เทรดต้องยืนยันตัวตน เวลาแฝงต่ำ
Data Dashboard, analytics, portfolio tracker สาธารณะตามที่อยู่กระเป๋าเงิน ข้อมูลย้อนหลังและการวิเคราะห์

วิธีนำไปใช้

อย่าเริ่มจากการรวมทุก resource ไว้ใต้ API เดียว เช่น:

/markets
/orders
/users
/trades
Enter fullscreen mode Exit fullscreen mode

ให้เริ่มจากคำถามว่า API แต่ละส่วนมีไว้เพื่ออะไร แล้วกำหนด base URL หรือ service boundary ตามหน้าที่:

/discovery/*   # ค้นหาและเรียกดูข้อมูล
/trading/*     # สร้าง ยกเลิก และติดตามคำสั่งซื้อ
/analytics/*   # ประวัติ สถิติ และพอร์ตโฟลิโอ
Enter fullscreen mode Exit fullscreen mode

ผลลัพธ์คือคุณสามารถปรับขนาด กำหนด rate limit และใช้ authentication ที่เหมาะกับแต่ละโดเมนได้อิสระ


รูปแบบที่ 2: เปิดอ่านข้อมูลเป็นอันดับแรก

ข้อมูลตลาด เช่น ราคา สมุดคำสั่งซื้อ เมตาเดตาเหตุการณ์ และประวัติการซื้อขาย เปิดให้อ่านได้แบบสาธารณะ:

curl "https://gamma-api.polymarket.com/events?limit=5"
Enter fullscreen mode Exit fullscreen mode

ไม่ต้องใช้ API key หรือ OAuth เพื่อเริ่มดึงข้อมูลตลาด

แนวทางนี้ลด friction สำหรับนักพัฒนา ผู้สร้าง dashboard นักวิเคราะห์ และผู้ที่กำลังประเมินแพลตฟอร์ม โดยให้การยืนยันตัวตนเกิดขึ้นเฉพาะเมื่อการกระทำนั้นมีความเสี่ยง เช่น การส่งคำสั่งซื้อ

วิธีนำไปใช้

แยกสิทธิ์ read และ write ให้ชัดเจน:

GET  /markets              # public
GET  /markets/:id/orderbook # public
GET  /events               # public

POST /orders               # authenticated
DELETE /orders/:id         # authenticated
POST /withdrawals          # authenticated + stronger verification
Enter fullscreen mode Exit fullscreen mode

สำหรับ API ของคุณ ให้ถามคำถามนี้ก่อนเพิ่ม authentication:

การป้องกัน endpoint นี้ช่วยลดความเสี่ยงจริง หรือเพียงเพิ่มอุปสรรคให้ผู้ใช้?

หากข้อมูลมีเป้าหมายเพื่อให้คนค้นพบหรือสร้างต่อได้ การเปิด read API อาจช่วยเพิ่ม adoption ได้มากกว่าการล็อกไว้หลัง API key


รูปแบบที่ 3: ใช้การยืนยันตัวตนสองระดับตามระดับความไว้วางใจ

การซื้อขายต้องมีการยืนยันตัวตน แต่ Polymarket แยกขั้นตอนออกเป็นสองระดับ

L1: พิสูจน์ความเป็นเจ้าของกระเป๋าเงิน

L1 ใช้ลายเซ็น EIP-712 จาก private key เพื่อสร้างหรือดึง API credentials:

// L1: ใช้ private key เพื่อสร้าง API credentials
const credentials = await client.createOrDeriveApiKey();

// { key: "...", secret: "...", passphrase: "..." }
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนนี้เกิดขึ้นไม่บ่อย เพราะเป็นการพิสูจน์ตัวตนที่มีความสำคัญสูง

L2: ยืนยันคำขอซื้อขายแต่ละครั้ง

L2 ใช้ HMAC-SHA256 พร้อม credentials ที่ได้จาก L1:

{
  "POLY_ADDRESS": "0x...",
  "POLY_SIGNATURE": "<hmac-sha256>",
  "POLY_TIMESTAMP": "1716000000",
  "POLY_API_KEY": "550e8400-...",
  "POLY_PASSPHRASE": "..."
}
Enter fullscreen mode Exit fullscreen mode

วิธีนำไปใช้

นำแนวคิดนี้ไปใช้ได้แม้ไม่ใช่ระบบคริปโต:

  1. ใช้ credential ที่แข็งแรงสำหรับการสร้าง session หรือ API key
  2. ใช้ credential ที่จำกัดขอบเขตและหมุนเวียนได้สำหรับคำขอประจำ
  3. กำหนดอายุของ session token หรือ signed request
  4. รองรับการเพิกถอน key โดยไม่ต้องเปลี่ยนข้อมูลยืนยันตัวตนหลัก

ตัวอย่าง flow สำหรับระบบทั่วไป:

1. ผู้ใช้ยืนยันตัวตนด้วย passkey, MFA หรือ hardware key
2. ระบบออก API key แบบมี scope
3. ไคลเอนต์ลงนามคำขอด้วย HMAC หรือ short-lived token
4. ระบบตรวจ timestamp, signature และ scope ทุกครั้ง
Enter fullscreen mode Exit fullscreen mode

แนวคิดสำคัญคือแยก:

  • พิสูจน์ว่าคุณเป็นใคร
  • พิสูจน์ว่าคำขอนี้ได้รับอนุญาตจากคุณ

รูปแบบที่ 4: มองคำสั่งซื้อเป็นข้อความที่ลงนาม ไม่ใช่เพียง API request

เมื่อส่งคำสั่งซื้อบน Polymarket คุณไม่ได้เพียงส่ง JSON ไปให้เซิร์ฟเวอร์ แต่กำลังสร้างข้อความที่ลงนามด้วยคริปโตกราฟี ซึ่งเป็นข้อผูกพันทางการเงินที่ตรวจสอบได้

const response = await client.createAndPostOrder(
  {
    tokenID: "71321045679...",
    price: 0.65,
    size: 100,
    side: Side.BUY,
  },
  {
    tickSize: "0.01",
    negRisk: false,
  },
  OrderType.GTC
);
Enter fullscreen mode Exit fullscreen mode

ภายใน SDK จะสร้างข้อมูลแบบ EIP-712 ลงนามด้วย private key และส่งลายเซ็นพร้อมคำสั่งซื้อ การจับคู่คำสั่งเกิดขึ้นแบบ off-chain แต่การชำระบัญชีบน Polygon อาศัยข้อความที่ลงนามเหล่านี้

วิธีนำไปใช้

สำหรับการดำเนินการที่มีความเสี่ยงสูง ให้พิจารณาลงนาม payload แทนการพึ่งพา transport-level authentication เพียงอย่างเดียว

ตัวอย่าง payload ที่ออกแบบให้ลงนามได้:

{
  "action": "TRANSFER",
  "from": "account_123",
  "to": "account_456",
  "amount": "100.00",
  "currency": "USD",
  "nonce": "a5ec7d8d-1ed9-4e07-b06d-bf978ddfa241",
  "expiresAt": "2026-05-18T12:00:00Z"
}
Enter fullscreen mode Exit fullscreen mode

องค์ประกอบที่ควรมี:

  • action เพื่อป้องกันการนำลายเซ็นไปใช้กับ operation อื่น
  • nonce เพื่อป้องกัน replay attack
  • expiresAt เพื่อจำกัดอายุของคำสั่ง
  • ข้อมูลธุรกรรมครบถ้วน เพื่อให้ลายเซ็นผูกกับเจตนาที่ชัดเจน

รูปแบบนี้มีประโยชน์กับระบบการเงิน เอกสารกฎหมาย การอนุมัติรายการสำคัญ และ workflow ที่ต้องการ audit trail


รูปแบบที่ 5: ทำให้ออนโทโลยีของโดเมนชัดเจนในโมเดลข้อมูล

Polymarket แยกวัตถุหลักเป็น เหตุการณ์ (Event) และ ตลาด (Market)

  • Event คือคำถามระดับสูง เช่น “ใครจะชนะการเลือกตั้งวุฒิสภาสหรัฐฯ ปี 2026 ในรัฐเพนซิลเวเนีย?”
  • Market คือผลลัพธ์ที่ซื้อขายได้ภายในเหตุการณ์ เช่น “Bob Casey จะชนะหรือไม่?”

หนึ่ง Event จึงมีได้หลาย Market:

{
  "id": "501",
  "title": "2026 Pennsylvania Senate Race",
  "negRisk": true,
  "markets": [
    {
      "id": "2301",
      "question": "Will Bob Casey win?",
      "outcomePrices": "[\"0.42\", \"0.58\"]"
    },
    {
      "id": "2302",
      "question": "Will Dave McCormick win?",
      "outcomePrices": "[\"0.35\", \"0.65\"]"
    },
    {
      "id": "2303",
      "question": "Will a third candidate win?",
      "outcomePrices": "[\"0.23\", \"0.77\"]"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

โมเดลนี้ไม่ได้เก็บเพียงข้อมูล แต่บอกความสัมพันธ์เชิงแนวคิดของโดเมนด้วย

วิธีนำไปใช้

เมื่อออกแบบ resource ให้หลีกเลี่ยงการ flatten ความสัมพันธ์ที่สำคัญจนผู้ใช้ API ต้องเดาเอง

แทนที่จะใช้:

{
  "marketId": "2301",
  "eventName": "2026 Pennsylvania Senate Race",
  "candidate": "Bob Casey",
  "price": "0.42"
}
Enter fullscreen mode Exit fullscreen mode

ให้รักษาความสัมพันธ์ไว้:

{
  "event": {
    "id": "501",
    "title": "2026 Pennsylvania Senate Race",
    "markets": []
  }
}
Enter fullscreen mode Exit fullscreen mode

หาก API มี array ที่สัมพันธ์กันตาม index ให้ระบุสัญญานี้ใน schema และ SDK อย่างชัดเจน เช่น outcomes[0] ต้องตรงกับ outcomePrices[0]


รูปแบบที่ 6: ทำให้ข้อจำกัดด้านเงินทุนเป็นส่วนหนึ่งของ API

แฟล็ก negRisk แสดงว่าตลาดใน Event เดียวกันมีความสัมพันธ์ด้านเงินทุน

สำหรับเหตุการณ์ NegRisk ซึ่งมีผลลัพธ์ชนะได้เพียงหนึ่งรายการ:

1 โทเค็น “ไม่” สำหรับผลลัพธ์ A ≡ 1 โทเค็น “ใช่” สำหรับผลลัพธ์อื่นทั้งหมด

ตัวอย่างการแปลงตำแหน่ง:

ก่อน หลัง
1× ไม่ (อื่นๆ) 1× ใช่ (Casey) + 1× ใช่ (McCormick)

API เปิดเผยข้อจำกัดนี้ผ่าน negRisk: true ทั้งในข้อมูลตลาดและตัวเลือกการส่งคำสั่งซื้อ

const options = {
  tickSize: "0.01",
  negRisk: true
};
Enter fullscreen mode Exit fullscreen mode

หากส่งค่าผิด คำสั่งซื้ออาจถูกปฏิเสธหรือชำระไม่ถูกต้อง

วิธีนำไปใช้

หากโดเมนของคุณมี invariant ที่สำคัญ อย่าซ่อนไว้แค่ในเอกสาร ให้แสดงเป็น field ที่ตรวจสอบได้

ตัวอย่าง:

{
  "order": {
    "amount": 100,
    "currency": "USD",
    "settlementMode": "NETTED",
    "requiresCollateral": true
  }
}
Enter fullscreen mode Exit fullscreen mode

หรือใช้ enum เพื่อป้องกันค่าที่ไม่ถูกต้อง:

type SettlementMode = "GROSS" | "NETTED";
Enter fullscreen mode Exit fullscreen mode

หลักการคือ:

  • ข้อจำกัดที่ละเว้นแล้วทำให้ระบบผิดพลาด ควรอยู่ใน schema
  • ค่าที่เปลี่ยนพฤติกรรมของระบบ ควรถูก validate โดย server
  • SDK ควรบังคับให้ผู้เรียกตัดสินใจอย่างชัดเจน

รูปแบบที่ 7: ปฏิบัติต่อขนาด Tick เป็นสถานะตลาดแบบไดนามิก

API การเงินจำนวนมากมอง tick size เป็นค่าคงที่ แต่ Polymarket เปลี่ยน tick size ตามราคาตลาด

เมื่อราคาสูงกว่า 0.96 หรือต่ำกว่า 0.04 ขนาด tick ขั้นต่ำจะลดจาก 0.01 เป็น 0.001:

{
  "event_type": "tick_size_change",
  "asset_id": "65818619657...",
  "old_tick_size": "0.01",
  "new_tick_size": "0.001",
  "timestamp": "100000000"
}
Enter fullscreen mode Exit fullscreen mode

เหตุผลคือใกล้ขอบเขตราคา การขยับทีละ 0.01 มีผลมากเกินไป ตัวอย่างเช่น จาก 0.04 เป็น 0.03 คือการเปลี่ยนแปลงสัมพัทธ์ 25% การใช้ tick ที่ละเอียดขึ้นทำให้ราคาแสดงความน่าจะเป็นได้แม่นยำกว่า เช่น 0.973 แทนการปัดเป็น 0.97

วิธีนำไปใช้

อย่า hard-code parameter ที่เปลี่ยนแปลงได้ในระบบการเงินหรือระบบเรียลไทม์

ให้ไคลเอนต์ทำงานตามขั้นตอนนี้:

  1. ดึงค่าตั้งต้นของตลาดเมื่อเริ่มต้น
  2. Subscribe event ที่บอกการเปลี่ยนแปลงสถานะ
  3. อัปเดต cache ในหน่วยความจำ
  4. ตรวจสอบคำสั่งก่อนส่ง
  5. รีเฟรชสถานะเมื่อได้รับ validation error จาก server

ตัวอย่าง pseudo-code:

let tickSize = "0.01";

socket.on("tick_size_change", (event) => {
  if (event.asset_id === assetId) {
    tickSize = event.new_tick_size;
  }
});

function validatePrice(price: number) {
  const tick = Number(tickSize);
  return Number.isInteger(price / tick);
}
Enter fullscreen mode Exit fullscreen mode

หลักการที่นำไปใช้ได้กว้างกว่าตลาดการเงินคือ: หากค่าใดส่งผลต่อความถูกต้องของ request และเปลี่ยนได้ตามสถานะระบบ ให้เผยแพร่การเปลี่ยนแปลงนั้นเป็น event


รูปแบบที่ 8: แยก WebSocket ตามโปรไฟล์ผู้ใช้

Polymarket มี WebSocket แยกกันสองระบบ ซึ่งตอบโจทย์ผู้ใช้คนละประเภท

Market Channel

wss://ws-subscriptions-clob.polymarket.com/ws/market

ช่องทางนี้ออกแบบสำหรับผู้ที่ต้องซื้อขาย โดยส่งข้อมูลสมุดคำสั่งซื้อ การเปลี่ยนแปลงราคา การจับคู่การซื้อขาย และการเปลี่ยน tick size

{
  "assets_ids": [
    "65818619657568813474341868652308942079804919287380422192892211131408793125422"
  ],
  "type": "market"
}
Enter fullscreen mode Exit fullscreen mode

Real-Time Data Socket

wss://ws-live-data.polymarket.com

ช่องทางนี้รองรับข้อมูลแบบอื่น เช่น ความคิดเห็น ราคาคริปโตจาก Binance และ Chainlink ราคาหุ้น และกิจกรรมทางสังคม

{
  "action": "subscribe",
  "subscriptions": [
    {
      "topic": "crypto_prices",
      "type": "update",
      "filters": "btcusdt,ethusd"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

วิธีนำไปใช้

อย่าพยายามทำ WebSocket เดียวให้รองรับทุก use case หากผู้ใช้มีความต้องการด้าน latency ปริมาณข้อมูล และความน่าเชื่อถือที่ต่างกัน

แยก channel ตามลักษณะ workload:

wss://api.example.com/ws/trading
wss://api.example.com/ws/market-data
wss://api.example.com/ws/notifications
wss://api.example.com/ws/social
Enter fullscreen mode Exit fullscreen mode

ตัวอย่างการแบ่งความรับผิดชอบ:

Channel ความสำคัญ รูปแบบข้อมูล
Trading latency ต่ำมาก order update, fill, rejection
Market data throughput สูง order book, price ticks
Notifications รับประกันการส่งระดับหนึ่ง account alert, system event
Social ยอมรับความหน่วงได้มากกว่า comments, reactions, feed

การแยก infrastructure ทำให้คุณกำหนด retry policy, retention, backpressure และ SLA ให้เหมาะกับแต่ละประเภทข้อมูลได้


สิ่งที่รูปแบบเหล่านี้มีร่วมกัน

การออกแบบ API ของ Polymarket สะท้อนแนวคิดเดียวกัน: API ควรเปิดเผยโครงสร้างจริงของโดเมน แทนที่จะซ่อนความซับซ้อนไว้หลัง endpoint ที่ดูเรียบง่าย

สรุปเป็น checklist สำหรับนำไปใช้:

  • แยก API ตามโดเมนและรูปแบบ workload ไม่ใช่แค่ตาม entity
  • เปิดข้อมูลอ่านที่ปลอดภัยต่อสาธารณะเพื่อลด friction
  • แยกการพิสูจน์ตัวตนออกจากการยืนยันคำขอประจำ
  • ใช้ signed payload สำหรับการดำเนินการที่มีความเสี่ยงสูง
  • ทำให้ความสัมพันธ์ของ resource ปรากฏใน schema
  • เข้ารหัส domain constraint เป็น field หรือ type ที่ตรวจสอบได้
  • ส่ง state change ผ่าน event แทนการบังคับให้ client เดา
  • แยก real-time infrastructure ตามความต้องการของผู้ใช้

คำแนะนำ API ทั่วไปมักเน้นความง่ายในการเรียกใช้ ความสม่ำเสมอของชื่อ และการจัดการข้อผิดพลาดที่คาดการณ์ได้ ซึ่งทั้งหมดสำคัญ แต่ระบบที่ซับซ้อนต้องการมากกว่านั้น: ความซื่อสัตย์ต่อโดเมน

เมื่อโดเมนมีข้อจำกัด API ควรบังคับใช้ เมื่อโดเมนมีสถานะที่เปลี่ยนแปลง API ควรแจ้งให้ทราบ และเมื่อโดเมนมีความสัมพันธ์ที่มีผลต่อความถูกต้อง API ควรทำให้ความสัมพันธ์นั้นมองเห็นได้ชัดเจน

Top comments (0)