Open Food Facts API sản phẩm: barcode, khẩu phần và dữ liệu thiếu
Open Food Facts API sản phẩm là cách tra thông tin thực phẩm theo mã vạch từ cơ sở dữ liệu cộng đồng. Bài này viết cho người dựng bảng dinh dưỡng cần phân biệt giá trị thiếu với số 0 và giữ nguồn đóng góp. ProxyVN đã gọi sáu mã vạch thật ngày 05/11/2026, thấy mã sai vẫn trả HTTP 200 và một mã ngắn bị đệm thành sản phẩm khác.
Open Food Facts API sản phẩm: mục tiêu và phạm vi không lặp
Phần này chốt bài toán, đầu ra và ranh giới với các bài lấy dữ liệu sản phẩm khác.
Bài toán cần giải
Một ứng dụng theo dõi khẩu phần cần hiện năng lượng, đường và muối khi người dùng quét mã vạch. Nhóm dựng bảng từ API, tính lượng đường theo khẩu phần, rồi nhận phản hồi rằng một lon nước ngọt có số quá cao. Một sản phẩm khác hiện 0 gam chất xơ dù nhãn không ghi. Cả hai lỗi đến từ cách đọc dữ liệu cộng đồng như thể đó là số liệu đã kiểm định.
Đầu ra nghiệm thu
Đầu ra là dataset gồm barcode chuẩn hóa, tên, khẩu phần gốc, khẩu phần số, cơ sở dinh dưỡng, từng nutrient với đơn vị và cờ thiếu. Đi kèm là cột nguồn: người tạo, người sửa cuối, thời điểm sửa cuối, cảnh báo chất lượng. Bảng phải cho biết dòng nào có thể dùng, dòng nào cần người xem lại.
Ranh giới với bài JSON-LD
Bài trích JSON-LD Product bằng Python lấy dữ liệu có cấu trúc từ HTML của trang bán hàng. Bài này không tải HTML. Nó dùng API của một cơ sở dữ liệu mở, khóa theo mã vạch và có mô hình đóng góp riêng. Hai bài giải hai việc khác nhau, nên code và rủi ro cũng khác.
Giới hạn của lab
Lab gọi sáu mã vạch và một truy vấn tìm kiếm theo quốc gia. Mẫu tìm kiếm chỉ là 24 sản phẩm đầu của Việt Nam, không đại diện cho toàn bộ 1681 sản phẩm API báo. Lab không sửa, không ghi gì lên cơ sở dữ liệu. Tên sản phẩm trong bài chỉ để minh họa cách đọc dữ liệu, không đánh giá sản phẩm.
Ai nên đọc
Bài hợp với người làm ứng dụng thực phẩm, nhóm nghiên cứu dinh dưỡng, người viết nội dung cần tra nhãn nhanh. Kỹ sư dữ liệu nên đọc phần mã vạch và phần giá trị thiếu. Người tìm số liệu cho tư vấn y tế không nên dùng nguồn này làm căn cứ duy nhất.
Những gì bài không làm
Bài không tư vấn dinh dưỡng. Bài không đánh giá Nutri-Score hay điểm môi trường. Bài không hướng dẫn ghi dữ liệu lên Open Food Facts, vì việc đó cần tài khoản riêng và phạm vi khác.
Thuật ngữ dùng trong bài
Barcode là chuỗi số EAN hoặc UPC in trên bao bì. Serving là khẩu phần do nhãn hoặc người đóng góp khai. Nutriments là khối số liệu dinh dưỡng trong JSON. Provenance là thông tin ai nhập, nhập lúc nào. Từ đây anh em sẽ thấy mỗi phần bài đều xoay quanh câu hỏi số này tin được tới đâu.
Tra sản phẩm theo barcode và xử lý dữ liệu cộng đồng, giấy phép với Open Food Facts API sản phẩm
Phần này đi qua điều kiện dùng API và lời gọi đầu tiên, vì giới hạn ở đây chặt hơn nhiều người nghĩ.
Nguồn tài liệu đã đọc
Lab đọc trang giới thiệu API của Open Food Facts. Trang này nêu giới hạn gọi, cách đặt User-Agent, phiên bản API và giấy phép dữ liệu. Tài liệu cũng nói rõ dữ liệu do cộng đồng đóng góp, không có bảo đảm về độ chính xác và người dùng tự chịu rủi ro.
Giới hạn gọi
Tài liệu ghi 15 yêu cầu mỗi phút mỗi IP cho đọc sản phẩm và 10 yêu cầu mỗi phút mỗi IP cho tìm kiếm. Con số này thấp, nên quét hàng loạt bằng API là cách sai. Lab nghỉ bảy giây giữa các lần tra sản phẩm, tức dưới chín lượt mỗi phút. Với nhu cầu tải lớn, Open Food Facts cung cấp bản xuất dữ liệu riêng, anh em nên dùng bản đó.
User-Agent bắt buộc về thực tế
Tài liệu yêu cầu User-Agent riêng theo dạng AppName/Version (ContactEmail), và nói việc đọc không cần xác thực ngoài điều này. Lab dùng ProxyVN-docs-lab/1.0 kèm URL trang web, không ghi email cá nhân. Ứng dụng thật nên dùng email hỗ trợ của dự án để nhà cung cấp liên hệ khi có sự cố. Tài liệu cũng mời người dùng điền biểu mẫu mô tả cách sử dụng API.
Lời gọi đầu tiên
Endpoint đọc là /api/v2/product/{barcode}.json với tham số fields để giới hạn trường. Không có fields thì response rất lớn. Lab xin đúng các trường cần cho bảng nghiệm thu.
import requests, time
s = requests.Session()
s.headers["User-Agent"] = "ProxyVN-docs-lab/1.0 (+https://proxy.vn/)"
B = "https://world.openfoodfacts.org/api/v2/product/"
F = ("code,product_name,brands,quantity,serving_size,serving_quantity,"
"nutrition_data_per,nutriments,last_modified_t,last_editor,creator,"
"completeness,data_quality_warnings_tags")
def get_product(code):
r = s.get(B + code + ".json", params={"fields": F}, timeout=60)
time.sleep(7)
return r.status_code, r.json()
st, j = get_product("3017624010701")
print(st, j["status"], j["status_verbose"], j["product"]["product_name"])
Output thật là 200, 1, product found, Nutella. Trường status trong JSON mới là tín hiệu tìm thấy hay không, như phần sau sẽ cho thấy.
Giấy phép có ba lớp
Cơ sở dữ liệu theo Open Database License. Nội dung từng mục theo Database Contents License. Ảnh sản phẩm theo Creative Commons Attribution ShareAlike, và có thể chứa yếu tố có bản quyền khác trên bao bì. Ba lớp này khác nhau, nên một sản phẩm phái sinh dùng dữ liệu cần đọc kỹ điều kiện chia sẻ lại. Dùng ảnh bao bì còn cần thận trọng hơn dùng số liệu.
ODbL ảnh hưởng gì tới bảng của anh em
ODbL yêu cầu ghi nguồn, và với cơ sở dữ liệu phái sinh công khai thì có điều kiện chia sẻ tương tự. Bảng nội bộ dùng để phân tích khác với API công khai dựa trên dữ liệu này. Trang tài liệu trỏ tới điều khoản và một trang wiki về ODbL. Bài này không phải tư vấn pháp lý, nên dự án thương mại nên hỏi người phụ trách pháp lý.
Chỉ đọc, không ghi
Toàn bộ lab là GET, không dùng tài khoản. Việc sửa sản phẩm sai trên cơ sở dữ liệu là đóng góp cộng đồng và cần tài khoản riêng. Khi phát hiện dòng lỗi, bảng của anh em chỉ gắn cờ. Việc sửa gốc là quyết định riêng của người có tài khoản.
Lấy sản phẩm theo barcode và chọn fields trong Open Food Facts API sản phẩm
Mã vạch nhìn đơn giản nhưng là nơi có các lỗi im lặng nguy hiểm nhất của lab.
Kết quả sáu mã vạch

HTTP 200 chưa đủ
Lab tra sáu chuỗi, gồm sản phẩm thật, cùng mã có và không có số 0 đầu, mã toàn số 0 và mã quá ngắn. Mọi lượt đều trả HTTP 200.
Chuỗi gửi |
HTTP |
status |
code trả về |
Sản phẩm |
|---|---|---|---|---|
3017624010701 |
200 |
1 |
3017624010701 |
Nutella, Ferrero |
0737628064502 |
200 |
1 |
0737628064502 |
Thai peanut noodle kit |
737628064502 |
200 |
1 |
0737628064502 |
Cùng sản phẩm trên |
8934588012112 |
200 |
1 |
8934588012112 |
Pepsi 320ml |
0000000000000 |
200 |
0 |
00000000 |
no code or invalid code |
12345 |
200 |
1 |
00012345 |
2 Pâtes feuilletées |
HTTP 200 không có nghĩa là tìm thấy
Mã toàn số 0 trả HTTP 200 với status 0 và status_verbose là no code or invalid code. Script chỉ kiểm r.ok sẽ đi tiếp rồi lỗi ở chỗ khác. Kiểm đúng là đọc status trong JSON. Lỗi này nhỏ nhưng xuất hiện ở hầu hết code mẫu copy nhanh.
Mã ngắn bị đệm thành sản phẩm khác
Đây là phát hiện đáng lưu ý nhất. Chuỗi 12345 không phải mã vạch thật, nhưng API đệm thành 00012345 và trả một sản phẩm do cộng đồng nhập dưới mã đó. Tên sản phẩm là 2 Pâtes feuilletées, trường brands ghi 12312. Một đầu vào rác có thể trả về một sản phẩm có vẻ hợp lệ. Ứng dụng đọc mã sai từ camera sẽ hiện thông tin của một món không liên quan.
Chuẩn hóa mã trước khi gọi
Trước khi gọi, anh em kiểm độ dài và số kiểm tra của mã. Mã EAN-13, EAN-8 và UPC-A đều có chữ số cuối là số kiểm tra. Chuỗi 12345 không qua được phép kiểm độ dài, nên sẽ bị chặn ngay tại ứng dụng. Dưới đây là hàm kiểm của lab.
def gtin_ok(c):
if not c.isdigit() or len(c) not in (8, 12, 13, 14):
return False
d = [int(x) for x in c][::-1]
s = sum(x * (3 if i % 2 == 0 else 1) for i, x in enumerate(d[1:]))
return (10 - s % 10) % 10 == d[0]
for c in ["3017624010701", "0737628064502", "737628064502",
"8934588012112", "0000000000000", "12345"]:
print(c, gtin_ok(c))
Hàm này cho phép mã toàn số 0 vì số kiểm tra của nó đúng về mặt công thức, nên anh em cần thêm luật chặn riêng. Mã 12 số là dạng UPC-A, còn API lưu dạng 13 số có số 0 đầu.
Lưu mã dạng chuỗi
Cột barcode phải là chuỗi. Đọc vào Excel hay pandas dạng số thì số 0 đầu biến mất, và 0737628064502 thành 737628064502. API vẫn tìm được trong ca này, nhưng khi ghép với bảng khác thì khóa lệch. Lab lưu cả chuỗi gửi và trường code trả về.
Ghép theo mã chuẩn hóa
Khóa ghép nên là mã 13 số đệm trái, kể cả với mã 12 số. Trường code trong response là mốc tham chiếu vì cho thấy cách API lưu. Trong lab, 737628064502 và 0737628064502 quy về cùng một dòng. Không có bước này, cùng một sản phẩm sẽ thành hai dòng trong bảng.
Chọn fields cho nhẹ
Tham số fields giảm mạnh dung lượng và giúp anh em ý thức mình dùng gì. Khối nutriments vẫn lớn vì chứa nhiều biến thể của mỗi chất. Ở bước lưu thô, anh em giữ nguyên JSON đã lọc. Bước chuẩn hóa mới chọn các cột cuối.
Mã không tìm thấy thì ghi gì
Dòng không tìm thấy cũng là dữ liệu. Bảng ghi barcode, status, thời điểm tra. Lần tra lại sau một tháng có thể có kết quả vì cộng đồng đã thêm. Không ghi thì script sẽ tra đi tra lại, tốn hạn mức rất thấp đã nêu.
Chuẩn hóa serving size, nutrient unit và missing value với Open Food Facts API sản phẩm
Khối nutriments cần đọc cẩn thận, vì một chất có nhiều biến thể khóa và cơ sở tính khác nhau.
Khối nutriments có gì

Khẩu phần là chuỗi tự do
Với Nutella, lab thấy energy-kcal_100g 539, energy-kj_100g 2228, energy_100g 2228, energy_unit kJ. Đường có sugars_100g 56.3 và sugars_unit g. Muối có salt_100g 0.1075, natri có sodium_100g 0.043. Khóa có đuôi _100g là theo 100 gam hoặc 100 ml, khóa có đuôi _serving là theo khẩu phần.
Energy không ghi đơn vị là kcal
Trường energy_100g của Nutella là 2228 với đơn vị kJ, không phải kcal. Người đọc nhanh sẽ lấy nhầm con số này và báo 2228 kcal cho 100 gam. Bảng nên dùng energy-kcal_100g khi có, và đọc energy_unit khi phải dùng energy_100g. Ở sản phẩm Pepsi, energy-kj_100g là None trong khi energy_100g có 585, nên đừng giả định mọi khóa đều có mặt.
Serving size là chuỗi tự do
Nutella không có serving_size. Gói mì Thái ghi "0.333 PACKAGE (52 g)" với serving_quantity 52. Mẫu 24 sản phẩm Việt Nam có các dạng như "1 package (140 g)", "2.5 g", "1 serving (30 g)", "100.0ml", "60 Pieces (7 g)", "1 portion (105 g)". Chuỗi này là văn bản từ nhãn và do người nhập, nên không parse được bằng một quy tắc duy nhất.
Ưu tiên serving_quantity
Trường serving_quantity đã là số, nên dùng trước khi tự đọc chuỗi. Gói mì có 52 và Pâtes feuilletées có 30. Khi trường này thiếu, regex tìm số có đơn vị trong ngoặc đơn là cách an toàn hơn đọc số đầu chuỗi. Với "0.333 PACKAGE (52 g)", số đầu là 0.333 chứ không phải khối lượng.
import re
def serving_g(p):
q = p.get("serving_quantity")
if q not in (None, ""):
return float(q), "serving_quantity"
t = p.get("serving_size") or ""
m = re.search(r"\(\s*([\d.,]+)\s*(g|ml)\s*\)", t, re.I)
if m:
return float(m.group(1).replace(",", ".")), "regex_ngoac"
m = re.fullmatch(r"\s*([\d.,]+)\s*(g|ml)\s*", t, re.I)
if m:
return float(m.group(1).replace(",", ".")), "regex_tron"
return None, "khong_doc_duoc"
Hàm trả cả giá trị lẫn cách đọc được, để bảng biết dòng nào đến từ regex. Mục "60 Pieces (7 g)" cho thấy khẩu phần 7 gam có thể mơ hồ, và anh em nên gắn cờ cho người xem lại.
Cơ sở dinh dưỡng 100g hay 100ml
Trường nutrition_data_per trong mẫu 24 sản phẩm có 18 dòng 100g, 3 dòng 100ml, 3 dòng rỗng. Đồ uống dùng 100ml nên không so trực tiếp với 100g nếu chưa biết khối lượng riêng. Bảng giữ cột này, và chỉ so các dòng cùng cơ sở. Khóa _100g trong API được dùng cho cả hai trường hợp, nên tên khóa dễ gây hiểu nhầm.
Tính theo khẩu phần
Gói mì ghi energy-kcal_serving 200 với khẩu phần 52 gam, và 385 kcal mỗi 100 gam. Lab tính 385 nhân 0.52 ra 200.2, khớp với 200 sau làm tròn. Phép tính này là cách kiểm nhanh tính nhất quán của một dòng. Khi lệch nhiều, dòng đó cần cờ.
Bảng chênh theo sản phẩm
Sản phẩm |
serving |
kcal/100 |
Đường/100 |
Xơ/100 |
Muối/100 |
|---|---|---|---|---|---|
Nutella |
không có |
539 |
56.3 |
không có |
0.1075 |
Thai peanut noodle kit |
52 g |
385 |
13.46 |
1.9 |
0.72 |
Pepsi 320ml |
không có |
140 |
35 |
0 |
không có |
2 Pâtes feuilletées |
30 g |
98 |
10 |
1.3 |
0.94 |
Đơn vị trong nutriments
Các trường _unit như sugars_unit ghi g. Số _100g của API đã được quy về đơn vị chuẩn của trường đó. Trường _value và _unit phản ánh cách người dùng nhập. Muối và natri được quy theo gam, nên anh em đừng đọc 0.043 là miligam. Bảng cuối nên ghi đơn vị trong tên cột, ví dụ sugars_g_per_100.
Giữ nguồn dữ liệu cộng đồng cùng license trong Open Food Facts API sản phẩm
Dữ liệu cộng đồng có giá trị khi anh em biết nó đến từ đâu. Phần này giữ lại những trường đó.
Trường provenance lab đọc được
Response có creator, last_editor, last_modified_t, completeness. Nutella do openfoodfacts-contributors tạo, người sửa cuối là krishp, sửa cuối đầu tháng 10/2026, completeness 0.7625. Pepsi do kiliweb tạo, người sửa cuối lamle, sửa cuối tháng 1/2024, completeness 0.575. Gói mì có người sửa cuối là mephisto-bot, là tài khoản dạng bot theo tên gọi.
Đọc completeness thế nào
Completeness là một chỉ số độ đầy đủ các ô thông tin. Nó không đo độ chính xác. Sản phẩm Pepsi có độ đầy đủ 0.575 và số liệu đáng ngờ, nhưng một sản phẩm 0.9 vẫn có thể sai. Dùng trường này để ưu tiên việc xem lại, không dùng để kết luận dòng đúng.
Cảnh báo chất lượng có sẵn
Trường data_quality_warnings_tags là tài sản đáng dùng nhất. Pepsi có en:nutrition-value-very-high-for-category-energy, cùng cảnh báo tương tự cho carbohydrates và sugars. Pâtes feuilletées có en:nutrition-salt-does-not-match-sodium. Nutella có cảnh báo về ngôn ngữ thành phần. Hệ thống của Open Food Facts đã làm giúp phần lọc thô.
Ca Pepsi trong lab
Sản phẩm 320ml ghi 140 kcal và 35 gam đường trên cơ sở 100g. Con số này rất cao cho đồ uống, và hệ thống đã gắn cảnh báo. Một giả thuyết hợp lý là người nhập lấy số của cả lon làm số trên 100. Lab không xác minh được điều đó nếu không có nhãn thật. Giả thuyết ghi vào ghi chú, không tự sửa số.
Ca muối và natri
Muối thường xấp xỉ 2.5 lần natri. Pâtes feuilletées có salt 0.94 và sodium 0.0002032, lệch rất xa, trùng với cảnh báo của hệ thống. Nutella có 0.1075 và 0.043, tỷ lệ đúng 2.5. Phép kiểm tỷ lệ này chạy được ngay trong pipeline của anh em.
def check_salt(n):
a, b = n.get("salt_100g"), n.get("sodium_100g")
if a in (None, "") or b in (None, "") or not float(b):
return "khong_kiem_duoc"
r = float(a) / float(b)
return "ok" if 2.3 <= r <= 2.7 else f"lech_{r:.1f}"
Ngưỡng 2.3 tới 2.7 là tự đặt để minh họa, dựa trên quan hệ muối và natri. Anh em chỉnh theo dự án.
Đổi thời gian sửa cuối
Trường last_modified_t là giây epoch. Lưu cả dạng số và dạng ngày UTC giúp lọc dòng cũ. Một sản phẩm không ai sửa từ 2024 có thể đã đổi công thức trên thị trường. Cột này không kết luận gì, nhưng cho người đọc biết tuổi của dữ liệu.
Ghi nguồn trong sản phẩm
Ứng dụng hiện thông tin nên ghi nguồn Open Food Facts tại chỗ người dùng nhìn thấy, kèm ghi chú dữ liệu cộng đồng. Tài liệu còn nhắc không có bảo đảm về độ chính xác. Với thông tin dị ứng, ứng dụng nên nhắc người dùng đọc nhãn thật. Lab không kiểm dữ liệu dị ứng, nên bài dừng ở đó.
Đối soát trường thiếu với 0 và tránh ghép barcode bị đổi định dạng trong Open Food Facts API sản phẩm
Phân biệt "không có" với "bằng 0" là nơi bảng dinh dưỡng hay sai nhất.
Ba trạng thái của một nutrient

Thiếu khác số 0
Một nutrient có thể ở ba trạng thái: có số dương, có số 0, không có khóa. Nutella không có fiber_100g. Pepsi có fiber_100g bằng 0. Hai ca này khác nhau: một bên chưa ai nhập, một bên có giá trị 0. Trong mẫu 24 sản phẩm Việt Nam, 11 dòng thiếu khóa chất xơ và 4 dòng ghi 0.
Lỗi của dict.get mặc định
Code hay viết n.get("fiber_100g", 0) biến thiếu thành 0. Biểu đồ sau đó vẽ chất xơ bằng 0 cho sản phẩm chưa có dữ liệu. Trung bình của cả nhóm cũng bị kéo xuống. Dùng None làm mặc định, rồi đếm số thiếu riêng.
COLS = ["energy-kcal_100g", "sugars_100g", "fiber_100g", "salt_100g", "proteins_100g"]
def row(p):
n = p.get("nutriments", {})
out = {"code": str(p["code"]).zfill(13)}
for c in COLS:
out[c] = n.get(c)
out[c + "_state"] = "thieu" if c not in n else ("khong" if n[c] == 0 else "co")
return out
Cột trạng thái làm cho phép lọc rõ ràng. Báo cáo ghi "11 trên 24 thiếu dữ liệu chất xơ", đó là câu đúng hơn "chất xơ trung bình thấp".
Tỷ lệ thiếu trong mẫu Việt Nam
Trong 24 sản phẩm đầu, 9 dòng không có serving_size, 5 dòng thiếu sugars_100g, 3 dòng không có nutrition_data_per. Có 23 mã 13 số và 1 mã 8 số. Đây chỉ là mẫu đầu của một truy vấn, không nói lên độ đầy đủ của toàn bộ 1681 sản phẩm. Nhưng nó đủ để thấy bảng phải được thiết kế cho dữ liệu thiếu.
Tìm kiếm có thể không sẵn sàng
Lab gọi /api/v2/search với lọc Việt Nam và nhận HTTP 503 với trang HTML Page temporarily unavailable. Lab chuyển sang endpoint cgi/search.pl và nhận JSON với count 1681. Lý do 503 không rõ. Script phải kiểm content-type trước khi đọc JSON, nếu không sẽ gặp JSONDecodeError.
r = s.get("https://world.openfoodfacts.org/cgi/search.pl", params={
"action": "process", "tagtype_0": "countries", "tag_contains_0": "contains",
"tag_0": "vietnam", "json": 1, "page_size": 24,
"fields": "code,product_name,serving_size,nutrition_data_per,nutriments",
}, timeout=60)
if "json" not in r.headers.get("content-type", ""):
raise RuntimeError(f"khong phai JSON: {r.status_code}")
j = r.json()
print(j["count"], len(j["products"]))
Tài liệu có phần so sánh các phiên bản search, nên anh em đọc lại trang đó khi triển khai. Tìm kiếm chịu giới hạn 10 lượt mỗi phút, chặt hơn đọc sản phẩm.
Ghép với bảng hàng hóa nội bộ
Bảng nội bộ thường lưu mã vạch ở dạng số hoặc có khoảng trắng. Bước ghép cần loại ký tự không phải số, đệm trái 13 số, rồi mới so. Tỷ lệ ghép được phải được ghi lại. Dòng không ghép được xếp riêng để xem lại chứ không bỏ im lặng.
Mã nội bộ và mã 2xx
Mã bắt đầu bằng 2 thường là mã nội bộ của cửa hàng hoặc mã có cân trọng lượng, nên một mã có thể trỏ tới nhiều mặt hàng tùy nơi. Lab chưa gặp ca này trong mẫu. Anh em nên gắn cờ các mã như vậy nếu bảng nhập từ siêu thị.
Biến thể cùng sản phẩm
Cùng tên sản phẩm có thể có nhiều mã vạch theo cỡ gói hoặc thị trường. Công thức có thể khác nhau giữa các nước. Không gộp theo tên. Khóa vẫn là mã vạch, tên chỉ để đọc.
Đường tải và vai trò proxy với Open Food Facts API sản phẩm
Phần này tách việc tải khỏi việc xử lý và nói rõ khi nào nên dùng bản xuất dữ liệu.
Chọn API hay bản xuất
Nhu cầu |
Cách hợp |
Lý do |
|---|---|---|
Người dùng quét từng mã |
API sản phẩm |
Dữ liệu mới, ít lượt |
Phân tích cả quốc gia |
Bản xuất dữ liệu |
Giới hạn 15 lượt mỗi phút quá thấp |
Tìm theo danh mục, vài trang |
API search |
Tối đa 10 lượt mỗi phút |
Kiểm chất lượng hàng loạt |
Bản xuất rồi xử lý offline |
Không tốn hạn mức |
Bộ nhớ đệm cho ứng dụng quét
Ứng dụng nên cache kết quả theo mã trong một khoảng thời gian, kể cả kết quả không tìm thấy. Hai người quét cùng sản phẩm không cần hai lượt gọi. Cache cũng giúp ứng dụng chạy khi API tạm lỗi. Tài liệu có gợi ý cách xử lý downtime cho ứng dụng cần phản hồi ngay.
Proxy có cần không
Giới hạn tính theo IP, nhưng dùng nhiều IP để vượt là làm trái ý nhà cung cấp và có nguy cơ bị chặn. Cần nhiều dữ liệu thì dùng bản xuất, đó là con đường họ đã chuẩn bị. Bài rate limit API khi dùng proxy có cách đặt nhịp. Proxy chỉ hợp khi mạng công ty bắt buộc đi qua cổng ra cố định.
Khai báo proxy khi cần
Khi đó anh em đặt ở tầng session, theo định dạng proxy mà nhóm hạ tầng cấp. Lab không dùng proxy, nên không có số đo. Các bước chuẩn hóa dinh dưỡng chạy offline, không liên quan tới mạng.
Với tác vụ khác cần IP trong nước, anh em có thể mua proxy giá rẻ tại trang chủ ProxyVN.
Luồng xử lý khi người dùng quét mã
Luồng gọn cho ứng dụng có bốn bước. Bước một kiểm độ dài và số kiểm tra của mã ngay trên máy người dùng. Bước hai tra cache theo mã 13 số. Bước ba gọi API nếu cache không có, rồi đọc status. Bước bốn hiện kết quả kèm nhãn dữ liệu cộng đồng và cảnh báo nếu có.
Khi API tạm lỗi
Lab gặp một lần 503 ở endpoint search, nên ứng dụng cần một nhánh cho tình huống này. Với ứng dụng quét, hiện kết quả cache cũ kèm ngày tải vẫn tốt hơn báo lỗi trống. Nếu không có cache, ứng dụng báo chưa tra được và cho người dùng thử lại sau. Không nên tự động thử lại liên tục, vì giới hạn gọi tính theo phút.
Khóa phiên bản
Lab chạy Python 3.12.10, requests 2.34.2, API v2 trên world.openfoodfacts.org. Tài liệu có cả v3 cho một số tính năng. Cấu hình ghi rõ đường dẫn đang dùng, và giữ vài JSON mẫu làm test.
Đầu ra nghiệm thu dataset barcode, serving, unit, nutrient và provenance của Open Food Facts API sản phẩm
Mục này chốt bảng cuối và danh sách kiểm.
Schema của bảng

Nghiệm thu dataset
Cột |
Ví dụ thật |
Ghi chú |
|---|---|---|
barcode_sent |
737628064502 |
Chuỗi gửi |
code |
0737628064502 |
Chuỗi API trả |
status |
1 |
Từ JSON |
serving_raw |
0.333 PACKAGE (52 g) |
Nguyên văn |
serving_g |
52 |
Cách đọc: serving_quantity |
data_per |
100g |
Cơ sở |
fiber_g_per_100 |
1.9 |
None khi thiếu |
fiber_state |
co |
co, khong, thieu |
warnings |
en:nutrition-value-very-high-... |
Từ API |
last_editor |
mephisto-bot |
Provenance |
fetched_at |
2026-11-05 |
Ngày tải |
Danh sách kiểm
- Mã vạch: qua phép kiểm độ dài và số kiểm tra trước khi gọi.
- Tìm thấy: đọc status trong JSON, không dựa vào HTTP 200.
- Thiếu với 0: có cột trạng thái riêng.
- Năng lượng: dùng energy-kcal_100g, không lấy nhầm energy_100g.
- Cảnh báo: giữ data_quality_warnings_tags cho từng dòng.
- Nguồn: có ghi Open Food Facts, giấy phép và ngày tải.
Câu kết luận được phép viết
Từ lab có thể viết rằng API trả HTTP 200 cho cả mã không hợp lệ. Mã ngắn bị đệm và có thể ra sản phẩm rác. Thiếu khóa khác với số 0. Không nên viết rằng dữ liệu của một sản phẩm cụ thể là sai, vì lab chưa xem nhãn thật.
Những gì lab chưa chạy
Lab chưa thử v3, chưa tải bản xuất dữ liệu, chưa đo tỷ lệ thiếu trên toàn bộ sản phẩm Việt Nam. Lab chưa thử cache thật hay ứng dụng quét. Hàm serving_g và check_salt được viết cho bài, chạy trên các dòng mẫu nêu trên.
HTTP 200 chưa phải đã tìm thấy, ô trống chưa phải số 0, và dữ liệu cộng đồng luôn cần cột nguồn đi cùng.
Open Food Facts API sản phẩm: câu hỏi thường gặp
Các câu dưới đây gom những điểm hay bị hỏi.
Có cần tài khoản để đọc không?
Không. Đọc chỉ cần User-Agent riêng. Ghi dữ liệu mới cần xác thực.
Mã không tìm thấy có trả 404 không?
Trong lab, mã toàn số 0 trả 200 với status 0. Hãy đọc status trong JSON.
Vì sao mã 12 số trả code 13 số?
API lưu dạng 13 số có số 0 đầu. Bảng nên chuẩn hóa như vậy trước khi ghép.
Thiếu chất xơ có nghĩa là 0 gam không?
Không. Thiếu khóa nghĩa là chưa có dữ liệu. Giữ None và đếm riêng.
Có nên tin completeness cao không?
Đó là độ đầy đủ, không phải độ chính xác. Vẫn phải đọc cảnh báo chất lượng.
Số trên nhãn có thể khác API không?
Có. Dữ liệu do cộng đồng nhập và công thức có thể đổi. Với dị ứng, người dùng nên đọc nhãn thật.
Quét cả danh mục cửa hàng thì làm sao?
Dùng bản xuất dữ liệu của Open Food Facts rồi xử lý offline, vì giới hạn API rất thấp.
Dùng proxy để vượt 15 lượt mỗi phút được không?
Không nên. Đó là chính sách bảo vệ hạ tầng. Dùng bản xuất dữ liệu cho nhu cầu lớn.
Open Food Facts API sản phẩm và những điều cần nhớ
Open Food Facts API sản phẩm hữu ích khi anh em kiểm mã trước khi gọi, đọc status trong JSON, tách thiếu với 0 và giữ cảnh báo cùng nguồn đóng góp. Dữ liệu cộng đồng cần được đọc như dữ liệu cộng đồng. Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam khuyên anh em ghi nguồn đầy đủ khi dùng Open Food Facts API sản phẩm.