POST /api/v1/scan
Vision recognition — identify a product from a photo. Account-mode keys get results matched against the caller’s own AskBiz inventory; generic keys get raw identification only.
What it does
Sends a photo to the same Groq Llama-4-Scout vision pipeline that powers the AskBiz app’s in-app product scanner. If your key is in account mode, the result is matched against your own AskBiz inventory — you get back a real inventory_id, stock level, and price. In generic mode (or when nothing matches), you still get a raw identification with no catalog lookup.
Request
curl -X POST https://askbiz.co/api/v1/scan \
-H "x-api-key: abz_live_your_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5a9e2c1e-6b3f-4a2d-9c11-3f7e8a0b1c2d" \
-d '{
"image": "<base64-encoded JPEG>"
}'Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
image | string | Yes | Base64-encoded JPEG. No data URI prefix — raw base64 only. |
merchant_id | string | No | Scan on behalf of a connected merchant instead of your own account — requires an active connection that grants the read_inventory scope. |
Response
When the photo matches an item in the resolved inventory (your own, or a connected merchant’s):
{
"found": true,
"inventory_id": "b1e2c3d4-...",
"name": "Coca-Cola 500ml",
"price": 80,
"cost_price": 55,
"stock_qty": 24,
"unit": "bottle"
}When nothing matches, or the key has no catalog to match against:
{
"found": false,
"inventory_id": null,
"name": "Fanta Orange 300ml",
"price": null,
"stock_qty": null,
"unit": null
}If your balance drops below your account’s low-balance threshold as a result of this call, the response also includes low_balance_warning: true and the new balance_cents — in-band, at the moment it happens, not just when you hit a 402.
On a test key (abz_test_…), the vision model is never called and your wallet is never touched — every call returns this exact same canned match, tagged test_mode: true:
{
"found": true,
"inventory_id": null,
"name": "Coca-Cola 500ml",
"price": 80,
"cost_price": 60,
"stock_qty": 24,
"unit": "bottle",
"test_mode": true
}See Build safely with a sandbox key for the full test/live picture across every endpoint.
Errors
| Status | Meaning |
|---|---|
| 400 | Invalid JSON body, or image missing. |
| 401 | Missing or invalid x-api-key. |
| 402 | Insufficient credits — response includes required_cents. |
| 403 | merchant_id given with no active connection, or the connection doesn’t grant read_inventory. |
| 422 | Vision model couldn’t identify a product in the image. |
| 429 | Rate limit or monthly quota exceeded — check the X-RateLimit-Remaining response header. |
| 502 | Upstream vision recognition failed — safe to retry (with the same Idempotency-Key to avoid a double charge if the first call actually succeeded server-side after you gave up on it). |
None of the 4xx/5xx responses above are billed — a failed or rejected call never debits your wallet. See Errors and retries for the full idempotency contract.
Scan endpoint FAQ
Do I get charged if the vision model can’t identify the product?+
No. Debiting only happens after recognition succeeds — a 422 (no product identified) or 502 (upstream failure) never costs you anything.
What image formats are supported?+
JPEG only, base64-encoded with no data URI prefix (send the raw base64 string, not "data:image/jpeg;base64,...").
Can I scan on behalf of a merchant who isn’t my own account?+
Yes, with an active connection to that merchant that grants the read_inventory scope — pass their user ID as merchant_id. See the guide on connecting to a merchant.
Can I test this endpoint without spending real credits?+
Yes — use a test key (abz_test_…). It skips the vision model and your real inventory entirely, and always returns the same safe example match with test_mode: true. See Build safely with a sandbox key.