API کالاسنج

آزمایشی، نسخهٔ ۰٫۱

یک عنوان کالا بفرستید؛ نزدیک‌ترین کالاهای کاتالوگ مرجع و تصمیم کالاسنج را برمی‌گرداند: کدام نامزد همان کالاست، یا اینکه هیچ‌کدام نیست یا عنوان مبهم است.

شروع

نشانی پایه https://api.dokkan.digital است. درخواست و پاسخ JSON با کدگذاری UTF-8 است. کاتالوگ مرجع فعلی ۲۸۴٬۶۶۰ کالای خوراکی، آرایشی-بهداشتی و عطر دارد و شناسهٔ هر کالا (ref_id) در پاسخ می‌آید.

کلید

هر درخواست به /match کلید می‌خواهد، در سرآیند x-api-key یا به شکل Authorization: Bearer …. کلید را در کد سمت کاربر نگذارید.

برای ارزیابی یا آزمایش، کلید با سهمیهٔ جدا را از info@dokkan.digital بخواهید. دموی همین سایت با یک کلید عمومی کم‌سهمیه کار می‌کند که برای استفاده در برنامه مناسب نیست.

GET /health

GET/healthبدون کلید

وضعیت سرویس، نسخه، اندازهٔ کاتالوگ و تعداد درخواست‌های در حال اجرا یا در صف.

curl -s https://api.dokkan.digital/health
import requests

print(requests.get("https://api.dokkan.digital/health", timeout=10).json())
const health = await fetch("https://api.dokkan.digital/health").then((r) => r.json())
console.log(health.ok, health.version)
{"ok": true, "version": "0.1.1", "catalog": 284660, "busy": 0}

POST /match

POST/matchبا کلید
فیلدنوعتوضیح
titleرشته، ۱ تا ۳۰۰ نویسهعنوان کالا، همان‌طور که فروشنده نوشته است. الزامی.
brandرشته یا nullبرند، اگر می‌دانید. اگر نفرستید از خود عنوان تشخیص داده می‌شود؛ جستجو در کالاهای همان برند انجام می‌شود.
kعدد ۱ تا ۱۰تعداد نامزدها. پیش‌فرض ۱۰؛ کالاسنج با ۱۰ نامزد آموزش دیده است.
decideبولیپیش‌فرض true. با false فقط نامزدها برمی‌گردند و پاسخ چند برابر سریع‌تر است.
curl -s https://api.dokkan.digital/match \
  -H "x-api-key: $KALASANJ_KEY" \
  -H "content-type: application/json" \
  -d '{"title": "رب گوجه فرنگی چین چین 800 گرمی", "k": 5}'
import os
import requests

r = requests.post(
    "https://api.dokkan.digital/match",
    headers={"x-api-key": os.environ["KALASANJ_KEY"]},
    json={"title": "رب گوجه فرنگی چین چین 800 گرمی", "k": 5},
    timeout=30,
)
r.raise_for_status()
result = r.json()

decision = result["decision"]
if decision["verdict"] == "match":
    print(decision["ref_id"], round(decision["probability"], 3))
const res = await fetch("https://api.dokkan.digital/match", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-api-key": process.env.KALASANJ_KEY,
  },
  body: JSON.stringify({ title: "رب گوجه فرنگی چین چین 800 گرمی", k: 5 }),
})
if (!res.ok) throw new Error(`kalasanj: ${res.status}`)
const { candidates, decision } = await res.json()

if (decision.verdict === "match") {
  console.log(decision.ref_id, decision.probability.toFixed(3))
}

پاسخ

{
  "brand": "چین چین",
  "candidates": [
    {"rank": 0, "ref_id": 6832214, "title": "رب گوجه فرنگی چین چین - 800 گرم", "score": 0.971},
    {"rank": 1, "ref_id": 1717221, "title": "رب گوجه فرنگی چین چین مقدار 800 گرم", "score": 0.9597},
    {"rank": 2, "ref_id": 17492534, "title": "رب گوجه فرنگی چین چین - 800 گرم بسته 4 عددی", "score": 0.7939},
    {"rank": 3, "ref_id": 17492575, "title": "رب گوجه فرنگی چین چین - 800 گرم بسته 6 عددی", "score": 0.7382},
    {"rank": 4, "ref_id": 1717277, "title": "رب گوجه فرنگی چین چین مقدار 700 گرم", "score": 0.7248}
  ],
  "decision": {
    "verdict": "match",
    "probability": 0.9843594431877136,
    "probabilities": {
      "candidate_1": 0.984359, "candidate_2": 0.009452, "candidate_3": 0.000552,
      "candidate_4": 0.000189, "candidate_5": 0.000067,
      "none": 0.000838, "ambiguous": 0.004542
    },
    "ref_id": 6832214
  },
  "timings_ms": {"retrieve_ms": 320.7, "search_ms": 0.9, "decide_ms": 2651.9, "total_ms": 2973.6}
}
فیلدتوضیح
brandبرندی که جستجو در آن انجام شد، یا null.
candidatesنامزدها به ترتیب شباهت. rank از صفر شروع می‌شود؛ score شباهت بازیاب است، نه احتمال.
decision.verdictmatch (یکی از نامزدها همان کالاست)، none (هیچ‌کدام: در نوع، اندازه، تعداد در بسته یا خود کالا فرق دارند) یا ambiguous (عنوان برای انتخاب کافی نیست).
decision.probabilityاحتمال گزینهٔ انتخاب‌شده. کالیبره است؛ برای پذیرش خودکار آستانه بگذارید و بقیه را به بازبینی انسانی بفرستید.
decision.probabilitiesاحتمال همهٔ گزینه‌ها: candidate_1 تا candidate_k به ترتیب نامزدها، و none و ambiguous.
decision.ref_idشناسهٔ کالای انتخاب‌شده، یا null.
timings_msزمان هر مرحله روی سرور، به میلی‌ثانیه.

با decide: false فیلد decision در پاسخ نیست.

نامزدها اول، تصمیم بعد

بازیابی و جستجو معمولاً زیر یک ثانیه است (اگر برند در عنوان شناخته نشود و جستجو در کل کاتالوگ باشد، تا حدود دو ثانیه). تصمیم کالاسنج روی سرور فعلی حدود سه ثانیهٔ دیگر می‌گیرد. اگر کاربری منتظر پاسخ است، اول با decide: false نامزدها را نشان دهید و بعد تصمیم را بگیرید. هر کدام یک درخواست از سهمیه حساب می‌شود.

async function match(title, decide) {
  const res = await fetch("https://api.dokkan.digital/match", {
    method: "POST",
    headers: { "content-type": "application/json", "x-api-key": KEY },
    body: JSON.stringify({ title, decide }),
  })
  if (res.status === 429) {
    const wait = Number(res.headers.get("retry-after"))
    throw new Error(`quota: retry in ${wait}s`)
  }
  if (!res.ok) throw new Error(`kalasanj: ${res.status}`)
  return res.json()
}

const fast = await match(title, false)   // ~0.1-2 s: candidates only
showCandidates(fast.candidates)
const full = await match(title, true)    // ~3 s more: the decision
showDecision(full.decision)

سهمیه و محدودیت

کلیددر دقیقهدر روز
دموی عمومی این سایت (برای همهٔ بازدیدکنندگان روی هم)۳۰۲٬۰۰۰
کلید ارزیابی و آزمایشجدا، برای هر کلید
  • هر درخواست به /match، با تصمیم یا بی‌تصمیم، یک واحد از سهمیه است. سهمیهٔ روزانه ساعت ۰۰:۰۰ UTC نو می‌شود.
  • سرور در هر لحظه یک درخواست را اجرا می‌کند و تا چهار درخواست دیگر را در صف نگه می‌دارد. بیش از آن، یا انتظار بیش از ۲۰ ثانیه، پاسخ ۵۰۳ می‌گیرد.
  • بدنهٔ درخواست حداکثر ۴ کیلوبایت است.
  • API آزمایشی است و روی سروری مشترک اجرا می‌شود؛ اگر بار سرور بالا برود، به‌طور خودکار موقتاً متوقف می‌شود.

خطاها

کدمعناچه باید کرد
401کلید نیامده یا نامعتبر است.سرآیند x-api-key را بررسی کنید.
413بدنهٔ درخواست بزرگ‌تر از ۴ کیلوبایت است.فقط فیلدهای بالا را بفرستید.
422بدنه معتبر نیست؛ مثلاً عنوان خالی یا بلندتر از ۳۰۰ نویسه.detail فیلد مشکل‌دار را می‌گوید.
429سهمیهٔ کلید تمام شده است.به اندازهٔ Retry-After (ثانیه) صبر کنید.
503صف پر است یا سرویس موقتاً متوقف شده.بعد از Retry-After دوباره بفرستید.
HTTP/2 429
retry-after: 41
content-type: application/json

{"detail": "quota exceeded"}