Send a photo and a category to POST /search. Flume returns products that look like the photo, in two lists: strong matches, then other similar options.
Steps 3 to 5: search, product, quote. Each call carries both headers. The search returns a search_session_uuid that the later calls carry. A quote that is still pricing is polled.
Read this diagram as text
- Your backend calls GET /filters and builds your filter menus from the answer.
- Your screen sends the photo and the chosen filters to your backend.
- Your backend calls POST /search with the photo.
- Flume returns up to 24 results a page and a search_session_uuid.
- Your backend calls GET /products/{sku_uuid} for the product the user opens.
- Your backend calls POST /request-instant-quote with the product, a quantity, a ZIP code and the search_session_uuid. It carries an Idempotency-Key.
- Flume answers within 60 seconds: READY with prices, or FAILED with a reason.
- Only when the reason is TEMPORARILY_UNAVAILABLE: your backend polls GET /quotes/{quote_uuid} until the quote is READY.
- Your backend returns the prices to your screen.
Before you begin
Section titled “Before you begin”- A partner key and a user access token.
- A photo: JPEG, PNG or WebP, up to 6,000,000 bytes.
1. Read the filter options
Section titled “1. Read the filter options”Build your filter menus from this call, not from a hardcoded list. New values then appear without a release.
curl -X GET "https://sandbox-api.tryflume.ai/partner/v1/filters" \ -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY"import os
import requests
headers = { "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"],}
response = requests.get( "https://sandbox-api.tryflume.ai/partner/v1/filters", headers=headers, timeout=30,)response.raise_for_status()print(response.json())const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/filters", { method: "GET", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, },});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(await response.json());{ "tile": { "material": [ { "value": "PORCELAIN", "label": "Porcelain" }, { "value": "CERAMIC", "label": "Ceramic" } ], "finish": [ { "value": "MATTE", "label": "Matte", "also_matches": [ "NATURAL", "HONED", "ANTI_SLIP" ] }, { "value": "GLOSSY_POLISHED", "label": "Glossy / Polished", "also_matches": [ "LAPPATO_SEMI_POLISHED" ] } ], "application_area": [ { "value": "INTERIOR_DRY_FLOOR", "label": "Interior Dry Floor", "group": "FLOORS" }, { "value": "SHOWER_WALL", "label": "Shower Wall", "group": "WALLS" } ] }, "flooring": { "wear_layer_mil": [ { "value": 20, "label": "20 mil" }, { "value": 28, "label": "28 mil and up" } ], "installation_method": [ { "value": "CLICK_LOCK", "label": "Click Lock" } ], "finish": [ { "value": "MATTE", "label": "Matte" } ], "rigid_core": [ { "value": "SPC", "label": "SPC" } ] }}valueis what you send.labelis what you show.also_matcheslists the similar tile finishes a finish also finds.
2. Search
Section titled “2. Search”Send multipart/form-data with two parts: image and payload.
curl -X POST "https://sandbox-api.tryflume.ai/partner/v1/search" \ -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \ -H "X-Flume-User-Token: $FLUME_USER_TOKEN" \ -F "image=@photo.jpg" \ -F 'payload={"category":"TILE","tile_filters":{"material":["PORCELAIN"],"finish":["MATTE"],"size":{"width":24,"length":48,"unit":"in"}},"project":{"external_ref":"proj_7781","name":"Harbor Lofts - L2","destination_zip":"22102"}}'import osimport json
import requests
headers = { "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], "X-Flume-User-Token": os.environ["FLUME_USER_TOKEN"],}payload = { "category": "TILE", "tile_filters": { "material": ["PORCELAIN"], "finish": ["MATTE"], "size": { "width": 24, "length": 48, "unit": "in", }, }, "project": { "external_ref": "proj_7781", "name": "Harbor Lofts - L2", "destination_zip": "22102", },}
with open("photo.jpg", "rb") as image: response = requests.post( "https://sandbox-api.tryflume.ai/partner/v1/search", headers=headers, files={"image": image}, data={"payload": json.dumps(payload)}, timeout=30, )response.raise_for_status()print(response.json())import { readFile } from "node:fs/promises";
const form = new FormData();form.append("image", new Blob([await readFile("photo.jpg")]), "photo.jpg");form.append("payload", JSON.stringify({ "category": "TILE", "tile_filters": { "material": [ "PORCELAIN" ], "finish": [ "MATTE" ], "size": { "width": 24, "length": 48, "unit": "in" } }, "project": { "external_ref": "proj_7781", "name": "Harbor Lofts - L2", "destination_zip": "22102" }}));
const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/search", { method: "POST", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, "X-Flume-User-Token": process.env.FLUME_USER_TOKEN, }, body: form,});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(await response.json());{ "search_session_uuid": "5f0c1d2e-8a7b-4c3d-9e1f-2a3b4c5d6e7f", "total": 31, "strong_match_count": 9, "other_similar_count": 22, "page": 1, "page_size": 24, "has_more": true, "strong_matches": [ { "sku_uuid": "7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30", "name": "Porcelain 24×48″ Matte", "image_url": "https://sandbox-api.tryflume.ai/partner/v1/images/example-image-main", "more_image_urls": [ "https://sandbox-api.tryflume.ai/partner/v1/images/example-image-2", "https://sandbox-api.tryflume.ai/partner/v1/images/example-image-3" ], "size": "24×48 in", "thickness": "9 mm", "material": "Porcelain", "as_low_as_per_sqft": "6.40", "price_on_request": false, "lead_times": [ { "source": "US_WAREHOUSE", "lead_time_min_days": 14, "lead_time_max_days": 30 }, { "source": "OVERSEAS_DIRECT", "lead_time_min_days": 90, "lead_time_max_days": 120 } ], "suitable_application_areas": [ { "code": "INTERIOR_DRY_FLOOR", "label": "Interior Dry Floor" }, { "code": "INTERIOR_WALL_BACKSPLASH", "label": "Interior Wall / Backsplash" } ], "saving_vs_spec": null } ], "other_similar_options": [ { "sku_uuid": "0b6f9d34-2e71-4c5a-8f10-3d9a7e2b6c41", "name": "Porcelain 24×48″ Natural", "image_url": "https://sandbox-api.tryflume.ai/partner/v1/images/example-image-main", "more_image_urls": [ "https://sandbox-api.tryflume.ai/partner/v1/images/example-image-2", "https://sandbox-api.tryflume.ai/partner/v1/images/example-image-3" ], "size": "24×48 in", "thickness": "9 mm", "material": "Porcelain", "as_low_as_per_sqft": "5.90", "price_on_request": false, "lead_times": [ { "source": "US_WAREHOUSE", "lead_time_min_days": 14, "lead_time_max_days": 30 }, { "source": "OVERSEAS_DIRECT", "lead_time_min_days": 90, "lead_time_max_days": 120 } ], "suitable_application_areas": [ { "code": "INTERIOR_DRY_FLOOR", "label": "Interior Dry Floor" }, { "code": "INTERIOR_WALL_BACKSPLASH", "label": "Interior Wall / Backsplash" } ], "saving_vs_spec": null } ], "project": { "external_ref": "proj_7781", "status": "FILED", "reason": null, "missing": [], "message": null }}payload field | Required | Notes |
|---|---|---|
category | Yes | TILE or FLOORING |
tile_filters | No | Only with TILE |
flooring_filters | No | Only with FLOORING |
project | No | Files the search under a project in the user's Flume account |
page, page_size | No | Defaults 1 and 24 |
crop_box | No | Search on part of the photo: x, y, w, h as fractions from 0 to 1 |
spec_unit_price | No | Adds saving_vs_spec to each result. USD per SF only |
Filter rules:
- A list filter matches any of its values.
- Different filters must all hold.
- A filter you leave out is no constraint.
- The photo ranks the products that pass the filters.
3. Read the results
Section titled “3. Read the results”strong_matches: products that meet every filter and look closest to the photo. Best first.other_similar_options: close to the photo, with some filters relaxed. They start on the page where the strong matches end.totalis at most 50.as_low_as_per_sqftis the lowest delivered price per square foot, before tax. An instant quote can be higher.price_on_request: truemeans there is no instant quote. Hand off to sales.- Image links expire after about 24 hours, and earlier in the sandbox. Do not store them.
Keep search_session_uuid. Quotes, saves and deep links for these products need it.
4. Get the next page
Section titled “4. Get the next page”Send the same photo and payload with page: 2.
5. Read a product
Section titled “5. Read a product”curl -X GET "https://sandbox-api.tryflume.ai/partner/v1/products/7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30" \ -H "X-Flume-Partner-Key: $FLUME_PARTNER_KEY" \ -H "X-Flume-User-Token: $FLUME_USER_TOKEN"import os
import requests
headers = { "X-Flume-Partner-Key": os.environ["FLUME_PARTNER_KEY"], "X-Flume-User-Token": os.environ["FLUME_USER_TOKEN"],}
response = requests.get( "https://sandbox-api.tryflume.ai/partner/v1/products/7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30", headers=headers, timeout=30,)response.raise_for_status()print(response.json())const response = await fetch("https://sandbox-api.tryflume.ai/partner/v1/products/7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30", { method: "GET", headers: { "X-Flume-Partner-Key": process.env.FLUME_PARTNER_KEY, "X-Flume-User-Token": process.env.FLUME_USER_TOKEN, },});if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);console.log(await response.json());{ "sku_uuid": "7f3c2a91-4b0e-4c8d-9a21-1f6b2d5e7c30", "category": "TILE", "name": "Porcelain 24×48″ Matte", "as_low_as_per_sqft": "6.40", "price_on_request": false, "image_urls": [ "https://sandbox-api.tryflume.ai/partner/v1/images/example-image-main", "https://sandbox-api.tryflume.ai/partner/v1/images/example-image-2" ], "at_a_glance": [ { "label": "Size", "value": "24×48″" }, { "label": "Thickness", "value": "9 mm" }, { "label": "Finish", "value": "Matte" } ], "specification": [ { "group": "Slip resistance", "rows": [ { "label": "DCOF", "value": "> 0.42", "test_method": "ANSI A326.3" } ] } ], "shipping_options": [ { "source": "US_WAREHOUSE", "lead_time_min_days": 14, "lead_time_max_days": 30, "ships_from_country": "US", "minimum_order_sqft": 1000 }, { "source": "OVERSEAS_DIRECT", "lead_time_min_days": 90, "lead_time_max_days": 120, "ships_from_country": "ES", "minimum_order_sqft": 5000 } ], "suitable_application_areas": [ { "code": "INTERIOR_DRY_FLOOR", "label": "Interior Dry Floor" } ]}specificationleaves out any spec the product does not state.shipping_optionslists each way the product ships, its minimum order and its delivery window. The price comes from an instant quote.
File the search under a project
Section titled “File the search under a project”Send project.external_ref and project.name. Flume saves the project in the user's Flume account. project.status in the response says what happened.
status | Meaning |
|---|---|
FILED | This search created the project |
EXISTING | An earlier search created it |
NOT_FILED | Not saved. reason is MISSING_FIELDS or PROJECT_LIMIT_REACHED |
The search runs either way.
Handle the common errors
Section titled “Handle the common errors”| You get | Do this |
|---|---|
400 IMAGE_REQUIRED | Add the image part |
413 IMAGE_TOO_LARGE | Send a photo under 6,000,000 bytes |
422 IMAGE_UNSUPPORTED | Send a JPEG, PNG or WebP of at most 50 megapixels |
422 VALIDATION_FAILED | Fix the field named in error.field |
429 RATE_LIMITED | Wait Retry-After seconds |
429 DAILY_LIMIT_REACHED | This user's own daily limit is used up (only when Flume set one). Tell the user when it resets: details.reset_at. Other users are not affected |
503 SEARCH_UNAVAILABLE | Retry with backoff. The search is not counted |
See also
Section titled “See also”- Search by photo in the API reference
- Get an instant quote