Market NinjaMarket Ninja
使用情境

Webhook

擷取工作階段完成時的非同步通知,無需輪詢 API——訂閱方式、投遞驗證,以及一個 n8n 整合範例。

與其不斷輪詢 GET /me/scrapes/{id} 等待 status 變為 completed,不如訂閱一個 URL, 我們會在完成的那一刻主動向它發送 POST 請求。目前存在一種事件:scrape_session.completed

訂閱的建立與管理都在擴充功能的設定頁面完成(Webhook 區塊),而不是透過本 API——並沒有可供您自行呼叫的 POST /webhooks 端點。每個帳戶最多可擁有 5 個有效訂閱。

事件內容是一個指標,而不是資料本身

{
  "event": "scrape_session.completed",
  "user_id": "3e2f9a10-df34-4b8a-9c31-8e1d2f6a7b90",
  "scrape_session_id": "b1f8b6b0-2f7d-4a1a-9a3c-2f6d8e9a1234",
  "status": "completed",
  "stats": { "total": 40, "pending": 0, "processing": 0, "merged": 38, "rejected": 1, "flagged": 1 },
  "url": "https://data.marketninja.ru/v1/me/scrapes/b1f8b6b0-2f7d-4a1a-9a3c-2f6d8e9a1234/products"
}

我們刻意不將擷取到的資料直接嵌入事件本身——若要取得資料,請請求 url 欄位中的位址 (授權方式與本站其他所有介面相同,皆使用 Bearer 驗證,且這次呼叫會計入您平常的每日配額, 與直接呼叫該端點完全相同)。這樣設計有兩個原因:每日資料配額只會在這一個端點上被計算, 而且這種精簡的內容能確保同一個 webhook,無論工作階段中有 1 項商品還是 10,000 項商品, 處理起來都是一樣的。

user_id 用於標示該事件屬於您哪一個 Market Ninja 帳戶——如果您曾把多個帳戶的 webhook 指向同一個 URL(例如一個共用的自動化端點),這個欄位能協助您區分它們。

驗證投遞

每個請求都攜帶兩個標頭:

  • X-Marketninja-Event: scrape_session.completed
  • X-Marketninja-Signature: t=1735689600,v1=5257a869e7bcd1d2...

簽章是依 ${timestamp}.${rawBody} 計算的 HMAC-SHA256,使用建立訂閱時僅顯示一次的 簽署密鑰。請務必用收到的原始請求主體字串進行驗證——絕不要使用重新序列化後的 JSON.stringify(JSON.parse(body)) 來比對:這無法保證位元組層級一致(欄位順序、空白字元等), 會導致驗證悄悄失敗。

import crypto from "node:crypto";

function isValidSignature(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map((p) => p.split("="))
  );
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(parts.v1),
    Buffer.from(expected)
  );
}
如果您的端點在收到事件後會執行一些敏感操作,建議進行簽章驗證,但這並非強制要求。若還需要防重放攻擊,也請檢查 t 是否過舊(通常幾分鐘的容許範圍就足夠)——我們這一端不會對時間戳記做任何容許範圍限制。

投遞、重試與放棄的時機

  • 任何 2xx 回應都會被視為投遞成功——回應主體不會被讀取。
  • 我們不會跟隨重新導向——3xx 會被當作明確的失敗處理。
  • 每次嘗試的逾時時間為 5 秒。
  • 失敗的嘗試(網路錯誤、逾時、4295xx)會依退避排程重試:大致間隔為 1 分鐘、 5 分鐘、30 分鐘、2 小時、6 小時,最後再等 24 小時——最多重試 7 次,總跨度約為一天半—— 之後這一次具體的投遞會被標記為永久失敗。若 429 回應帶有 Retry-After 標頭, 會優先依該值執行,而非固定排程。
  • 任何其他非 2xx 回應(例如 400404)會立即被視為永久失敗——我們預設重試不會 改變您的端點所做的判斷。
  • 如果某個訂閱累積了足夠多次連續失敗,它會被自動停用——被停用的訂閱無法重新啟用, 只能在擴充功能的設定頁面重新建立一個新訂閱。
您的端點必須能透過公開的 https:// 位址存取——若 target_url 解析為私有位址、回送位址或連結本地位址,我們會在建立訂閱時就予以拒絕(並在每次投遞前重新檢查)。若要測試本機伺服器,需要先建立一個通道(如 ngrok、Cloudflare Tunnel 等)。

範例:在 n8n 中觸發 LLM 價格分析

一個常見的用法:收到通知、取出資料、交給 LLM 處理、再把結果傳送到某處。整個流程中, 只有兩個節點是 Market Ninja 特有的;之後的一切完全由您自行決定。

  1. 一個 Webhook 節點作為觸發器——HTTP Method: POST,路徑可任意選擇。這就是您在 註冊訂閱時要填入的目標 URL。

  2. 緊接其後的一個 HTTP Request 節點,設定為對事件中的 url 欄位發出 GET 請求, 並使用 Bearer 驗證:

    • URL:{{ $json.body.url }}
    • Authentication:Generic Credential Type → Bearer Auth,憑證中儲存您的某個 mn_live_... API 金鑰。

    其輸出格式為 { data: [...], meta: {...} }——與 GET /me/scrapes/{id}/products 一律回傳的格式完全相同:每筆提交記錄一項,product 欄位在尚未合併時為 null

  3. 接下來,您可以對 $json.data 做任何真正需要的處理——例如用 LLM 節點產生價格趨勢摘要、 傳送 Slack/Telegram 通知、寫入資料庫,或是把這幾步串接在一起。這部分完全與 Market Ninja 無關,依一般 n8n 工作流程的方式建置即可。

n8n 的 Webhook 節點預設會立即回應 200,無論後續工作流程是否成功執行。我們這一端顯示的「已投遞」狀態,只能確認您的 Webhook 節點收到了通知——自動化本身是否成功執行,請以您自己工作流程的執行歷史記錄為準。

Last updated on

On this page