開發者 API
將你自己的系統(CRM、ERP、網上商店)連接到 4pricing,直接從程式碼查詢競爭對手價格、比較組別及提醒。
基本網址: https://4pricing.net/api/v1/pricing · OpenAPI 規格(JSON)
三步快速整合
這是在你自己網站上看到 4pricing 資料的最快方法。你只需複製一個檔案、把金鑰貼進去,再上載到你的網站寄存空間 —— 毋須任何框架,亦毋須安裝任何東西。
- 有效的 4pricing 訂閱(每個方案都包含 API)。
- 支援 PHP 的網站寄存服務 —— 幾乎所有共享寄存服務都有支援(例如 cPanel、Plesk 等)。
- 上載檔案的方法:寄存服務的檔案管理員,或 FTP 程式。
建立你的 API 金鑰
登入 panel.4pricing.net,打開「價格監察 → API 與金鑰」。
按「產生金鑰」,為它改個名稱(例如「我的網店」),然後複製以 prc_live_ 開頭的金鑰。金鑰只會顯示一次 —— 如果弄丟了,請另建一個,並撤銷舊的。
複製現成檔案並貼上你的金鑰
把下面的 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 換成你的金鑰,並保留金鑰兩旁的引號。這是你唯一需要修改的一行。上載檔案並用瀏覽器打開
用檔案管理員或 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-Signature | sha256= 後接文字「<timestamp>.<raw body>」的十六進位 HMAC-SHA256,以你的 webhook 密鑰計算。設定了密鑰時就會出現(price.apply 及 price.revert 一律會有)。 |
| Content-Type | application/json; charset=utf-8 |
| User-Agent | 4pricing-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 金鑰缺少、錯誤或已被撤銷。 |
| 402 | plan_required | 帳戶沒有有效的 4pricing 訂閱。 |
| 402 | plan_limit | 會超出方案限制(受監察網址、競爭對手商店),或者在最短間隔之前就要求重新整理。訊息會說明是哪一項。 |
| 402 | insufficient_credits | 點數不足,無法進行需要讀取商店頁面的呼叫。沒有讀取任何內容,亦沒有扣除點數。 |
| 403 | — | 在到達 API 之前已被防火牆封鎖 —— 通常是因為使用了通用的 User-Agent,例如 Python-urllib。請傳送你自己的 User-Agent。 |
| 404 | — | 資源不存在、不屬於你的帳戶,或者該路徑無法透過 API 存取。 |
| 409 | recommendation_moved · own_from_listing · invalid_state · webhook_secret_required | 衝突:建議價格已改變(recommendation_moved)、動作處於另一個狀態(invalid_state)、需要 webhook 密鑰,或者該操作不適用於此項目。 |
| 422 | — | 資料無效,例如網址沒有 http/https,或缺少必填欄位。detail 會列出相關欄位。 |
| 429 | — | 帳戶每分鐘超過 60 次請求(或使用了太多無效金鑰)。請等待 Retry-After 指定的秒數。 |
| 503 | credit_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 規格亦可載入能自動產生客戶端的工具。