المطورون

واجهة برمجة التطبيقات للمطورين

اربط نظامك الخاص (CRM أو ERP أو المتجر الإلكتروني) بـ 4pricing واستعلم عن أسعار المنافسين ومجموعات المقارنة والتنبيهات مباشرةً من شيفرتك البرمجية.

العنوان الأساسي: https://4pricing.net/api/v1/pricing · مواصفات OpenAPI (JSON)

تكامل سريع في 3 خطوات

هذه أقصر طريقة لترى بيانات 4pricing على موقعك الخاص. تنسخ ملفًا واحدًا، وتلصق مفتاحك فيه، ثم ترفعه إلى استضافتك — دون أي إطار عمل ودون تثبيت.

ما الذي تحتاج إليه
  • اشتراك 4pricing نشط (الواجهة مضمّنة في كل باقة).
  • استضافة تدعم 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: ارفع الملف وافتحه في المتصفح

    ارفع 4pricing-prices.php إلى موقعك عبر مدير الملفات أو FTP، في المجلد نفسه الذي يوجد فيه موقعك (عادةً 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 = السعر × 100 (49.99 يصبح 4999).
$products = [
    [
        'sku' => 'WHITE-TSHIRT-M',
        'name' => 'قميص تي شيرت أبيض، مقاس 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":"قميص تي شيرت أبيض، مقاس 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_ فقط).
  • لا توجد نطاقات صلاحيات (scopes): يستطيع المفتاح قراءة كل شيء في حسابك على 4pricing وتغييره. أنشئ مفتاحًا لكل عملية تكامل (متجر، ERP، تقرير) كي تتمكن من إلغاء أحدها دون إيقاف البقية.
  • المفتاح المفقود أو الخاطئ أو الملغى يحصل على 401. ويسري الإلغاء فورًا. وكثرة المفاتيح غير الصالحة في دقيقة واحدة تحصل على 429.
  • تُنشأ المفاتيح وتُعرض وتُلغى في لوحة التحكم فقط (مراقبة الأسعار ← 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. أما النسب المئوية فبنقاط الأساس (100 = 1%).
  • القيمة المجهولة هي null وليست 0 أبدًا — اعرضها على هيئة «—».
  • تُستدعى المجموعات بشرطة مائلة في النهاية (/products/ و/groups/ و/insights/).
  • تُرجع القوائم المقسّمة إلى صفحات الإجمالي في الترويسة X-Total-Count (مثلًا GET /products/?skip=0&limit=200).
  • أرسل ترويسة User-Agent تُسمّي تكاملك (مثلًا my-shop-integration/1.0). يردّ جدار الحماية أمام الواجهة بالرمز 403 على بعض القيم الافتراضية العامة للمكتبات، مثل Python-urllib.

حد الطلبات والاشتراك وحدود الباقة

حد الطلبات

  • 60 طلبًا في الدقيقة لكل حساب (ACCOUNT)، ضمن نافذة متحركة — تتشارك جميع مفاتيح الحساب الواحد هذه الـ 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"}

الاشتراك وحدود الباقة

  • تحتاج الواجهة إلى اشتراك 4pricing نشط؛ وبدونه يحصل كل طلب على 402 مع الرمز plan_required.
  • تتضمن كل باقة الواجهة بالحدود نفسها المطبّقة في لوحة التحكم. وتجاوز أحد الحدود عند إضافة شيء أو إعادة تنشيطه يحصل على 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 والدور 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، سواء كان لك أو لمنافس (الدور own أو competitor).

الطريقةنقطة النهايةماذا تفعلالتكلفة
GET/products/الروابط المراقَبة، مقسّمة إلى صفحات (skip، وlimit حتى 500؛ والإجمالي في X-Total-Count)مجاني
POST/products/ابدأ مراقبة رابط (url وname والدور 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أعد بناء ملخص اليوم من الرؤى المفتوحة (لا يرسل شيئًا)مجاني

إجراءات الأسعار

تغييرات الأسعار التي اقتُرحت أو أُرسلت أو طُبّقت أو أُلغي تطبيقها. عبر الواجهة يمكنك قراءتها وتأكيد التي وصلتك عبر 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

ما لا تستطيع الواجهة فعله

بعض الأمور تبقى في لوحة التحكم عن قصد، حيث يقرّر شخص مسجَّل الدخول. عبر الواجهة تردّ هذه المسارات بالرمز 404:

المسارالسبب
/keys/…إنشاء المفاتيح وإلغاؤها — لا يستطيع المفتاح إنشاء مفاتيح أخرى.
/autopilot/…سياسة الطيار الآلي، التي تحدد ما يجوز تغييره دون تدخل شخص؛ ولا يجوز أن يتمكن مفتاح مسرَّب من توسيعها.
/watchlists/…قوائم المتابعة، التي تستهلك أرصدة عند كل تشغيل.
/actions/{action_id}/approve · reject · revertالموافقة على تغيير السعر أو رفضه أو التراجع عنه قرارٌ يتخذه شخص في لوحة التحكم. تستطيع الواجهة قراءة الإجراءات وتأكيدها فقط.
/insights/{insight_id}/act · /insights/runتنفيذ إجراء بناءً على رؤية (فهو يكتب الأسعار) وإعادة حساب الرؤى.
/admin/… · /internal/…الإدارة الداخلية للمنصة.

Webhooks

تستطيع 4pricing إرسال أحداث إلى عنوان تملكه (webhook): تُجري طلب POST بصيغة JSON كلما حدث شيء، فلا يضطر نظامك إلى السؤال باستمرار.

يوجد عنوان webhook واحد لكل حساب؛ وتذهب إليه كل أنواع الأحداث. ميّز بينها بالحقل event — والتنبيه هو الحدث الوحيد الذي لا يحمله.

التفعيل

  • اضبط alert_webhook_url (عنوان http/https عام على المنفذ 80 أو 443) و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= متبوعة بقيمة HMAC-SHA256 بالصيغة السداسية (hex) للنص “<timestamp>.<raw body>”، محسوبة بسرّ 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']
    // ثم أكّد: POST /actions/{action_id}/confirm مع مفتاح API الخاص بك
}
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)
      // ثم أكّد: POST /actions/{action_id}/confirm مع مفتاح API الخاص بك
    }
    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 مطبّقًا حتى يُعلن نظامك ذلك. بعد أن تغيّر السعر في متجرك، استدعِ POST /actions/{action_id}/confirm بمفتاح API الخاص بك: ينتقل الإجراء من «مُرسَل» إلى «مطبَّق».
  • التأكيد مرتين لا ضرر فيه — فالإجراء المطبَّق أصلًا يُعاد كما هو.
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 إما نص، أو كائن يحمل رمزًا يمكنك معالجته في برنامجك، أو — مع 422 — قائمة الحقول غير الصالحة.

الحالةالرمزالمعنى
401—مفتاح API مفقود أو خاطئ أو ملغى.
402plan_requiredلا يوجد في الحساب اشتراك 4pricing نشط.
402plan_limitسيتم تجاوز أحد حدود الباقة (الروابط المراقَبة، متاجر المنافسين) أو جاء تحديث قبل أدنى فاصل. وتوضح الرسالة أيّها.
402insufficient_creditsالأرصدة غير كافية لاستدعاء يقرأ صفحة متجر. لم تتم قراءة شيء ولم يُخصم شيء.
403—حجبه جدار الحماية قبل وصوله إلى الواجهة — غالبًا بسبب User-Agent عام مثل Python-urllib. أرسل User-Agent خاصًا بك.
404—المورد غير موجود، أو لا يخص حسابك، أو أن المسار غير متاح عبر الواجهة.
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؟

لا. يمنحك الدليل السريع ملف PHP جاهزًا: تنشئ مفتاحًا في لوحة التحكم، وتلصقه في الملف، وترفع الملف إلى استضافتك، ثم تفتحه في المتصفح. أما التكاملات الأعمق (نظام ERP أو منصة متجر) فيستطيع المطوّر الاستعانة فيها بمرجع نقاط النهاية ومواصفات OpenAPI.

كم تكلّف واجهة 4pricing؟

الواجهة مضمّنة في كل اشتراك 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 ولا تتم قراءة شيء.

هل تُحتسب الأسعار التي أرسلها عبر الواجهة ضمن حد الروابط في باقتي؟

لا. تُحتسب فقط العناوين التي تقرؤها 4pricing من صفحة — أي صفحات منتجاتك وصفحة كل منافس على حدة. والأسعار المرسلة عبر POST /own-products/ أو /own-products/bulk لا تُحتسب لا كروابط ولا كأرصدة.

هل أستطيع تحديد ما يمكن لمفتاح الوصول إليه؟

لا، لا توجد نطاقات صلاحيات: يرى المفتاح حسابك كله على 4pricing ويستطيع تغييره. استخدم مفتاحًا مستقلًا لكل عملية تكامل وألغِ أي مفتاح لم تعد تستخدمه؛ ويسري الإلغاء فورًا.

ماذا يحدث إذا أرسلت أكثر من 60 طلبًا في الدقيقة؟

تحصل على 429 مع ترويسة Retry-After. الحد مفروض لكل حساب ويتقاسمه جميع مفاتيحك. انتظر عدد الثواني المذكور في Retry-After ثم أعد المحاولة؛ ولعدد كبير من المنتجات استخدم الاستدعاءات الجماعية (bulk) (حتى 2,000 سطر في الطلب).

كيف أُدخل السعر الموصى به إلى متجري تلقائيًا؟

اربط المجموعة بمتجرك عبر store_kind = webhook، واضبط عنوان webhook وسرّه، وعالج الحدث price.apply: غيّر السعر في متجرك ثم استدعِ POST /actions/{action_id}/confirm. أما الموافقة على تغيير السعر فتبقى قرارًا يُتخذ في لوحة التحكم.

هل توجد SDK أو إضافة جاهزة؟

لا توجد SDK أو إضافة رسمية. الواجهة REST بسيطة بصيغة JSON، فتعمل معها أي لغة قادرة على إجراء طلبات HTTPS؛ ويمكن تحميل مواصفات OpenAPI في أدوات تولّد عميلًا برمجيًا تلقائيًا.