Query Products
The query endpoints let you search and filter Product across models using a JSON-style query language. You can filter by IFC, property values, quantities, and more. Each query returns a list of Product objects.
Filtering by model and revision
All query endpoints share the same filtering logic for models and revisions, controlled through the model and revision parameters. Both accept multiple values and are resolved in order:
- If model is specified, the query targets the latest revision of each given model.
- If only revision is specified, the query targets those exact revisions.
- If neither is specified, the query targets the latest revision of every model in the project.
All endpoints accept model and revision as repeatable query parameters (e.g. ?model=id1&model=id2). POST endpoints also accept models and revisions arrays (note the plural form) in the request body. When both are present, the body value takes precedence.
List product fields
List the fields that can be queried for products.
GET /v2/projects/{project-id}/ifc/products/fieldsQuery parameters
Name | Type | Description |
|---|---|---|
data-mode | String | explicit (default) returns fields as stored. inferred merges type properties with product properties |
model | String | Filter by model ID |
revision | String | Filter by revision ID |
page | Number | Page number (default 1) |
pageSize | Number | Results per page (default 1000) |
Example
curl -X GET \
"https://api.catenda.com/v2/projects/{project-id}/ifc/products/fields" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Accept: application/json'Response
[
{
"field": "attributes.GlobalId",
"type": "string",
"measureType": null
},
{
"field": "attributes.Name",
"type": "string",
"measureType": null
},
{
"field": "ifcType",
"type": "string",
"measureType": null
},
{
"field": "objectId",
"type": "number",
"measureType": null
},
{
"field": "quantitySets.BaseQuantities.quantities.Width",
"type": "number",
"measureType": "length"
},
{
"field": "propertySets.Pset_WallCommon.properties.IsExternal",
"type": "boolean",
"measureType": null
}
]The measureType field indicates the physical measure type of a field. Common values include "length", "area", and null for non-measure fields.
Field names map directly to the Product JSON structure. For example, propertySets.Pset_WallCommon.properties.IsExternal corresponds to the IsExternal property inside the Pset_WallCommon property set.
A POST variant of this endpoint is also available. It accepts the same query parameters and an optional request body with models and revisions arrays.
Query suggested field values
Query suggested values for a specific field. This is useful for building autocomplete interfaces or discovering available values.
POST /v2/projects/{project-id}/ifc/products/fields/suggested_valuesQuery parameters
Name | Type | Description |
|---|---|---|
data-mode | String | explicit (default) returns fields as stored. inferred merges type properties with product properties |
model | String | Filter by model ID |
revision | String | Filter by revision ID |
Request body
Name | Type | Description |
|---|---|---|
field | String | The field to get values for (required) |
search | String | Search term to filter values, or an empty string to return the most common values (required) |
models | Array | Filter by model IDs |
revisions | Array | Filter by revision IDs |
Example
curl -X POST \
"https://api.catenda.com/v2/projects/{project-id}/ifc/products/fields/suggested_values" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"field": "ifcType",
"search": "wall"
}'Response
[
{
"value": "IfcWall"
},
{
"value": "IfcWallStandardCase"
}
]Query products
Query products using a JSON-style query language based on the MongoDB query language.
POST /v2/projects/{project-id}/ifc/productsQuery parameters
Name | Type | Description |
|---|---|---|
data-mode | String | explicit (default) returns fields as stored. inferred merges type properties with product properties |
model | String | Filter by model ID |
revision | String | Filter by revision ID |
page | Number | Page number (default 1) |
pageSize | Number | Results per page (default 100, max 1000) |
Request body
Name | Type | Description |
|---|---|---|
query | Object | Query expression (required) |
fields | Object | Field projection — use 1 to include specific fields or 0 to exclude them |
models | Array | Filter by model IDs |
revisions | Array | Filter by revision IDs |
Filter by model or revision when possible to reduce query scope and improve performance.
Example
curl -X POST \
"https://api.catenda.com/v2/projects/{project-id}/ifc/products" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"query": {
"ifcType": { "$eq": "IfcWall" }
},
"fields": {
"attributes.Name": 1,
"ifcType": 1
}
}'Response
Returns an array of Product objects.
Field projection
The fields parameter controls which fields are returned for each product. Use dot-notation to target nested fields. If fields is omitted or empty, all fields are returned.
Include specific fields only:
{
"fields": {
"attributes.Name": 1,
"ifcType": 1
}
}Returns only the listed fields.
objectId is always included unless explicitly excluded with "objectId": 0.
Exclude specific fields:
{
"fields": {
"quantitySets": 0,
"materials": 0
}
}Returns all fields except the listed ones.
You can mix 1 and 0 in a single projection. Fields are processed in the order given, so overlapping paths may produce different results depending on their sequence.
Query language
Implicit equality
A field mapped directly to a value implies $eq.
{ "query": { "ifcType": "IfcWall" } }is equivalent to:
{ "query": { "ifcType": { "$eq": "IfcWall" } } }Comparison operators
$eq — Equal
{ "query": { "ifcType": { "$eq": "IfcWall" } } }$ne — Not equal
{ "query": { "ifcType": { "$ne": "IfcSpace" } } }$gt / $gte — Greater than / Greater than or equal
{ "query": { "quantitySets.BaseQuantities.quantities.Width": { "$gt": 0.2 } } }{ "query": { "quantitySets.BaseQuantities.quantities.Width": { "$gte": 0.19 } } }$lt / $lte — Less than / Less than or equal
{ "query": { "quantitySets.BaseQuantities.quantities.Height": { "$lt": 3.0 } } }{ "query": { "quantitySets.BaseQuantities.quantities.Height": { "$lte": 2.8 } } }Set operators
$in — Match any value in array
{ "query": { "ifcType": { "$in": ["IfcWall", "IfcSlab", "IfcColumn"] } } }$nin — Match none of the values
{ "query": { "ifcType": { "$nin": ["IfcSpace", "IfcOpeningElement"] } } }Logical operators
$and — Match all conditions
{
"query": {
"$and": [
{ "ifcType": "IfcWall" },
{ "attributes.Name": { "$regex": "exterior", "$options": "i" } }
]
}
}$or — Match any condition
{
"query": {
"$or": [{ "ifcType": "IfcWall" }, { "ifcType": "IfcSlab" }, { "ifcType": "IfcColumn" }]
}
}$not — Negate a condition
{
"query": {
"attributes.Name": {
"$not": { "$regex": "internal", "$options": "i" }
}
}
}String operators
$regex — Regular expression match
{ "query": { "attributes.Name": { "$regex": "Wall" } } }Use $options to set flags:
{ "query": { "attributes.Name": { "$regex": "wall", "$options": "i" } } }Common patterns:
Pattern | Description |
|---|---|
"wall" | Contains "wall" |
"^Basic Wall" | Starts with "Basic Wall" |
"200mm$" | Ends with "200mm" |
Available $options flags
Flag | Description |
|---|---|
i | Case-insensitive matching |
m | Multi-line (^/$ match line boundaries) |
s | Dot matches newlines |
Do not use regex syntax like "/pattern/i". The pattern and options are always separate fields.
Existence operator
$exists — Check if field is present
{ "query": { "propertySets.Pset_WallCommon.properties.FireRating": { "$exists": true } } }{ "query": { "propertySets.Pset_WallCommon.properties.FireRating": { "$exists": false } } }Full-text search
$text / $search — Search across all fields
Performs full-text search across all indexed string fields. Results are ranked by relevance.
{ "query": { "$text": { "$search": "concrete structural" } } }IFC-specific operator
$ifcType — Match IFC type hierarchy
Matches the specified IFC type and all its subtypes.
{ "query": { "ifcType": { "$ifcType": "IfcWall" } } }This matches IfcWall, IfcWallStandardCase, IfcWallElementedCase, etc.
In contrast, $eq matches only the exact type:
{ "query": { "ifcType": { "$eq": "IfcWall" } } }$ifcType accepts a single string. To match multiple IFC types, wrap them in $or:
{
"query": {
"$or": [{ "ifcType": { "$ifcType": "IfcWall" } }, { "ifcType": { "$ifcType": "IfcSlab" } }]
}
}Range queries
Combine comparison operators on the same field to create range queries:
{
"query": {
"quantitySets.BaseQuantities.quantities.Width": {
"$gte": 0.15,
"$lte": 0.25
}
}
}This is equivalent to using $and:
{
"query": {
"$and": [
{ "quantitySets.BaseQuantities.quantities.Width": { "$gte": 0.15 } },
{ "quantitySets.BaseQuantities.quantities.Width": { "$lte": 0.25 } }
]
}
}Examples
The examples below combine multiple operators to cover common real-world queries.
Find exterior walls wider than 190mm
curl -X POST \
"https://api.catenda.com/v2/projects/{project-id}/ifc/products?data-mode=inferred" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"query": {
"$and": [
{ "ifcType": { "$ifcType": "IfcWall" } },
{ "attributes.Name": { "$regex": "exterior", "$options": "i" } },
{ "quantitySets.BaseQuantities.quantities.Width": { "$gte": 0.19 } }
]
}
}'Find elements with fire rating
curl -X POST \
"https://api.catenda.com/v2/projects/{project-id}/ifc/products" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"query": {
"$and": [
{ "propertySets.Pset_WallCommon.properties.FireRating": { "$exists": true } },
{ "propertySets.Pset_WallCommon.properties.FireRating": { "$ne": "" } }
]
}
}'Full-text search
curl -X POST \
"https://api.catenda.com/v2/projects/{project-id}/ifc/products" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"query": {
"$text": { "$search": "concrete" }
}
}'Complex nested query
Find walls (including subtypes) that are not named "internal", and are either wider than 200mm or marked as external:
curl -X POST \
"https://api.catenda.com/v2/projects/{project-id}/ifc/products?data-mode=inferred" \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
"query": {
"$and": [
{
"$or": [
{ "ifcType": { "$ifcType": "IfcWall" } },
{ "ifcType": { "$ifcType": "IfcCurtainWall" } }
]
},
{ "attributes.Name": { "$not": { "$regex": "internal", "$options": "i" } } },
{
"$or": [
{ "quantitySets.BaseQuantities.quantities.Width": { "$gte": 0.2 } },
{ "propertySets.Pset_WallCommon.properties.IsExternal": true }
]
}
]
}
}'Operator quick reference
Operator | Type | Example |
|---|---|---|
$eq | Comparison | { "field": { "$eq": "value" } } |
$ne | Comparison | { "field": { "$ne": "value" } } |
$gt | Comparison | { "field": { "$gt": 10 } } |
$gte | Comparison | { "field": { "$gte": 10 } } |
$lt | Comparison | { "field": { "$lt": 10 } } |
$lte | Comparison | { "field": { "$lte": 10 } } |
$in | Set | { "field": { "$in": ["a", "b"] } } |
$nin | Set | { "field": { "$nin": ["a", "b"] } } |
$and | Logical | { "$and": [ ... ] } |
$or | Logical | { "$or": [ ... ] } |
$not | Logical | { "field": { "$not": { ... } } } |
$regex | String | { "field": { "$regex": "pattern" } } |
$options | String | { "field": { "$regex": "...", "$options": "i" } } |
$exists | Existence | { "field": { "$exists": true } } |
$text | Search | { "$text": { "$search": "terms" } } |
$ifcType | IFC | { "ifcType": { "$ifcType": "IfcWall" } } |