開發者

開發者 API

將你自己的系統(CRM、ERP、網上商店)連接到 4pricing,直接從程式碼查詢競爭對手價格、比較組別及提醒。

基本網址: https://4pricing.net/api/v1/pricing · OpenAPI 規格(JSON)

三步快速整合

這是在你自己網站上看到 4pricing 資料的最快方法。你只需複製一個檔案、把金鑰貼進去,再上載到你的網站寄存空間 —— 毋須任何框架,亦毋須安裝任何東西。

你需要準備
  • 有效的 4pricing 訂閱(每個方案都包含 API)。
  • 支援 PHP 的網站寄存服務 —— 幾乎所有共享寄存服務都有支援(例如 cPanel、Plesk 等)。
  • 上載檔案的方法:寄存服務的檔案管理員,或 FTP 程式。
  1. 步驟 1: 建立你的 API 金鑰

    登入 panel.4pricing.net,打開「價格監察 → API 與金鑰」。

    按「產生金鑰」,為它改個名稱(例如「我的網店」),然後複製以 prc_live_ 開頭的金鑰。金鑰只會顯示一次 —— 如果弄丟了,請另建一個,並撤銷舊的。

    在控制台開啟 API 與金鑰 →

  2. 步驟 2: 複製現成檔案並貼上你的金鑰

    把下面的 PHP 檔案複製到文字編輯器(記事本、TextEdit 或 VS Code),並儲存為 4pricing-prices.php。

    php4pricing-prices.php
    <?php
    // =====================================================================
    // 請在此貼上你的 API 金鑰(panel.4pricing.net → API 與金鑰),放在引號之間
    $apiKey = 'PASTE_YOUR_KEY_HERE';
    // =====================================================================
    
    header('Content-Type: text/html; charset=utf-8');
    
    function h($text) { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); }
    function money($cents) {
        if ($cents === null) { return '—'; }
        return number_format($cents / 100, 2, '.', ',');
    }
    
    if (strpos($apiKey, 'prc_') !== 0) {
        exit('<p>' . h('缺少 API 金鑰:請打開檔案,把金鑰貼在標記的那一行。') . '</p>');
    }
    
    $ch = curl_init('https://4pricing.net/api/v1/pricing/groups/');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_USERAGENT => 'my-shop-integration/1.0',
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Accept: application/json'],
    ]);
    $body = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    if ($body === false) {
        exit('<p>' . h('無法連線到 4pricing:' . ' ' . curl_error($ch)) . '</p>');
    }
    if ($status !== 200) {
        $hints = [401 => 'API 金鑰錯誤或已被撤銷', 402 => '帳戶沒有有效的 4pricing 訂閱(或點數不足)', 429 => '一分鐘內請求太多,請一分鐘後再試'];
        $hint = isset($hints[$status]) ? ' — ' . $hints[$status] : '';
        exit('<p>' . h('4pricing 回覆了錯誤' . ' ' . $status . $hint) . '</p><pre>' . h($body) . '</pre>');
    }
    
    $groups = json_decode($body, true);
    $verdicts = [
        'cheapest' => '你的價格最低',
        'too_cheap' => '遠低於市場價格 —— 可以考慮加價',
        'competitive' => '具競爭力',
        'too_expensive' => '比大部分競爭對手昂貴',
        'no_competitors' => '暫時未有競爭對手的價格',
        'no_own_price' => '缺少你的價格',
    ];
    
    echo '<!doctype html><html><head><meta charset="utf-8"><title>' . h('我監察的產品') . '</title>';
    echo '<style>body{font-family:Arial,sans-serif;margin:24px}table{border-collapse:collapse}';
    echo 'th,td{border:1px solid #ddd;padding:6px 10px;text-align:left}th{background:#f5f5f5}</style>';
    echo '</head><body><h1>' . h('我監察的產品') . '</h1>';
    
    if (!$groups) {
        echo '<p>' . h('你暫時未有已比較的產品。') . '</p>';
    } else {
        echo '<table><tr><th>' . h('產品') . '</th><th>' . h('SKU') . '</th>';
        echo '<th>' . h('你的價格') . '</th><th>' . h('最低價競爭對手') . '</th>';
        echo '<th>' . h('差額') . '</th><th>' . h('判斷') . '</th></tr>';
        foreach ($groups as $g) {
            $yours = $g['own_price_cents_effective'];
            $cheapest = $g['min_competitor_cents'];
            $diff = '—';
            if ($yours !== null && $cheapest !== null) {
                $diff = ($yours > $cheapest ? '+' : '') . money($yours - $cheapest);
            }
            $verdict = isset($verdicts[$g['verdict']]) ? $verdicts[$g['verdict']] : $g['verdict'];
            echo '<tr><td>' . h($g['name']) . '</td><td>' . h($g['sku']) . '</td>';
            echo '<td>' . money($yours) . '</td><td>' . money($cheapest) . '</td>';
            echo '<td>' . $diff . '</td><td>' . h($verdict) . '</td></tr>';
        }
        echo '</table>';
    }
    echo '</body></html>';
    
    把 PASTE_YOUR_KEY_HERE 換成你的金鑰,並保留金鑰兩旁的引號。這是你唯一需要修改的一行。
  3. 步驟 3: 上載檔案並用瀏覽器打開

    用檔案管理員或 FTP 把 4pricing-prices.php 上載到你的網站,放在網站所在的資料夾(通常是 public_html)。

    用瀏覽器打開 https://your-site/4pricing-prices.php。你會看到一個表格,列出每件已比較的產品:你的價格、最低價競爭對手的價格、差額及判斷結果。

檔案內容:列出你的產品及最低價競爭對手的價格

這個檔案會向 4pricing 查詢你已比較的產品(GET /groups/),並以表格顯示。讀取資料免費,不會使用點數。

  • 「你的價格」是 4pricing 所掌握的你的售價;「最低價競爭對手」是你所監察的競爭對手中找到的最低價格。
  • 價格以一般單位顯示(12999 仙會變成 129.99);橫線(—)表示 4pricing 暫時未有價格,並非零。
  • 如果看到的是錯誤訊息而不是表格,訊息會告訴你要修正甚麼:金鑰錯誤、沒有有效訂閱,或一分鐘內請求太多。

第二個範例:傳送你的價格

當你希望 4pricing 不用讀取你的商店頁面,也能知道你自己的價格時(例如直接從商店的資料庫取得),就可以用這個方法。傳送價格免費(不使用點數),亦不計入你方案的受監察網址上限。

欄位意思
sku你的產品編號;4pricing 會憑它找到產品,第一次傳送時則會自動建立
name產品名稱,即你想在 4pricing 中看到的名稱
url選填:產品在你商店中的頁面網址(系統不會讀取它)
price_cents價格乘以 100,不含小數:49.99 → 4999
currency貨幣代碼,例如 EUR、GBP 或 RON(沒有填寫時為 RON)
php4pricing-send-price.php
<?php
// =====================================================================
// 請在此貼上你的 API 金鑰(panel.4pricing.net → API 與金鑰),放在引號之間
$apiKey = 'PASTE_YOUR_KEY_HERE';
// =====================================================================

// 你的產品:每件產品一個區塊。price_cents = 價格 x 100(49.99 變成 4999)。
$products = [
    [
        'sku' => 'WHITE-TSHIRT-M',
        'name' => '白色 T 恤(M 碼)',
        'url' => 'https://your-shop.example/white-tshirt-m',
        'price_cents' => 4999,
        'currency' => 'EUR',
    ],
];

header('Content-Type: text/html; charset=utf-8');

function h($text) { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); }

if (strpos($apiKey, 'prc_') !== 0) {
    exit('<p>' . h('缺少 API 金鑰:請打開檔案,把金鑰貼在標記的那一行。') . '</p>');
}

$ch = curl_init('https://4pricing.net/api/v1/pricing/own-products/bulk');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_USERAGENT => 'my-shop-integration/1.0',
    CURLOPT_TIMEOUT => 60,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Content-Type: application/json', 'Accept: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['items' => $products], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);

if ($body === false) {
    exit('<p>' . h('無法連線到 4pricing:' . ' ' . curl_error($ch)) . '</p>');
}
if ($status !== 200) {
    $hints = [401 => 'API 金鑰錯誤或已被撤銷', 402 => '帳戶沒有有效的 4pricing 訂閱(或點數不足)', 429 => '一分鐘內請求太多,請一分鐘後再試'];
    $hint = isset($hints[$status]) ? ' — ' . $hints[$status] : '';
    exit('<p>' . h('4pricing 回覆了錯誤' . ' ' . $status . $hint) . '</p><pre>' . h($body) . '</pre>');
}

$result = json_decode($body, true);
echo '<h1>' . h('已傳送到 4pricing 的價格') . '</h1>';
echo '<p>' . h('已建立的新產品:') . ' ' . (int) $result['created'] . '</p>';
echo '<p>' . h('已更新的產品:') . ' ' . (int) $result['updated'] . '</p>';
if (!empty($result['errors'])) {
    echo '<p>' . h('有問題的資料列:') . '</p><ul>';
    foreach ($result['errors'] as $error) {
        echo '<li>' . h($error) . '</li>';
    }
    echo '</ul>';
}
  • 儲存為 4pricing-send-price.php,貼上你的金鑰,修改產品資料列,上載後打開一次:頁面會顯示建立了多少件產品、更新了多少件。
  • 每次請求最多可帶 2,000 件產品;有問題的資料列會被報告,其餘的仍會儲存。再次傳送相同的 sku 就會更新價格。

用 curl 執行相同的呼叫

如果你較喜歡用終端機,這兩個指令的效果與兩個 PHP 檔案完全相同:

bash
curl https://4pricing.net/api/v1/pricing/groups/ \
  -H "X-API-Key: PASTE_YOUR_KEY_HERE"
bash
curl -X POST https://4pricing.net/api/v1/pricing/own-products/bulk \
  -H "X-API-Key: PASTE_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"items":[{"sku":"WHITE-TSHIRT-M","name":"白色 T 恤(M 碼)","url":"https://your-shop.example/white-tshirt-m","price_cents":4999,"currency":"EUR"}]}'
妥善保管金鑰
  • 金鑰可存取你整個 4pricing 帳戶。請只把它放在伺服器端的檔案(PHP、你的後端)中 —— 切勿放在瀏覽器的 JavaScript 或公開的程式碼庫內。
  • 任何知道已上載頁面網址的人都能看到價格表(但看不到金鑰)。請為檔案改一個難以猜到的名稱、用密碼保護該資料夾,或在不再需要時刪除它。
  • 如果金鑰外洩,請在「API 與金鑰」中撤銷它 —— 它會即時失效 —— 然後建立新的金鑰。

驗證方式

  • 每個請求都在 X-API-Key 標頭中帶上你的金鑰。Authorization: Bearer prc_live_… 亦可使用(僅限以 prc_ 開頭的金鑰)。
  • 金鑰沒有權限範圍之分:一個金鑰可讀取及更改你 4pricing 帳戶內的所有內容。請為每項整合(商店、ERP、報表)各建立一個金鑰,這樣撤銷其中一個時,不會影響其他整合。
  • 缺少、錯誤或已撤銷的金鑰會得到 401。撤銷會即時生效。一分鐘內使用太多無效金鑰會得到 429。
  • 金鑰只能在控制台(價格監察 → API 與金鑰)中建立、列出及撤銷;API 本身無法管理金鑰。建立金鑰需要有效的 4pricing 訂閱。
http
X-API-Key: prc_live_…
# or
Authorization: Bearer prc_live_…

基本網址與慣例

  • 本文件中的每個路徑都是相對於基本網址 —— /groups/ 即是 https://4pricing.net/api/v1/pricing/groups/。
  • 請求與回應均為 JSON(Content-Type: application/json)。
  • 金額是以最小貨幣單位(仙)表示的整數:price_cents = 12999 即是 129.99。百分比以基點(basis points)表示(100 = 1%)。
  • 未知的數值是 null,絕不是 0 —— 請以「—」顯示。
  • 呼叫集合時要加上結尾斜線(/products/、/groups/、/insights/)。
  • 分頁列表會在 X-Total-Count 標頭中回傳總數(例如 GET /products/?skip=0&limit=200)。
  • 請傳送一個能標示你的整合名稱的 User-Agent 標頭(例如 my-shop-integration/1.0)。API 前方的防火牆會對某些通用程式庫的預設值(例如 Python-urllib)回覆 403。

請求頻率、訂閱及方案限制

請求頻率限制

  • 每個帳戶每分鐘 60 次請求,以滑動時間窗計算 —— 同一帳戶的所有金鑰共用這 60 次。
  • 每個回應都會帶有 X-RateLimit-Limit、X-RateLimit-Remaining 及 X-RateLimit-Reset(時間窗的長度,以秒計)。
  • 超出限制時會得到 429,並附帶 Retry-After 標頭(以秒計)。請等待該段時間後再重試 —— 下面的範例已幫你處理這一點。
http
HTTP/2 200
x-ratelimit-limit: 60
x-ratelimit-remaining: 59
x-ratelimit-reset: 60

HTTP/2 429
retry-after: 60
{"detail":"Rate limit exceeded"}

訂閱與方案限制

  • 使用 API 需要有效的 4pricing 訂閱;沒有的話,每個請求都會得到 402,代碼為 plan_required。
  • 每個方案都包含 API,限制與控制台相同。新增或重新啟用項目時如果超出限額,會得到 402,代碼為 plan_limit,訊息會說明是哪一項限制。
限制意思
受監察網址4pricing 讀取的每個產品頁面網址會分開計算:你自己的頁面和每個競爭對手的頁面。你透過 /own-products 傳送的價格不計算在內。
競爭對手商店你所監察的不同競爭對手網站(網域)的數量。
最短檢查間隔同一個網址可以隔多久檢查一次。較短的間隔會自動調高至你方案的最低值;太早重新整理網址會得到 402 plan_limit。

哪些呼叫會使用點數

讀取商店頁面會從你帳戶的錢包扣除點數。以下的呼叫會在讀取任何內容之前先檢查餘額:如果餘額不足,會得到 402 insufficient_credits;如果暫時無法讀取錢包,會得到 503 credit_wallet_unavailable(請稍後再試)。這兩種情況都不會讀取任何內容,也不會扣除任何點數。

會使用點數的呼叫

方法端點功能費用
POST/groups/{group_id}/members把網址加入群組,可用 product_id 或新網址(新網址會即時讀取)點數
POST/groups/{group_id}/refresh立即檢查群組內的每個網址點數
POST/groups/{group_id}/suggestions「尋找競爭對手」:建議的競爭對手網址,附有評分及原因點數
POST/products/開始監察一個網址(url、name、role 為 own 或 competitor)—— 會即時讀取點數
POST/products/{product_id}/refresh立即檢查此網址的價格(遵守你方案的最短間隔)點數
POST/search/向零售商即時搜尋產品價格(每搜尋一間零售商便使用點數)點數

免費的呼叫

  • 所有讀取操作(GET):產品、群組、比較、歷史、提醒、洞察、簡報、點數、統計。
  • POST /own-products/ 及 POST /own-products/bulk —— 你自己的價格。
  • POST /sales/bulk —— 你的銷售數據。
  • 提醒、設定、確認,以及其他所有不讀取商店頁面的操作。
  • 你所追蹤網址的定時監察每次檢查時同樣會使用點數;GET /credits/ 會顯示你的餘額及預測用量。
  • POST /products/import 可一次新增多個網址,但不會即時讀取:它們會由定時監察檢查,並在當時扣除點數。

端點參考

金鑰可以使用的所有操作,按用途分組。OpenAPI 規格會逐個欄位說明每項操作,並附有確切的請求及回應內容。

你自己的價格

直接告訴 4pricing 你的價格,無需讀取你的商店頁面。免費。

方法端點功能費用
POST/own-products/傳送你某件產品的現行價格(先以 sku 尋找,找不到再以名稱尋找;不存在則建立)免費
POST/own-products/bulk一次請求傳送最多 2,000 件產品的價格;有問題的資料列會被報告,其餘的會儲存免費

已比較的產品(群組)

一個群組包含你的一件產品,以及銷售同一件商品的競爭對手頁面。群組會保存判斷結果及建議價格。

方法端點功能費用
GET/groups/所有已比較的產品,每件附有你的價格、最低價競爭對手的價格、判斷結果及建議價格免費
POST/groups/建立已比較的產品(名稱、sku、你的價格、成本、定價策略)免費
GET/groups/{group_id}單一已比較的產品免費
PUT/groups/{group_id}更新價格、成本、策略、檢查間隔或商店連結(store_kind)免費
DELETE/groups/{group_id}刪除群組;群組內的網址仍會繼續被監察免費
GET/groups/{group_id}/comparison你相對於每個競爭對手的位置,以及建議價格和其計算方式免費
GET/groups/{group_id}/history群組內每個網址的價格歷史免費
GET/groups/series你在市場中的定位,按日顯示,涵蓋所有產品免費
POST/groups/{group_id}/members把網址加入群組,可用 product_id 或新網址(新網址會即時讀取)點數
DELETE/groups/{group_id}/members/{product_id}把網址移出群組(它仍會繼續被監察)免費
POST/groups/{group_id}/refresh立即檢查群組內的每個網址點數
POST/groups/{group_id}/apply-recommended把建議價格設為你在 4pricing 中的自家價格(請傳送你所見到的價格;如果價格已改變,會回覆 409)免費
GET/groups/suggestions/sources「尋找競爭對手」可使用的來源免費
POST/groups/{group_id}/suggestions「尋找競爭對手」:建議的競爭對手網址,附有評分及原因點數

受監察網址

4pricing 讀取的每個頁面網址佔一行,無論是你的還是競爭對手的(role 為 own 或 competitor)。

方法端點功能費用
GET/products/受監察的網址,分頁顯示(skip、limit 最多 500;總數在 X-Total-Count)免費
POST/products/開始監察一個網址(url、name、role 為 own 或 competitor)—— 會即時讀取點數
POST/products/import一次新增多個網址;稍後由定時監察讀取免費
GET/products/export以 CSV 匯出有效的網址免費
GET/products/{product_id}單一受監察的網址及其最新價格免費
PATCH/products/{product_id}更改名稱、類別或檢查間隔免費
DELETE/products/{product_id}停止監察網址(會保留為停用狀態)免費
POST/products/{product_id}/refresh立即檢查此網址的價格(遵守你方案的最短間隔)點數
GET/products/{product_id}/history單一網址的價格歷史免費
GET/products/sparklines一次取得多個網址的簡短價格歷史免費
GET/products/price-changes在最近一次檢查中價格有變動的網址免費

即時查詢零售商現時的產品價格。

方法端點功能費用
POST/search/向零售商即時搜尋產品價格(每搜尋一間零售商便使用點數)點數

提醒

價格規則,以及已觸發提醒的收件匣。

方法端點功能費用
GET/alerts/你的價格提醒免費
POST/alerts/建立提醒(價格低於/高於某值、跌幅 %、重新有貨、被競爭對手壓價……)免費
DELETE/alerts/{alert_id}刪除提醒免費
POST/alerts/{alert_id}/rearm重新啟用已觸發的提醒免費
GET/alerts/events已觸發提醒的收件匣免費
GET/alerts/events/unread-count你有多少則未讀提醒免費
POST/alerts/events/read-all把提醒標記為已讀(可選擇只標記某個時間點之前的提醒)免費
POST/alerts/events/{event_id}/read把單一提醒標記為已讀免費

點數

餘額、預測及每一筆點數變動。

方法端點功能費用
GET/credits/點數餘額及用量預測免費
GET/credits/ledger每一筆點數變動免費
GET/credits/by-product每個網址使用的點數(總計及最近 30 日)免費

你的銷售

每件產品的每日銷售數據,用來計算每個機會的價值。免費。

方法端點功能費用
POST/sales/bulk每件產品的每日銷售數據(sku 或 group_id、day、units_sold、revenue_cents;可選 stock_units、cost_cents),最多 2,000 行;再次傳送相同產品及日期會覆寫原有數據免費

洞察、機會與簡報

甚麼有變化、為何重要、該怎麼做 —— 以及每日簡報。

方法端點功能費用
GET/insights/附有證據及價格建議的洞察(X-Total-Count)免費
GET/insights/{insight_id}單一洞察的詳情免費
POST/insights/{insight_id}/dismiss忽略一則洞察免費
GET/opportunities/機會摘要:根據你的銷售數據得出的價值,與市場估算分開顯示免費
GET/briefings/每日簡報的歷史免費
GET/briefings/latest最新的每日簡報免費
POST/briefings/generate根據未處理的洞察重新建立今日簡報(不會傳送任何內容)免費

價格動作

已建議、已傳送、已套用或已還原的價格更改。透過 API 你可以讀取這些動作,並確認透過 webhook 收到的動作。

方法端點功能費用
GET/actions/價格動作:已建議、已傳送、已套用、已還原免費
POST/actions/{action_id}/confirm確認你的系統已套用透過 price.apply 或 price.revert 收到的價格免費

設定

帳戶設定,包括 webhook 網址及密鑰。

方法端點功能費用
GET/settings/帳戶設定(零售商、調價策略、提醒傳送方式、webhook)免費
PUT/settings/更改設定,包括 alert_webhook_url、alert_webhook_secret 及 alert_webhook_enabled免費

統計

帳戶概覽及價格檢查的健康狀況。

方法端點功能費用
GET/stats/帳戶統計:網址、提醒、24 小時內的變動、群組免費
GET/stats/scrapers每間零售商的價格檢查健康狀況免費

53 endpoints · https://4pricing.net/api/v1/pricing/openapi.json

API 不能做的事

有些功能刻意只留在控制台,由已登入的人員作決定。透過 API 存取這些路徑時會得到 404:

路徑原因
/keys/…建立及撤銷金鑰 —— 金鑰不能建立更多金鑰。
/autopilot/…自動調價(autopilot)政策,決定哪些內容可以在沒有人員參與下更改;外洩的金鑰不應能擴大這個範圍。
/watchlists/…監察清單,每次執行都會消耗點數。
/actions/{action_id}/approve · reject · revert批准、拒絕或還原價格更改,是由人員在控制台作出的決定。API 只能讀取動作及確認它們。
/insights/{insight_id}/act · /insights/run對洞察採取行動(會寫入價格)及重新計算洞察。
/admin/… · /internal/…平台的內部管理。

Webhooks

4pricing 可以把事件傳送到你指定的網址(webhook):每當有事情發生,它就會發出一個 JSON POST,這樣你的系統便毋須不斷查詢。

每個帳戶只有一個 webhook 網址;所有事件類型都會傳到這裡。請以 event 欄位區分它們 —— 提醒是唯一沒有該欄位的事件。

啟用方法

  • 設定 alert_webhook_url(使用 80 或 443 埠的公開 http/https 網址)、alert_webhook_secret 及 alert_webhook_enabled: true —— 可在控制台設定,或使用 PUT /settings/。
  • 如果網址解析到私人或內部網絡,會被拒絕並回覆 422。系統不會跟隨重新導向。
  • 如要接收 briefing.ready,還需開啟 briefing_webhook_enabled。price.apply 及 price.revert 只會在群組已用 store_kind = webhook 連結到你的商店、且已設定密鑰時傳送。
bash
curl -X PUT https://4pricing.net/api/v1/pricing/settings/ \
  -H "X-API-Key: PASTE_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"alert_webhook_url":"https://your-shop.example/4pricing-webhook.php","alert_webhook_secret":"YOUR_WEBHOOK_SECRET","alert_webhook_enabled":true,"briefing_webhook_enabled":true}'

事件

事件意思
alert價格提醒已觸發。內容:event_id(請用它去除重複)、alert_id、alert_type、message、message_code、message_params、price_cents、currency、triggered_at、product、group、panel_url。沒有 event 欄位。
price.apply已批准、需要在你商店套用的價格:action_id、group(id、name、sku、ean、store_product_id)、old_price_cents、new_price_cents、currency、reason_code、mode 及 confirm.path。
price.revert還原至之前的價格。內容與 price.apply 相同;套用及確認的方式也相同。
briefing.ready每日簡報已準備好:day、headline、summary、sections、total_value_cents(僅根據你的銷售數據)、currency、panel_url。

每個 webhook 請求的標頭

標頭意思
X-4Pricing-Timestamp請求發出的時間,以 Unix 秒數表示。請拒絕過舊的請求(例如超過 5 分鐘)。
X-4Pricing-Signaturesha256= 後接文字「<timestamp>.<raw body>」的十六進位 HMAC-SHA256,以你的 webhook 密鑰計算。設定了密鑰時就會出現(price.apply 及 price.revert 一律會有)。
Content-Typeapplication/json; charset=utf-8
User-Agent4pricing-alerts/1.0

驗證簽章

請根據時間戳標頭及你收到的原始(RAW)內容重新計算簽章,以恆定時間比較,並檢查時間戳。完成這些步驟後才處理事件。接受事件後,請回覆任何 2xx 狀態碼。

php
<?php
$secret = 'YOUR_WEBHOOK_SECRET';

// 請使用原始(RAW)內容,必須與收到的完全一致 —— 不要使用重新編碼的 JSON
$body = file_get_contents('php://input');
$timestamp = isset($_SERVER['HTTP_X_4PRICING_TIMESTAMP']) ? $_SERVER['HTTP_X_4PRICING_TIMESTAMP'] : '';
$signature = isset($_SERVER['HTTP_X_4PRICING_SIGNATURE']) ? $_SERVER['HTTP_X_4PRICING_SIGNATURE'] : '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);

// 簽章錯誤或已超過 5 分鐘:拒絕
if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
if (isset($event['event']) && ($event['event'] === 'price.apply' || $event['event'] === 'price.revert')) {
    // 把 new_price_cents 套用到你商店中的產品(以 sku 或 store_product_id 對應)
    // $event['group']['sku'], $event['new_price_cents'], $event['currency']
    // 然後確認:用你的 API 金鑰呼叫 POST /actions/{action_id}/confirm
}
http_response_code(200);
echo 'ok';
javascript
// Node.js, no package needed
const http = require("http");
const crypto = require("crypto");
const SECRET = process.env.WEBHOOK_SECRET; // YOUR_WEBHOOK_SECRET

http.createServer((req, res) => {
  const chunks = [];
  req.on("data", (c) => chunks.push(c));
  req.on("end", () => {
    // 請使用原始(RAW)內容,必須與收到的完全一致 —— 不要使用重新編碼的 JSON
    const body = Buffer.concat(chunks);
    const ts = String(req.headers["x-4pricing-timestamp"] || "");
    const sig = String(req.headers["x-4pricing-signature"] || "");
    const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(ts + ".").update(body).digest("hex");
    const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
    const same = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
    if (!same || !fresh) {
      // 簽章錯誤或已超過 5 分鐘:拒絕
      res.writeHead(401);
      return res.end();
    }
    const event = JSON.parse(body.toString("utf8"));
    if (event.event === "price.apply" || event.event === "price.revert") {
      // 把 new_price_cents 套用到你商店中的產品(以 sku 或 store_product_id 對應)
      // 然後確認:用你的 API 金鑰呼叫 POST /actions/{action_id}/confirm
    }
    res.writeHead(200);
    res.end("ok");
  });
}).listen(3000);
python
# Python 3, standard library only
import hashlib
import hmac
import time

SECRET = "YOUR_WEBHOOK_SECRET"


def is_valid(raw_body: bytes, timestamp: str, signature: str) -> bool:
    # 請使用原始(RAW)內容,必須與收到的完全一致 —— 不要使用重新編碼的 JSON
    if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
        return False
    expected = "sha256=" + hmac.new(
        SECRET.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

傳送與重試

  • 提醒:遇到連線或 DNS 錯誤、429 或 5xx 時,請求會以指數退避方式重試,最多 5 次。其他 4xx 一律視為最終結果,不會重試。
  • 如果請求已發出但 10 秒內沒有回應,系統不會再次傳送(以免重複)—— 但仍然偶爾可能收到重複的事件,所以請以 event_id 去除重複。
  • price.apply 及 price.revert 只會傳送一次:如果傳送失敗,該動作會變成 failed。請以 action_id 去除重複。
  • briefing.ready 每日傳送一次。

確認你已套用的價格

  • 在你的系統確認之前,price.apply 不會被視為已套用。你在商店更改價格後,請用你的 API 金鑰呼叫 POST /actions/{action_id}/confirm:該動作會由「sent」變為「applied」。
  • 重複確認並無問題 —— 已套用的動作會原樣回傳。
bash
curl -X POST https://4pricing.net/api/v1/pricing/actions/ACTION_ID/confirm \
  -H "X-API-Key: PASTE_YOUR_KEY_HERE"

範例內容

json · price.apply
{
  "event": "price.apply",
  "action_id": "8d0f2c4e-0b7a-4f7e-9d61-3c2b1a0e9f55",
  "tenant_id": "…",
  "group": {
    "id": "2fa912df-440c-4ddf-896a-5d558d56085f",
    "name": "Canon RF 50mm F1.8 STM",
    "sku": "4515C005AA",
    "ean": "4549292181623",
    "store_product_id": "1042"
  },
  "old_price_cents": 124900,
  "new_price_cents": 118699,
  "currency": "RON",
  "reason_code": "undercut",
  "mode": "manual",
  "confirm": { "method": "POST", "path": "/api/v1/pricing/actions/8d0f2c4e-0b7a-4f7e-9d61-3c2b1a0e9f55/confirm" }
}
json · alert
{
  "event_id": 18342,
  "alert_id": "c1b5…",
  "alert_type": "competitor_undercut",
  "tenant_id": "…",
  "message_code": "competitor_undercut",
  "message": "…",
  "message_params": { "…": "…" },
  "price_cents": 118990,
  "currency": "RON",
  "triggered_at": "2026-10-13T07:05:12",
  "product": { "id": "…", "name": "…", "url": "https://…", "retailer": "…", "current_price_cents": 118990, "in_stock": true },
  "group": { "id": "…", "name": "Canon RF 50mm F1.8 STM", "sku": "4515C005AA" },
  "panel_url": "https://panel.4pricing.net/…"
}

錯誤

錯誤會附帶對應的 HTTP 狀態碼及 JSON 內容 {"detail": …}。detail 可以是一段文字、一個帶有可供你的程式處理的 code 的物件,或者(在 422 時)無效欄位的列表。

狀態代碼意思
401—API 金鑰缺少、錯誤或已被撤銷。
402plan_required帳戶沒有有效的 4pricing 訂閱。
402plan_limit會超出方案限制(受監察網址、競爭對手商店),或者在最短間隔之前就要求重新整理。訊息會說明是哪一項。
402insufficient_credits點數不足,無法進行需要讀取商店頁面的呼叫。沒有讀取任何內容,亦沒有扣除點數。
403—在到達 API 之前已被防火牆封鎖 —— 通常是因為使用了通用的 User-Agent,例如 Python-urllib。請傳送你自己的 User-Agent。
404—資源不存在、不屬於你的帳戶,或者該路徑無法透過 API 存取。
409recommendation_moved · own_from_listing · invalid_state · webhook_secret_required衝突:建議價格已改變(recommendation_moved)、動作處於另一個狀態(invalid_state)、需要 webhook 密鑰,或者該操作不適用於此項目。
422—資料無效,例如網址沒有 http/https,或缺少必填欄位。detail 會列出相關欄位。
429—帳戶每分鐘超過 60 次請求(或使用了太多無效金鑰)。請等待 Retry-After 指定的秒數。
503credit_wallet_unavailable暫時無法讀取點數錢包。請稍後再試;沒有扣除任何點數。

錯誤內容

json
{"detail": "Invalid or missing API key"}
{"detail": {"code": "plan_required", "message": "…"}}
{"detail": {"code": "insufficient_credits", "message": "…"}}
{"detail": [{"loc": ["body", "price_cents"], "msg": "…", "type": "…"}]}

程式碼範例

同一個讀取操作的四種語言版本,遇到 429 時會暫停並重試,然後傳送銷售數據。請把 PASTE_YOUR_KEY_HERE 換成你的金鑰(或設定 PRICING_API_KEY 環境變數)。

Node.js:讀取你已比較的產品

javascript
// Node.js 18+ (fetch is built in, no package needed)
const API_KEY = process.env.PRICING_API_KEY; // PASTE_YOUR_KEY_HERE
const BASE = "https://4pricing.net/api/v1/pricing";

async function call(path, options = {}) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const res = await fetch(BASE + path, {
      ...options,
      headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", ...(options.headers || {}) },
    });
    if (res.status === 429) {
      // 超過每分鐘 60 次請求:等待 Retry-After 指定的秒數,然後再試
      const wait = Number(res.headers.get("Retry-After") || 60);
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    if (!res.ok) throw new Error(res.status + " " + (await res.text()));
    return res.status === 204 ? null : res.json();
  }
  throw new Error("rate limited");
}

async function main() {
  // 你已比較的產品:你的價格、最低價競爭對手及判斷結果
  const groups = await call("/groups/");
  for (const g of groups) {
    console.log(g.name, g.own_price_cents_effective, g.min_competitor_cents, g.verdict);
  }
}

main().catch((err) => {
  console.error(err.message);
  process.exit(1);
});

Python:讀取你已比較的產品

python
# Python 3, standard library only
import json
import os
import time
import urllib.error
import urllib.request

API_KEY = os.environ["PRICING_API_KEY"]  # PASTE_YOUR_KEY_HERE
BASE = "https://4pricing.net/api/v1/pricing"


def call(path, method="GET", payload=None):
    data = json.dumps(payload).encode() if payload is not None else None
    for _ in range(3):
        req = urllib.request.Request(BASE + path, data=data, method=method, headers={
            "X-API-Key": API_KEY, "Content-Type": "application/json", "Accept": "application/json",
            # 設定你自己的 User-Agent:部分 HTTP 程式庫的預設值(例如 Python urllib)會被以 403 封鎖
            "User-Agent": "my-shop-integration/1.0",
        })
        try:
            with urllib.request.urlopen(req, timeout=30) as res:
                body = res.read()
                return json.loads(body) if body else None
        except urllib.error.HTTPError as err:
            if err.code == 429:
                # 超過每分鐘 60 次請求:等待 Retry-After 指定的秒數,然後再試
                time.sleep(int(err.headers.get("Retry-After", "60")))
                continue
            raise
    raise RuntimeError("rate limited")


# 你已比較的產品:你的價格、最低價競爭對手及判斷結果
for g in call("/groups/"):
    print(g["name"], g["own_price_cents_effective"], g["min_competitor_cents"], g["verdict"])

curl:傳送你的每日銷售數據

bash
curl -X POST https://4pricing.net/api/v1/pricing/sales/bulk \
  -H "X-API-Key: PASTE_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"rows":[{"sku":"WHITE-TSHIRT-M","day":"2026-10-12","units_sold":7,"revenue_cents":34993}]}'

PHP:傳送你的每日銷售數據

php
<?php
$apiKey = 'PASTE_YOUR_KEY_HERE';

// 每件產品的每日銷售數據(以 sku 或 group_id 識別),每次請求最多 2,000 行
$rows = [
    ['sku' => 'WHITE-TSHIRT-M', 'day' => '2026-10-12', 'units_sold' => 7, 'revenue_cents' => 34993],
];

$ch = curl_init('https://4pricing.net/api/v1/pricing/sales/bulk');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_USERAGENT => 'my-shop-integration/1.0',
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Content-Type: application/json'],
    CURLOPT_POSTFIELDS => json_encode(['rows' => $rows]),
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE) . ' ' . $body;   // 200 {"upserted": 1, "skipped": []}

常見問題

使用 4pricing API 需要懂編程嗎?

不需要。快速入門會提供一個現成的 PHP 檔案:你在控制台建立金鑰,貼進檔案,把檔案上載到你的網站寄存空間,再用瀏覽器打開即可。如果要做更深入的整合(ERP、網店平台),開發人員可以參考端點參考及 OpenAPI 規格。

4pricing API 收費多少?

API 包含在每個 4pricing 訂閱之內,限制與控制台相同。讀取你的資料免費;只有需要讀取商店頁面的呼叫(新增網址、重新整理、即時搜尋、尋找競爭對手)才會使用點數,與控制台一樣。

哪些呼叫會使用點數?

POST /products/、POST /products/{product_id}/refresh、POST /groups/{group_id}/refresh、帶有新網址的 POST /groups/{group_id}/members、POST /groups/{group_id}/suggestions 及 POST /search/。如果餘額不足,會得到 402 insufficient_credits,亦不會讀取任何內容。

我透過 API 傳送的價格,會計入方案的網址上限嗎?

不會。只有 4pricing 從頁面讀取的網址才會計算 —— 你自己的產品頁面和每個競爭對手的頁面分開計算。以 POST /own-products/ 或 /own-products/bulk 傳送的價格,既不算作網址,也不使用點數。

我可以限制金鑰能存取的內容嗎?

不可以,金鑰沒有權限範圍之分:一個金鑰可查看及更改你整個 4pricing 帳戶。請為每項整合使用獨立的金鑰,並撤銷不再使用的金鑰;撤銷會即時生效。

如果我每分鐘傳送超過 60 個請求會怎樣?

你會得到 429,並附帶 Retry-After 標頭。限制是以帳戶計算,由你所有金鑰共用。請等待 Retry-After 所示的秒數後再試;如有大量產品,請使用批量呼叫(每次請求最多 2,000 行)。

如何讓建議價格自動套用到我的商店?

用 store_kind = webhook 把群組連結到你的商店,設定 webhook 網址及密鑰,然後處理 price.apply 事件:在你的商店更改價格,再呼叫 POST /actions/{action_id}/confirm。批准價格更改仍然是在控制台由人員作出的決定。

有 SDK 或現成的外掛程式嗎?

沒有官方 SDK 或外掛程式。API 是以 JSON 為主的純 REST,所以任何能發出 HTTPS 請求的語言都適用;OpenAPI 規格亦可載入能自動產生客戶端的工具。