شروع
نشانی پایه https://api.dokkan.digital است. درخواست و پاسخ JSON با کدگذاری UTF-8 است. کاتالوگ مرجع فعلی
۲۸۴٬۶۶۰ کالای خوراکی، آرایشی-بهداشتی و عطر دارد و شناسهٔ هر کالا (ref_id) در پاسخ میآید.
کلید
هر درخواست به /match کلید میخواهد، در سرآیند x-api-key یا به شکل
Authorization: Bearer …. کلید را در کد سمت کاربر نگذارید.
برای ارزیابی یا آزمایش، کلید با سهمیهٔ جدا را از info@dokkan.digital بخواهید. دموی همین سایت با یک کلید عمومی کمسهمیه کار میکند که برای استفاده در برنامه مناسب نیست.
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
| فیلد | نوع | توضیح |
|---|---|---|
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.verdict | match (یکی از نامزدها همان کالاست)، 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"}