Fitment
Contact your Marqo account manager to enable Fitment.
Fitment is an addon for Marqo that resolves vehicle compatibility data for parts and accessories catalogs (powersports, automotive, marine, etc.). You upload one or two CSVs describing which products fit which vehicles, and Marqo does the rest — your products are enriched with structured vehicle attributes automatically in the background, and a nested hierarchy is built for cascading Type / Year / Make / Model dropdowns.
Once processing is ready, you search your Marqo index as normal and use the enriched fields as filter parameters. You do not need to call any enrichment endpoint — it runs automatically whenever a CSV is uploaded or updated.
In every example below, customer_id is your Marqo account ID (a UUID, visible in the top-right of the Marqo console). Replace {index_name} and {index_id} with your own index.
Quick Start
A minimal end-to-end flow: upload your fitment CSV, fetch the hierarchy to drive a dropdown, and search Marqo filtered to the selected vehicle.
1. Upload a fitment CSV
The fitment CSV is the file that tells Marqo which products fit which vehicles. Each row maps a product ID to one or more vehicles described by Type / Year / Make / Model.
Two formats are supported. Format A is recommended for large catalogs.
Format A — one row per product, pipe-delimited fitments:
Product ID,Fitments
1001,"ATV>2020>Honda>TRX250|ATV>2021>Honda>TRX250|ATV>2022>Honda>TRX250"
1003,"Motorcycle>2022>Kawasaki>Ninja ZX-6R|Motorcycle>2023>Kawasaki>Ninja ZX-6R"
Format B — one row per fitment entry:
Product ID,type,year,make,model
1001,ATV,2020,Honda,TRX250
1001,ATV,2021,Honda,TRX250
1003,Motorcycle,2022,Kawasaki,Ninja ZX-6R
Download an example fitment CSV to see the full structure. Host your CSV at a public URL, then point Marqo at it:
curl -X POST "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/fitment/from-url" \
-H "content-type: application/json" \
-d '{
"customer_id": "c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c",
"url": "https://d2dhilujomkt2t.cloudfront.net/examples/example_fitment.csv",
"id_field": "Product ID"
}'
Set id_field to whatever your CSV's product ID column is called (e.g. "SKU", "Item Number").
2. (Optional) Upload a keywords CSV
The keywords CSV is an optional second file that adds free-text search keywords per product — things like common nicknames, alternate spellings, or vehicle slang. These are merged into a fitment_keywords field on each product and boost recall on natural-language queries.
Product ID,Fitment
1001,"atv honda trx250 trx 250 recon quad four wheeler"
1003,"motorcycle kawasaki ninja zx6r zx-6r 636 sportbike supersport"
Download an example keywords CSV. Upload it the same way:
curl -X POST "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/keywords/from-url" \
-H "content-type: application/json" \
-d '{
"customer_id": "c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c",
"url": "https://d2dhilujomkt2t.cloudfront.net/examples/example_keywords.csv"
}'
You can always add or update the keywords CSV later — it's independent of the fitment CSV.
3. Wait until processing is ready
curl "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/status?customer_id=c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c"
# Poll until: {"processing_status": "ready", ...}
4. Fetch the hierarchy for your dropdowns
curl "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/hierarchy?customer_id=c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c"
{
"levels": ["type", "year", "make", "model"],
"separator": ">",
"tree": {
"Motorcycle": {
"2023": {
"Kawasaki": ["Ninja ZX-6R"]
}
}
}
}
Cache this on your front end. As the user picks Type → Year → Make → Model, walk the tree to populate the next dropdown — no extra API calls.
5. Search Marqo filtered to the selected vehicle
Once a user has selected a full vehicle in your dropdown, build the composite key (Type>Year>Make>Model, using the separator from the hierarchy response) and filter on fitment_keys. For "Motorcycle / 2023 / Kawasaki / Ninja ZX-6R":
curl -X POST https://ecom.marqo-ep.ai/api/v1/indexes/{index_name}/search \
-H "x-marqo-index-id: {index_id}" \
-H "Content-Type: application/json" \
-d '{
"q": "brake lines",
"filter": "fitment_keys:(Motorcycle>2023>Kawasaki>Ninja ZX-6R)"
}'
fitment_keys is the right choice for an exact vehicle match because it stores the full Type>Year>Make>Model tuple atomically. Filtering on the individual fitment_types / fitment_years / fitment_makes / fitment_models fields works too, but those are independent arrays — so a product that fits 2023 Kawasaki Ninja ZX-6R AND 2022 Honda CBR600 would also match a (bogus) filter for 2023 Honda CBR600, because each clause matches independently. Use the individual fields for partial selections (e.g. user has only picked Type + Make so far) or faceted UIs, and use fitment_keys once a full vehicle is selected. See Filtering Products by Fitment below for more patterns.
That's the whole flow.
Filtering Products by Fitment
Once your fitment CSV has been processed, every product in your index is enriched with the following fields:
| Field | Type | Description |
|---|---|---|
fitment_keys | Array of strings | Composite keys like Motorcycle>2023>Kawasaki>Ninja ZX-6R — one per fitment row |
fitment_types | Array of strings | e.g. ["Motorcycle"] |
fitment_years | Array of strings | e.g. ["2022", "2023"] |
fitment_makes | Array of strings | e.g. ["Kawasaki"] |
fitment_models | Array of strings | e.g. ["Ninja ZX-6R"] |
fitment_keywords | String | Free-text keywords for the vehicle (from the keywords CSV) — boosts recall on natural-language queries |
has_fitment | Boolean | true if the product has any fitment data |
You can filter on any of these using Marqo's filter DSL.
Which field to filter on
- Exact vehicle match (all four levels selected) → use
fitment_keys. It stores the fullType>Year>Make>Modeltuple atomically, so the filter cannot cross-match components from two different fitments on the same product. - Partial vehicle match (user has only selected some levels) → use the individual
fitment_types/fitment_years/fitment_makes/fitment_modelsfields. These are independent arrays, so they're ideal for faceted UIs and dropdown counts but will cross-match for products with multiple fitments (a product that fits 2023 Kawasaki Ninja and 2022 Honda CBR600 will match a filter for 2023 Honda).
Common filter patterns
Exact vehicle match:
{
"filter": "fitment_keys:(Motorcycle>2023>Kawasaki>Ninja ZX-6R)"
}
Exact match against multiple vehicles (e.g. a user selected two vehicles in a garage):
{
"filter": "fitment_keys:(Motorcycle>2023>Kawasaki>Ninja ZX-6R) OR fitment_keys:(ATV>2020>Honda>TRX250)"
}
Partial vehicle match (only Type + Make selected so far):
{
"filter": "fitment_types:Motorcycle AND fitment_makes:Kawasaki"
}
Only show products that have fitment data:
{
"filter": "has_fitment:true"
}
Composite keys and model names containing spaces, hyphens, or other special characters must be wrapped in parentheses — e.g. fitment_keys:(Motorcycle>2023>Kawasaki>Ninja ZX-6R) or fitment_models:(Ninja ZX-6R). See the Filter DSL Guide for full escaping rules.
Hierarchy & Cascading Dropdowns
The hierarchy is a nested tree of all vehicle combinations in your fitment CSV. It powers client-side Type → Year → Make → Model dropdowns with zero API calls after the initial load.
Fetching the hierarchy
curl "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/hierarchy?customer_id=c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c"
Response:
{
"levels": ["type", "year", "make", "model"],
"separator": ">",
"tree": {
"ATV": {
"2006": {
"Honda": ["TRX90", "TRX250EX"],
"Yamaha": ["YFZ450"]
}
},
"Motorcycle": {}
}
}
Client-side dropdown logic
- Load the hierarchy once — typically ~30KB gzipped. Cache it.
- Type dropdown —
Object.keys(tree)→["ATV", "Motorcycle", ...] - Year dropdown — user selects "ATV" →
Object.keys(tree["ATV"])→["2006", ...] - Make dropdown — user selects "2006" →
Object.keys(tree["ATV"]["2006"])→["Honda", "Yamaha"] - Model dropdown — user selects "Honda" →
tree["ATV"]["2006"]["Honda"]→["TRX90", "TRX250EX"]
All interactions are instant — no API calls after the initial hierarchy fetch.
CDN access
The hierarchy is also available via CloudFront CDN for faster global access:
https://<cdn-domain>/fitment/<customer_id>/hierarchy.json
Cached for 5 minutes (s-maxage=3600 at origin).
Upload API
All upload endpoints are under https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/.
There are three ways to get fitment or keywords data in. All three write to the same underlying store and hierarchy.
Option 1: URL upload (recommended for bulk)
Point the service at a remote CSV URL. The file is streamed, parsed, and processed end-to-end. Handles multi-GB files.
# Fitment CSV
curl -X POST "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/fitment/from-url" \
-H "content-type: application/json" \
-d '{
"customer_id": "c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c",
"url": "https://example.com/fitment.csv",
"id_field": "Product ID"
}'
# Keywords CSV
curl -X POST "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/keywords/from-url" \
-H "content-type: application/json" \
-d '{
"customer_id": "c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c",
"url": "https://example.com/keywords.csv",
"id_field": "Product ID"
}'
| Parameter | Required | Default | Description |
|---|---|---|---|
customer_id | yes | Your Marqo account ID (UUID) | |
url | yes | URL to the CSV file | |
id_field | no | "Product ID" | Name of the CSV column containing the product identifier. Set this to match your CSV header (e.g. "SKU", "Item Number", "product_id"). |
Returns 202 with a job_id. Poll /fitment/upload/status until processing_status is ready.
The URL and id_field are stored on your customer record. Marqo checks the URL on an hourly schedule and automatically re-processes if the file has changed. Calling /from-url again with the same URL always re-fetches and re-processes immediately, regardless of whether the file has changed.
Option 2: Presigned PUT (for client-side uploads)
Get a presigned S3 URL and upload the CSV directly from your own application. No size limit on the API side. Processing starts automatically when the file lands.
# Step 1: Get presigned URL
curl -X POST "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/fitment/url" \
-H "content-type: application/json" \
-d '{"customer_id": "c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c", "id_field": "Product ID"}'
# Returns: {"upload_url": "https://s3.amazonaws.com/...", ...}
# Step 2: Upload the CSV
curl -X PUT "<upload_url from step 1>" \
-H "content-type: text/csv" \
--data-binary @fitment.csv
# Step 3: Poll status
curl "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/status?customer_id=c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c"
Option 3: JSON push (for real-time webhooks)
Push individual product fitment data directly. Best for Shopify products/update webhooks or other incremental updates.
curl -X POST "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/json" \
-H "content-type: application/json" \
-d '{
"customer_id": "c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c",
"fitments": {
"product-001": [
{"type": "ATV", "year": "2024", "make": "Honda", "model": "TRX520"}
]
}
}'
Returns 200 with {"status": "ok", "changed_products": 1, "added_vehicle_combos": 1}.
Limit: 500 products per request. For larger payloads use the URL or presigned PUT paths.
Upload Status
GET /fitment/upload/status
Returns the processing state for your customer.
Query params: customer_id (required)
Response:
{
"customer_id": "c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c",
"processing_status": "ready",
"last_job_id": "8f3a-...",
"last_job_kind": "fitment",
"last_processed_at": "2026-04-09T20:14:17.231Z",
"last_rows_written": 125000,
"last_error": "",
"fitment_url": "https://example.com/fitment.csv",
"fitment_id_field": "Product ID",
"keywords_url": "https://example.com/keywords.csv",
"keywords_id_field": "Product ID",
"last_reconciled_at": "2026-04-09T21:00:00.000Z"
}
Status values:
| Status | Meaning |
|---|---|
idle | No uploads have been processed |
queued | Upload request received, waiting to start |
fetching | Streaming your CSV from its URL |
partitioning | Parsing the CSV into shards |
processing | Writing enriched data to the index |
ready | Done. Your index is queryable with fitment filters |
failed | Error occurred. Check last_error for details |
FAQ
I updated the CSV but the search results didn't update. Why?
Marqo checks your hosted CSV for changes on an hourly schedule, so it can take up to an hour for an update to be picked up and re-processed.
If you need the change reflected sooner, re-run the /from-url call against the same URL — that forces an immediate re-fetch and re-processes the file end-to-end:
curl -X POST "https://m9kie4pbmg.execute-api.us-east-1.amazonaws.com/fitment/upload/fitment/from-url" \
-H "content-type: application/json" \
-d '{
"customer_id": "c2f4b8e1-3a7d-4f2e-9b6c-8d1a5e3f7b2c",
"url": "https://example.com/your-fitment.csv",
"id_field": "Product ID"
}'
Then poll /fitment/upload/status until processing_status is ready.