Product Trace API
The Product Trace API provides image-based product verification and authenticity checking with multi-modal feature extraction including barcode detection, OCR, and visual feature matching.
Overview
The Product Trace API enables you to:
- Register Products: Store product images with extracted features for later verification
- Verify Authenticity: Compare query images against registered products
- Detect Barcodes: Scan and decode barcodes from product images
- Extract Text: OCR extraction for text matching
- Multi-Image Fusion: Combine features from multiple angles
- Async Processing: Handle large-scale product registrations
Endpoints
Record Product
Register a product with its features for later verification.
POST /api/product-trace/record
Register a product with single image.
Request Body:
{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://your-storage.com/product.jpg",
"image_urls": ["https://.../angle1.jpg", "https://.../angle2.jpg"],
"product_name": "Premium Widget X2000",
"description": "High-quality industrial widget",
"category": "Industrial"
}Fields:
| Field | Type | Required | Description |
|---|---|---|---|
| sku | string | ✓ | Product SKU identifier |
| identification_code | string | ✓ | Batch or serial number for verification |
| image_url | string | - | Single image URL (legacy support) |
| image_urls | string[] | - | Multiple image URLs for multi-angle features |
| product_name | string | - | Human-readable product name |
| description | string | - | Product description |
| category | string | - | Product category |
Backward Compatibility
Both image_url and image_urls are supported. image_urls accepts either a single string or an array of strings for maximum flexibility.
Response:
{
"id": 42,
"user_id": 1,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://your-storage.com/product.jpg",
"image_hash": "a1b2c3d4e5f6...",
"product_name": "Premium Widget X2000",
"description": "High-quality industrial widget",
"category": "Industrial",
"created_at": "2024-01-15T10:30:00"
}POST /api/product-trace/record/multi
Register product with multiple images for feature fusion. This endpoint combines features from all provided images for more robust verification.
Request Body:
{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_urls": [
"https://.../front.jpg",
"https://.../back.jpg",
"https://.../label.jpg"
],
"product_name": "Premium Widget X2000"
}Feature Fusion Strategy:
- Global features: Vector averaging for noise suppression
- Barcodes: Frequency voting + confidence weighting
- OCR: Union + deduplication across images
- Fine-grained: Best quality image selection
Response:
{
"success": true,
"message": "Product recorded successfully with multi-image fusion",
"total_images": 3,
"product_info": {
"id": 43,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"has_barcode": true,
"has_ocr": true,
"has_fusion": true
},
"fusion_stats": {
"barcodes_found": 2,
"text_areas": 8,
"features_extracted": 3
}
}POST /api/product-trace/record/async
Submit product for asynchronous feature extraction. Use this for large-scale imports or high-throughput scenarios.
Request Body:
{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_urls": [
"https://.../image1.jpg",
"https://.../image2.jpg"
]
}Response:
{
"record_id": 44,
"status": "pending",
"progress": 0,
"message": "Processing task queued",
"estimated_seconds": 15,
"created_at": "2024-01-15T10:30:00"
}Processing Time
Async processing typically takes 5-20 seconds depending on image size and server load. Poll the status endpoint for updates.
Processing Status
GET /api/product-trace/status/
Check the status of an asynchronous processing task.
Response:
{
"record_id": 44,
"status": "processing",
"progress": 65,
"message": "Extracting fine-grained features",
"current_phase": "fine_grained_extraction",
"sub_tasks": {
"barcode": "completed",
"ocr": "completed",
"global_features": "completed",
"fine_grained": "in_progress"
},
"has_barcode": true,
"has_ocr": true,
"has_fine_grained": false,
"has_fusion": false,
"started_at": "2024-01-15T10:30:00",
"completed_at": null
}Status Values:
pending: Task queued, not startedprocessing: Feature extraction in progresscompleted: All features extracted and storedfailed: Processing failed, check error_message
Phase Values:
barcode_extraction: Barcode detection in progressocr_extraction: Text extraction in progressglobal_feature_extraction: Global visual featuresfine_grained_extraction: Detailed feature extractionfeature_fusion: Combining multi-image featuresdatabase_save: Saving results to database
POST /api/product-trace/status/batch
Check status of multiple records in a single request.
Request Body:
{
"record_ids": [44, 45, 46, 47]
}Response:
{
"total_count": 4,
"pending_count": 1,
"processing_count": 1,
"completed_count": 1,
"failed_count": 1,
"results": [
{ /* ProcessingStatus for record 44 */ },
{ /* ProcessingStatus for record 45 */ },
{ /* ProcessingStatus for record 46 */ },
{ /* ProcessingStatus for record 47 */ }
]
}Query Products
GET /api/product-trace/products
Get a paginated list of registered products.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
sku | string | No | Filter by SKU (supports partial match) |
category | string | No | Filter by category (supports partial match) |
page | integer | No | Page number (default: 1) |
page_size | integer | No | Items per page (default: 20, max: 100) |
Response:
{
"total": 150,
"page": 1,
"page_size": 20,
"products": [
{
"id": 42,
"user_id": 1,
"sku": "PROD-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://storage.com/img.jpg",
"image_hash": "abc123...",
"product_name": "Premium Widget X2000",
"description": "High-quality industrial widget",
"category": "Industrial",
"created_at": "2024-01-15T10:30:00",
"already_exists": false,
"message": "Product recorded successfully",
"has_barcode": true,
"has_ocr": true,
"has_fusion": true,
"has_fine_grained": true,
"has_enhanced": true
}
]
}GET /api/product-trace/products/
Get details for a single product by its database ID.
Response: Same as single item in product list.
GET /api/product-trace/products/lookup
Multi-condition product lookup. Find products by ID, SKU, identification code, OCR text hash, or barcode MD5. Supports combined queries with AND semantics.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
product_id | integer | No* | Product database ID |
sku | string | No* | Exact SKU match |
identification_code | string | No* | Exact identification code match |
text_hash | string | No* | OCR text normalized MD5 hash |
barcode_md5 | string | No* | Barcode MD5 value (fuzzy match in barcode_data) |
*At least one query parameter is required. All parameters use AND logic when combined.
Example: Lookup by SKU
GET /api/product-trace/products/lookup?sku=PROD-12345Example: Lookup by text_hash
GET /api/product-trace/products/lookup?text_hash=d41d8cd98f00b204e9800998ecf8427eExample: Lookup by barcode_md5
GET /api/product-trace/products/lookup?barcode_md5=a1b2c3d4e5f6...Response:
{
"total": 1,
"products": [
{
"id": 42,
"user_id": 1,
"sku": "PROD-12345",
"identification_code": "BATCH-2024-001",
"product_name": "Premium Widget X2000",
"description": "High-quality industrial widget",
"category": "Industrial",
"image_url": "https://storage.com/img.jpg",
"image_hash": "abc123...",
"original_image_urls": "[\"https://.../angle1.jpg\"]",
"has_barcode": true,
"has_ocr": true,
"has_fusion": true,
"has_fine_grained": true,
"has_enhanced": true,
"text_hash": "d41d8cd98f00b204e9800998ecf8427e",
"text_fingerprint": "e99a18c428cb38d5...",
"barcode_data": "{\"barcode_md5s\":[\"a1b2c3d4e5f6...\"]}",
"ocr_data": "{\"text\":\"...\",\"success\":true}",
"ocr_text": "Extracted OCR text content here",
"fine_grained_data": "{...}",
"processing_status": "completed",
"processing_progress": 100,
"processing_message": null,
"verification_count": 5,
"last_verified_at": "2024-01-20T15:00:00",
"created_at": "2024-01-15T10:30:00",
"updated_at": "2024-01-15T10:30:00"
}
]
}Error Response (404):
{
"detail": "未找到匹配的产品"
}DELETE /api/product-trace/products/
Delete a product record and its associated verification history (cascade delete).
Response:
{
"message": "删除成功",
"product_id": 42
}Verification History
GET /api/product-trace/verification-history
Get paginated verification history records.
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
product_id | integer | No | Filter by product ID |
page | integer | No | Page number (default: 1) |
page_size | integer | No | Items per page (default: 20, max: 100) |
Response:
{
"total": 50,
"page": 1,
"page_size": 20,
"history": [
{
"id": 1,
"product_feature_id": 42,
"similarity_score": 0.95,
"threshold": 0.85,
"is_verified": true,
"created_at": "2024-01-20T15:00:00"
}
]
}Verify Product
POST /api/product-trace/verify
Verify product authenticity by comparing against registered features.
Request Body:
{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://.../query_image.jpg",
"image_urls": ["https://.../query1.jpg", "https://.../query2.jpg"],
"threshold": 0.85
}Verification Pipeline (Hash-Only Mode - Default):
Query Image
↓
[Stage 1] Barcode MD5 Match (<10ms, O(log n))
↓ (if match found)
Return: Verified (similarity=1.0)
↓ (if no match)
[Stage 2] Text Hash Match (<10ms, O(log n))
↓ (if match found)
Return: Verified (similarity=1.0)
↓ (if no match)
Return: Not Verified (NO_HASH_HIT)Verification Pipeline (Legacy Mode - Optional):
Set SCAN_HASH_ONLY_MODE=false to enable full cascade:
Query Image
↓
[Stage 1] Barcode Match (<10ms)
↓ (if match found)
Return: Verified
↓ (if no match)
[Stage 2] Text Hash Match (<10ms)
↓ (if match found)
Return: Verified
↓ (if no match)
[Stage 3] Global Feature Match (<100ms)
↓ (if score > threshold)
Return: Verified
↓ (if score < reject)
Return: Not Verified
↓ (inconclusive)
[Stage 4] Fine-Grained + OCR Match (2-6s)
↓
Final Verification ResultHash-Only Mode Response (Match Found):
{
"matched": true,
"match_source": "barcode",
"product_info": {
"id": 42,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"product_name": "Premium Widget X2000"
},
"similarity": 1.0,
"match_stage": "hash_only_barcode",
"diagnostics": {
"extracted_text_hash": "a1b2c3d4e5f6...",
"extracted_barcodes": ["5901234123457"],
"ocr_text_preview": "PRODUCT-12345 BATCH-2024-001...",
"conflict": false
}
}Hash-Only Mode Response (No Match):
{
"matched": false,
"match_source": null,
"reason": "NO_HASH_HIT",
"similarity": 0.0,
"match_stage": "hash_only_no_match",
"diagnostics": {
"extracted_text_hash": "a1b2c3d4e5f6...",
"extracted_barcodes": [],
"ocr_text_preview": "PRODUCT-12345...",
"conflict": false
}
}Legacy Mode Response:
{
"is_verified": true,
"similarity_score": 0.92,
"threshold": 0.85,
"product_info": {
"id": 42,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"product_name": "Premium Widget X2000"
},
"message": "Product verified via fine-grained feature matching"
}Verification Methods:
| Method | Speed | Use Case | Mode |
|---|---|---|---|
| barcode_match | <10ms | Fast verification when barcode is visible | Hash-Only |
| text_hash_match | <10ms | OCR text fingerprint matching | Hash-Only |
| global_feature_match | <100ms | Quick visual matching | Legacy |
| fine_grained_match | 2-6s | Detailed, high-accuracy verification | Legacy |
| ocr_text_match | 1-3s | Text-based verification | Legacy |
Hash-Only Mode
By default, the verification pipeline uses Hash-Only Mode which provides deterministic, millisecond-level matching based on barcode and OCR text hashes. This mode:
- Faster: <10ms response time
- Deterministic: No probability-based similarity scores
- Reliable: Consistent results across platforms
To enable legacy visual similarity matching, set environment variable SCAN_HASH_ONLY_MODE=false.
Tuning Verification
- Lower
thresholdfor more lenient matching (0.7-0.8) - Legacy mode only - Higher
thresholdfor stricter matching (0.85-0.95) - Legacy mode only - Default threshold is 0.8, which provides a good balance
POST /api/product-trace/verify/multi
Verify using multiple query images from different angles.
Request Body:
{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_urls": [
"https://.../query_front.jpg",
"https://.../query_back.jpg"
],
"threshold": 0.8
}Response:
{
"is_verified": true,
"max_similarity": 0.94,
"avg_similarity": 0.89,
"threshold": 0.8,
"total_images": 2,
"verified_images": 2,
"product_info": { /* product info */ },
"message": "Product verified from multiple angles",
"image_results": [
{
"image_index": 0,
"similarity": 0.94,
"method": "fine_grained_match",
"is_verified": true
},
{
"image_index": 1,
"similarity": 0.83,
"method": "global_feature_match",
"is_verified": true
}
]
}Image Search (Scan)
POST /api/product-trace/scan
Scan an image and find matching products without requiring specific SKU/identification code. In Hash-Only Mode (default), this endpoint performs barcode and text hash based matching.
Request Body:
{
"image_url": "https://.../unknown_product.jpg",
"image_urls": ["https://.../unknown1.jpg", "https://.../unknown2.jpg"],
"top_k": 5,
"request_id": "req-20260101-001",
"notification_url": "https://your-server.com/callback/scan",
"notification_token": "your-auth-token"
}| Parameter | Type | Required | Description |
|---|---|---|---|
image_url | string | No* | Single image URL |
image_urls | string[] | No* | Multiple image URLs for multi-view scanning |
top_k | integer | No | Max results (default: 5) |
request_id | string | No | Unique request identifier for dedup & callback passthrough. Same user + same request_id limited to 1 request per 30s. |
notification_url | string | No | Callback URL for async scan result notification |
notification_token | string | No | Token set in X-Verification-Token header of callback request |
*At least one of
image_urlorimage_urlsmust be provided.
Hash-Only Mode Response (Match Found):
{
"total_count": 1,
"top_k": 5,
"mode": "hash_only",
"results": [
{
"product_info": {
"id": 42,
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"product_name": "Premium Widget X2000"
},
"similarity": 1.0,
"match_source": "barcode",
"match_stage": "hash_only_barcode"
}
]
}Hash-Only Mode Response (No Match):
{
"total_count": 0,
"top_k": 5,
"mode": "hash_only",
"results": [],
"diagnostics": {
"extracted_text_hash": "a1b2c3d4e5f6...",
"extracted_barcodes": [],
"ocr_text_preview": "PRODUCT-12345..."
}
}Legacy Mode Response (SCAN_HASH_ONLY_MODE=false):
{
"total_count": 12,
"threshold": 0.7,
"top_k": 5,
"mode": "legacy",
"results": [
{
"product_info": { /* product data */ },
"similarity_score": 0.94,
"global_similarity": 0.94,
"fine_similarity": 0.91,
"match_type": "strong_match"
},
{
"product_info": { /* product data */ },
"similarity_score": 0.82,
"global_similarity": 0.82,
"fine_similarity": null,
"match_type": "potential_match"
}
]
}Scan vs Verify
- Verify (
/verify): Requiressku+identification_codeto match against a specific product - Scan (
/scan): No SKU required, searches all products for hash matches - Both endpoints use Hash-Only Mode by default
Barcode Detection
POST /api/product-trace/barcode/detect
Detect and decode barcodes from images without storing product information.
Request Body:
{
"image_url": "https://.../product_with_barcode.jpg",
"image_urls": ["https://.../barcode1.jpg", "https://.../barcode2.jpg"],
"return_image_data": true
}Response:
{
"success": true,
"total_images": 2,
"total_barcodes": 3,
"results": [
{
"image_index": 0,
"image_url": "https://.../barcode1.jpg",
"image_size": { "width": 1920, "height": 1080 },
"barcodes_count": 2,
"barcodes": [
{
"type": "EAN13",
"data": "5901234123457",
"md5": "d41d8cd98f00b204e9800998ecf8427e",
"confidence": 0.98,
"rect": {
"left": 450,
"top": 300,
"width": 200,
"height": 80
}
},
{
"type": "QRCODE",
"data": "https://example.com/product/12345",
"md5": "a1b2c3d4e5f6...",
"confidence": 0.95
}
]
}
],
"unique_barcodes": [
{ /* unique barcode 1 */ },
{ /* unique barcode 2 */ }
],
"message": "Successfully detected 3 barcodes from 2 images"
}Supported Barcode Types:
- EAN-8, EAN-13
- UPC-A, UPC-E
- Code 39, Code 93, Code 128
- ITF, Codabar
- QR Code, Data Matrix
- PDF417, Aztec
OCR Barcode/Serial Extraction
When zxing-cpp fails to detect physical barcodes in an image, the system also extracts numeric and alphanumeric patterns from OCR text and stores them in ocr_data.ocr_extracted_barcodes:
- EAN13: 13-digit numeric strings (e.g.,
5901234123457) - UPC-A: 12-digit numeric strings
- EAN8: 8-digit numeric strings
- ITF14: 14-digit numeric strings
- SERIAL: 8-32 character alphanumeric strings (e.g.,
SN:ABC123456)
These extracted codes are available for hash-based matching during Scan/Verify operations through the same ocr_data JSON column. Each extracted code is stored with its MD5 hash and type label.
Fine-Grained Verification
POST /api/product-trace/verify/fine
Detailed verification using fine-grained features and OCR text matching.
Request Body:
{
"sku": "PRODUCT-12345",
"identification_code": "BATCH-2024-001",
"image_url": "https://.../query_image.jpg",
"threshold": 0.7
}Response:
{
"success": true,
"verified": true,
"message": "Product verified with fine-grained features",
"base_similarity": 0.82,
"fine_grained_similarity": 0.89,
"threshold": 0.7,
"text_hash_match": true,
"text_areas_count": 6,
"sku_patterns_found": ["PROD", "12345"],
"product_info": { /* product data */ }
}ANN Search
POST /api/product-trace/ann/search
High-performance Approximate Nearest Neighbor vector search. In Hash-Only Mode, this endpoint returns hash-based matches only.
Request Body:
{
"image_url": "https://.../query.jpg",
"top_k": 10,
"min_similarity": 0.5,
"vector_field": "fusion_vector"
}Hash-Only Mode Response:
{
"total": 1,
"top_k": 10,
"mode": "hash_only",
"results": [
{
"product_info": { /* product data */ },
"similarity": 1.0,
"match_source": "barcode"
}
]
}Legacy Mode Response:
{
"total": 150,
"top_k": 10,
"min_similarity": 0.5,
"mode": "legacy",
"vector_field": "fusion_vector",
"results": [
{
"product_info": { /* product data */ },
"similarity": 0.94
}
]
}POST /api/product-trace/ann/verify
Fast two-stage ANN verification: coarse search + fine verify. In Hash-Only Mode, uses barcode/text_hash matching instead of vector similarity.
Request Body:
{
"image_url": "https://.../query.jpg",
"threshold": 0.7,
"coarse_top_k": 50
}Hash-Only Mode Response:
{
"success": true,
"verified": true,
"mode": "hash_only",
"match_source": "text_hash",
"message": "Product verified via hash match",
"product_info": { /* product data */ }
}Legacy Mode Response:
{
"success": true,
"verified": true,
"mode": "legacy",
"message": "Product verified via ANN search",
"candidates_count": 50,
"best_similarity": 0.88,
"threshold": 0.7,
"best_match": { /* product data */ }
}Async Notification Callback
Scan/Verify Notification
When scan_notification_url or verification_notification_url is configured for a user, the system will asynchronously POST results to the specified URL after Scan or Verify operations complete.
Scan Notification Payload:
{
"verification_id": 0,
"is_verified": true,
"similarity_score": 1.0,
"threshold": 0.8,
"message": "扫描完成,匹配到 1 个商品",
"timestamp": "2026-06-02T17:00:00",
"request_id": "req-20260101-001",
"total_matches": 1
}Verify Notification Payload:
{
"verification_id": 42,
"is_verified": true,
"similarity_score": 1.0,
"threshold": 0.8,
"message": "Product verified via hash match",
"timestamp": "2026-06-02T17:00:00",
"request_id": "req-20260101-001"
}| Field | Type | Description |
|---|---|---|
verification_id | integer | Verification record ID (0 for scan) |
is_verified | boolean | Whether the product was verified |
similarity_score | float | Match similarity score (1.0 for hash matches) |
threshold | float | Match threshold used |
message | string | Human-readable result message |
timestamp | string | ISO 8601 callback timestamp |
request_id | string | The request_id from the original request, returned unchanged for correlation |
total_matches | integer | Number of matched results (scan only) |
Callback Headers:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Verification-Token | The notification_token from the original request |
Note: The
resultdict passed to the notification service is fully transparent — any additional fields in the result (e.g.,product_info,match_source) are also included in the callback payload.
Configuration
Hash-Only Mode
The Product Trace API supports two verification modes:
| Mode | Environment Variable | Behavior |
|---|---|---|
| Hash-Only (Default) | SCAN_HASH_ONLY_MODE=true | Barcode → Text Hash → NO_HASH_HIT |
| Legacy | SCAN_HASH_ONLY_MODE=false | Full 5-stage cascade with visual similarity |
Key Differences:
| Aspect | Hash-Only Mode | Legacy Mode |
|---|---|---|
| Response Time | <10ms | 100ms - 8s |
| Matching Basis | Deterministic hash | Probability-based similarity |
| DINOv2 Usage | None | Full feature extraction |
| OCR Usage | Text hash only | Full text matching |
| Consistency | 100% deterministic | Platform-dependent |
Use Hash-Only Mode when:
- Barcode or OCR text is available on products
- Millisecond-level response time is required
- Deterministic results are preferred over probabilistic
Use Legacy Mode when:
- Products have no barcode or OCR text
- Visual similarity matching is specifically needed
- Fine-grained feature comparison is required
Endpoint Coverage
Hash-Only Mode applies to these endpoints:
/verify/verify/upload/verify/multi/scan/scan/upload/ann/verify/ann/verify/upload/ann/search
Endpoints NOT affected by Hash-Only Mode:
/record*(feature extraction)/fine-grained/*(fine-grained features)/barcode/*(barcode detection)/migration/*(feature migration)
Migration (Feature Upgrade)
POST /api/product-trace/migrate/start
Start feature migration to extract new format features for existing records.
Request Body:
{
"target_version": "v2",
"batch