Vision Identify
POST /v3/vision/identify. Identify a luxury item from one or more photos.
POST /v3/vision/identify
Legacy path: POST /api/v3/vision/identify (same behavior).
Send one or more photos of an item and get matching catalog rows in the same shape as Search catalog, each with a confidence. Ask for models (the catalog model, e.g. Neverfull), styles (the model's material or pattern, e.g. Damier Ebene) or products (model + style, with variants and pricing) with taxonomy_node, and narrow with the same filters as catalog search.
Two-step flow
The recommended flow for a listing app, one request each:
- Model step — the front photo,
taxonomy_node: "model": a ranked shortlist of catalog models. Add"facets"toincludesto get the brands present in the shortlist with their counts, so you can ask the user "is it one of these brands?" and re-run with a brand filter when the shortlist mixes brands. Add"next_node"to get each model's most likely styles in the same call, for a card that shows the style the photo matched. - Product step — all photos,
taxonomy_node: "product",filters: [{ "field": "model", "include": ["model_…"] }]for the model the user picked: that model's products ranked. Usetaxonomy_node: "style"with the same filter to get the same ranking as style rows (id, name, images, pricing summary) without the variants.
When the user confirms a product, send its id and the prediction_id to Vision feedback; send the prediction_id alone if they chose none.
Try it in the API Explorer: it opens on the model step: replace the example URL with a photo of your item, and after a call it offers to send feedback for that prediction.
Request
Model step
curl -X POST https://api.pricehero.ai/v3/vision/identify \
-H "Authorization: Bearer ph_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"images": [{ "base64": "data:image/jpeg;base64,/9j/4AAQ..." }],
"taxonomy_node": "model",
"filters": [{ "field": "category", "include": ["handbags"] }],
"page_size": 10,
"includes": ["taxonomy", "facets"]
}'Product step
All photos of the item, restricted to the model the user picked:
curl -X POST https://api.pricehero.ai/v3/vision/identify \
-H "Authorization: Bearer ph_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"images": [
{ "base64": "data:image/jpeg;base64,/9j/4AAQ..." },
{ "base64": "data:image/jpeg;base64,/9j/4AAQ..." },
{ "url": "https://example.com/my-bag-side.jpg" }
],
"taxonomy_node": "product",
"filters": [
{ "field": "category", "include": ["handbags"] },
{ "field": "model", "include": ["model_FR8RWgt7www6LCLVic8hUg"] }
],
"includes": ["taxonomy", "pricing"]
}'One photo, legacy form
The original one-image form still works and returns products:
curl -X POST https://api.pricehero.ai/v3/vision/identify \
-H "Authorization: Bearer ph_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/my-bag.jpg", "includes": ["taxonomy", "pricing"] }'Body parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
images | array | ; | Up to 5 photos, each { "url": "https://…" } or { "base64": "data:image/…;base64,…" } (raw base64 also accepted). The first image is the front view. Base64 is limited to 1 MB per image (413 above). |
url, base64 | string | ; | One-image alias of images (kept for existing integrations). Ignored when images is present. |
taxonomy_node | string | product | model for the model shortlist, style for styles (model + material), product for products. model and style belong to the handbags hierarchy, so those requests are scoped to handbags on their own; naming a category whose hierarchy lacks the node is a 400 that lists that category's nodes. |
category_id | string | ; | Alias of a category filter; kept for existing integrations. |
filters | array | ; | Same as catalog search: { "field": "category" | "brand" | "model" | …, "include": ["id", …], "exclude": [...] }. OR within one object, AND across objects. A category entry with one id (e.g. handbags) is recommended: it selects the recognition for that category and scopes the results; several categories act as a plain facet on the photo-and-text path. A model entry on the product step selects that model's styles (at most 3 ids). |
page | integer | 0 | Page index (0-based) |
page_size | integer | 10 | Page size, max 50 on every node (larger values are capped). "Show more" on the model step is the next page; the model step pages up to rank 100. |
includes | string[] | ["taxonomy"] | taxonomy, pricing, facets (free: counts per attribute value over the returned rows) and next_node (free: each row's top rows at the next level of its category hierarchy). |
allow_training | boolean | false | Lets Price Hero keep this request's photos to improve recognition. Only for organizations whose API Commercial Agreement includes training; otherwise a 403, with test keys too. A test key can send it to build the same request as live: it is validated the same way but photos are never kept, and the response carries a warning saying so. Applies to photo recognition only (a handbags category filter); otherwise the request succeeds with a warning that it was not applied. Photos are deleted within 7 days unless their feedback is accepted. Photos whose feedback is held for review are kept up to 60 days until the review, and deleted if it is not confirmed by then. |
Response
Same envelope and item shape as catalog search (resolved_taxonomy is omitted when the rows span several categories), plus:
confidenceon every row, in[0, 1]: how strongly the photos match that row. Rows are ordered by it. Model rows below a minimum similarity are not returned, so a page can be shorter thanpage_sizeandtotal_resultsstops there. Compare rows with each other rather than against a fixed threshold: a new photo of a catalog item typically scores 0.3–0.7 on its right row, higher only for near-identical photos.total_resultsandtotal_pagescount exactly on every step; on the model step they count the models above the similarity floor within the engine's reach of 100 ranks.prediction_idon every successful response: pass it to Vision feedback once the user has chosen.warningswhen the request used a deprecated or alternate spelling (same information as the response headers), on a test key when the sandbox reduced the answer (see above), and whenallow_trainingwas sent but not applied (a test key, or a request outside photo recognition).facetswhen requested:{ "<field>": [{ "id", "name", "count" }] }, most frequent first. The rows are ranked by confidence, so the first brand indatais the best-scoring one.next_nodewhen requested, on every row:{ "node", "level", "data": [...] }, the row's top rows at the next level of its category hierarchy,nodeandlevelspelled as inresolved_taxonomy, three rows at most, ranked by confidence, in the item shape withoutattributes(the parent row andnodealready say what a child is). Style children carry no pricing; product children carry their variants' pricing whenpricingis included, and the request is then billed as a product-row request. A model row carries its most likely styles, a style row its products; a product row has no next level and carriesnull. It is computed only when requested, so leave it out when speed matters more than the preview.
Model and style rows (taxonomy_node: "model" / "style") have no variants; they carry attributes (brand, model, and for a style its model), the entity's images, and, with pricing, a summary pricing.market_estimates (best estimate). Product rows carry variants exactly as in catalog search.
{
"success": true,
"results": 2,
"total_results": 10,
"page": 0,
"total_pages": 1,
"credits_used": 5,
"includes": ["taxonomy", "facets"],
"prediction_id": "0f2d5f4e-8c1a-4a2b-9e5d-1c3b7a9d2e40",
"resolved_taxonomy": { "node": "model", "level": 3 },
"facets": {
"brand": [
{
"id": "brand_9YJxjSHS5UK1jFmA2dThev",
"name": "Louis Vuitton",
"count": 2
}
]
},
"data": [
{
"id": "model_FR8RWgt7www6LCLVic8hUg",
"name": "Sevigne Clutch",
"confidence": 0.98,
"attributes": [
{ "field": "category", "id": "handbags", "name": "Handbags" },
{ "field": "brand", "id": "brand_9YJxjSHS5UK1jFmA2dThev", "name": "Louis Vuitton" }
],
"popularity": 40,
"images": ["https://cdn.pricehero.ai/img/model_FR8RWgt7www6LCLVic8hUg.jpg"],
"thumbnails": ["https://cdn.pricehero.ai/thumb/model_FR8RWgt7www6LCLVic8hUg.jpg"]
},
{
"id": "model_VdAGNxAApLCRukwzSUBZSb",
"name": "Sevigne Handbag",
"confidence": 0.57,
"attributes": [
{ "field": "category", "id": "handbags", "name": "Handbags" },
{ "field": "brand", "id": "brand_9YJxjSHS5UK1jFmA2dThev", "name": "Louis Vuitton" }
],
"popularity": 35,
"images": ["https://cdn.pricehero.ai/img/model_VdAGNxAApLCRukwzSUBZSb.jpg"],
"thumbnails": ["https://cdn.pricehero.ai/thumb/model_VdAGNxAApLCRukwzSUBZSb.jpg"]
}
]
}A product-step response looks like a catalog search for products, with confidence on each product and prediction_id at the top level.
Test keys see only the sandbox subset of the catalog, as on catalog search: the shortlist is drawn from the sandbox-enabled models and products, so use photos of items in the sandbox catalog when testing, and only sandbox ids in filters (a model outside the sandbox is refused with filters: <id> is not in the sandbox catalog; an id that is no model at all is filters: unknown model id, as for a live key). When the sandbox reduces the answer, the response says so in warnings: the closest match in the full catalog is outside the sandbox (with the two scores and nothing else about that item), or some of the matching products were left out because they are not in the sandbox (counts only). An empty data means nothing matched; it is not charged. On the photo-and-text path (no category scope), two outcomes answer 200 with success: false and an error instead, also uncharged: the photo is not a handbag, small leather good or watch, or no catalog product matched the description. Treat recognition as an assist and keep a text search fallback.
Without a category filter the request behaves as it always has: one photo is analyzed and products are returned, the same shape with confidence and prediction_id added. The category scope is what turns on photo recognition for that category, so send it whenever you know it.
Image guidelines
- Resize on the client to about 1024 px on the longest side, JPEG quality ~85 (around 200 KB). Recognition works at a much smaller size, so larger uploads only cost transfer time; base64 above 1 MB is rejected with
413. - First image = the front of the item; add a back and a side view for the product step (1–3 photos is enough).
- Formats: JPEG, PNG, WebP. URLs must be publicly reachable image files (fetched with a 12 MB cap; redirects are followed to public hosts only).
Errors
| Status | When |
|---|---|
400 | No image, more than 5 images, an image entry with neither url nor base64, an unknown taxonomy_node, a malformed filters, an unknown brand/model id, or taxonomy_node: "model" with a category whose hierarchy has no model node |
403 | allow_training for an organization whose agreement does not include training (test and live keys) |
402 | Not enough credits |
413 | A base64 image above 1 MB |
503 | Recognition temporarily unavailable; retry |
Credits
See Credits. A vision request is billed once per call when it returns at least one row: 5 credits with taxonomy, 10 with pricing on product rows, including product children returned through next_node. pricing on model rows is a free summary, and facets and next_node are always free.