Back to Blog
RevitAPIPythonFilteredElementCollectorBIM Automation

Element Collection with FilteredElementCollector — Complete Guide

Master the art of finding and filtering elements in Revit. This comprehensive guide covers 35+ filtering techniques, from basic category filters to advanced geometric queries.

Paulo Giavoni

Paulo Giavoni

Engineer & BIM Specialist

5 January 20269 min read
Element Collection with FilteredElementCollector — Complete Guide

Introduction to Element Collectors#

The FilteredElementCollector is the foundation of any Revit automation. Every time you need to work with elements — walls, doors, windows, views — you must first find them in the model. The collector is the tool that does this.

Practical analogy: Imagine your Revit model as a huge warehouse. The FilteredElementCollector is the automated search system that finds exactly what you need, without having to search manually.

This guide shows you 35+ different ways to filter elements, from basic filters (category) to advanced ones (geometry, parameters, location).

This is the second article in a three-part series on Revit API development:

  1. Getting Started with BIM Automation
  2. Element Collection with FilteredElementCollector (this article)
  3. UI Selection Methods

Why Use Collectors Instead of Manual Selection?#

Typical scenario without automation:

  • Open Revit
  • Manually select 50 walls
  • Change a parameter on each one
  • Repeat for every floor of the project

With a collector:

  • A script automatically finds all walls that meet precise criteria
  • Modifies all in a single operation
  • Can be repeated instantly on other projects

Key advantages:

  • Precision — no forgotten elements
  • Speed — seconds instead of minutes/hours
  • Reusability — same script for different projects
  • Documentation — the code documents exactly what was done

Required Libraries and Setup#

Before we begin, every Python script in Dynamo that uses Revit API must start with these lines:

Python
1import clr
2clr.AddReference("RevitAPI")
3from Autodesk.Revit.DB import *
4clr.AddReference('RevitServices')
5from RevitServices.Persistence import DocumentManager
6
7doc = DocumentManager.Instance.CurrentDBDocument

What this code does:

  • clr.AddReference — loads the Revit libraries into Python
  • from Autodesk.Revit.DB import * — imports all Revit API classes (Wall, Door, FilteredElementCollector, etc.)
  • DocumentManager — provides access to the current Revit file
  • doc — the variable you'll use to access the model

Note: Copy this block at the beginning of every Python Script node in Dynamo.


Getting Revit Application Information#

You can retrieve various properties of the Revit application using the Application object:

Python
1app = doc.Application
2version_info = f"""
3Version Number: {app.VersionNumber}
4Version Name: {app.VersionName}
5Version Build: {app.VersionBuild}
6Sub Version Number: {app.SubVersionNumber}
7Product: {app.Product}
8Language: {app.Language}
9User Data Folder: {app.CurrentUsersDataFolderPath}
10"""
11print(version_info)

Collecting Model Categories#

To start working with Revit elements, it's useful to understand the available categories:

Python
1from Autodesk.Revit.DB import CategoryType
2
3# Get all categories
4all_categories = doc.Settings.Categories
5
6# Filter for model categories
7model_categories = [c for c in all_categories if c.CategoryType == CategoryType.Model]
8
9# Print category names
10for category in model_categories:
11 print(category.Name)

Category Types#

In Revit API, categories and types are identified by enumerated values. Here's how you can work with them:

Python
1door_category = Category.GetCategory(doc, BuiltInCategory.OST_Doors)
2sub_categories = door_category.SubCategories
3
4print(f"The 'Doors' category has {sub_categories.Size} subcategories:")
5for sub_category in sub_categories:
6 print(f"- {sub_category.Name}")

Changes in Revit 2022#

Revit 2022 introduced 64-bit identifiers for built-in categories:

Python
1door_category_type_id = Category.GetBuiltInCategoryTypeId(BuiltInCategory.OST_Doors)
2print(f"The TypeId for the 'Doors' category is: {door_category_type_id}")

Filter by Element Class#

Understanding Classes vs Categories#

Fundamental concept: In Revit API, "Class" and "Category" are two different things, and confusion between them is the most common mistake for beginners.

  • Class = The type of object in the code (e.g., Wall, Floor, FamilyInstance)
  • Category = The functional group you see in the Revit interface (e.g., "Walls", "Floors", "Doors")

Practical example:

  • A door is a FamilyInstance (class) of the "Doors" category (BuiltInCategory.OST_Doors)
  • A window is a FamilyInstance (class) of the "Windows" category (BuiltInCategory.OST_Windows)
  • A wall is a Wall (class) of the "Walls" category (BuiltInCategory.OST_Walls)

When to filter by class:

  • When you want all elements of that object type
  • For operations specific to the class type (e.g., only Wall has .Flip())

When to filter by category:

  • When you want to distinguish between similar objects (doors vs windows, both FamilyInstance)
  • To read category-specific parameters

Using OfClass Shortcut#

The simplest way to filter by class is using the OfClass method:

Python
1# Get all walls (instances + types)
2walls = FilteredElementCollector(doc).OfClass(Wall).ToElements()
3
4# Get only floor types (not instances)
5floor_types = FilteredElementCollector(doc).OfClass(FloorType).ToElements()
6
7# Get all family instances (doors, windows, furniture, etc.)
8family_instances = FilteredElementCollector(doc).OfClass(FamilyInstance).ToElements()

What does .ToElements() mean? The collector doesn't immediately find all elements — it "promises" them. .ToElements() tells the collector to actually execute the search and return the complete list.

Alternatives to .ToElements():

  • .FirstElement() — returns only the first element found (faster)
  • .GetElementCount() — counts elements without loading them (very fast)
  • .ToElementIds() — returns only Ids instead of full objects (lightweight)

Get all family instances

family_instances = FilteredElementCollector(doc).OfClass(FamilyInstance).ToElements()

Text
1### Main Revit API Classes
2
3Here are the most commonly used classes for filtering:
4
5| Class | Description |
6|-------|-------------|
7| `Wall` | Wall elements |
8| `Floor` | Floor elements |
9| `Ceiling` | Ceiling elements |
10| `FamilyInstance` | All family-based elements |
11| `Level` | Level elements |
12| `Grid` | Grid lines |
13| `View3D` | 3D views |
14| `ViewPlan` | Plan views |
15| `ViewSection` | Section views |
16| `ViewSheet` | Sheets |
17| `Material` | Materials |
18| `Family` | Loaded families |
19
20---
21
22## Essential Quick Filters
23
24Quick filters are evaluated at the memory level and are the most efficient:
25
26```python
27# Get only element types (not instances)
28types = FilteredElementCollector(doc).WhereElementIsElementType().ToElements()
29
30# Get only element instances (not types)
31instances = FilteredElementCollector(doc).WhereElementIsNotElementType().ToElements()
32
33# Get all elements of a category
34doors = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Doors).ToElements()

Combining Quick Filters#

You can chain multiple filters for more precise results:

Python
1# Get all door instances (not door types)
2door_instances = FilteredElementCollector(doc) \
3 .OfCategory(BuiltInCategory.OST_Doors) \
4 .WhereElementIsNotElementType() \
5 .ToElements()
6
7# Get all wall types
8wall_types = FilteredElementCollector(doc) \
9 .OfClass(WallType) \
10 .WhereElementIsElementType() \
11 .ToElements()

Filter by Parameter Values#

One of the most powerful features is filtering by parameter values:

Python
1from Autodesk.Revit.DB import FilteredElementCollector, BuiltInParameter
2from Autodesk.Revit.DB import ParameterValueProvider, FilterStringRule
3from Autodesk.Revit.DB import FilterStringEquals, ElementParameterFilter
4
5# Create a parameter value provider
6provider = ParameterValueProvider(ElementId(BuiltInParameter.ALL_MODEL_MARK))
7
8# Create a filter rule (elements where Mark equals "A1")
9rule = FilterStringRule(provider, FilterStringEquals(), "A1", False)
10
11# Create and apply the filter
12param_filter = ElementParameterFilter(rule)
13
14elements = FilteredElementCollector(doc) \
15 .OfCategory(BuiltInCategory.OST_Walls) \
16 .WherePasses(param_filter) \
17 .ToElements()

Filter by Level#

Filter elements associated with a specific level:

Python
1# First, get the level you want to filter by
2levels = FilteredElementCollector(doc).OfClass(Level).ToElements()
3target_level = None
4
5for level in levels:
6 if level.Name == "Level 1":
7 target_level = level
8 break
9
10if target_level:
11 # Create a level filter
12 level_filter = ElementLevelFilter(target_level.Id)
13
14 # Get all elements on that level
15 elements_on_level = FilteredElementCollector(doc) \
16 .WherePasses(level_filter) \
17 .ToElements()
18
19 print(f"Found {len(elements_on_level)} elements on {target_level.Name}")

Elements within a BoundingBox#

Find elements that are completely within a defined bounding box:

Python
1from Autodesk.Revit.DB import BoundingBoxContainsPointFilter, Outline
2
3# Define the bounding box corners
4min_point = XYZ(0, 0, 0)
5max_point = XYZ(100, 100, 30) # feet
6
7# Create an outline from the points
8outline = Outline(min_point, max_point)
9
10# Create the bounding box filter
11bb_filter = BoundingBoxIsInsideFilter(outline)
12
13# Collect elements within the box
14elements_in_box = FilteredElementCollector(doc) \
15 .WherePasses(bb_filter) \
16 .ToElements()
17
18print(f"Found {len(elements_in_box)} elements within the bounding box")

Elements Intersecting a BoundingBox#

Find elements that intersect with a bounding box:

Python
1from Autodesk.Revit.DB import BoundingBoxIntersectsFilter, Outline
2
3# Define the search volume
4min_point = XYZ(0, 0, 0)
5max_point = XYZ(50, 50, 15)
6
7outline = Outline(min_point, max_point)
8bb_intersect_filter = BoundingBoxIntersectsFilter(outline)
9
10intersecting_elements = FilteredElementCollector(doc) \
11 .WherePasses(bb_intersect_filter) \
12 .ToElements()
13
14print(f"Found {len(intersecting_elements)} elements intersecting the box")

Filter Family Symbols (Types)#

Get all types of a specific family:

Python
1# Collect all family symbols (types)
2family_symbols = FilteredElementCollector(doc) \
3 .OfClass(FamilySymbol) \
4 .ToElements()
5
6# Group by family name
7families = {}
8for symbol in family_symbols:
9 family_name = symbol.Family.Name
10 if family_name not in families:
11 families[family_name] = []
12 families[family_name].append(symbol.Name)
13
14# Print results
15for family, types in families.items():
16 print(f"\n{family}:")
17 for t in types:
18 print(f" - {t}")

Logic Filters#

Combine filters using logical operations:

Python
1from Autodesk.Revit.DB import LogicalAndFilter, LogicalOrFilter
2
3# Create individual category filters
4wall_filter = ElementCategoryFilter(BuiltInCategory.OST_Walls)
5floor_filter = ElementCategoryFilter(BuiltInCategory.OST_Floors)
6door_filter = ElementCategoryFilter(BuiltInCategory.OST_Doors)
7
8# Combine with OR logic (walls OR floors OR doors)
9or_filter = LogicalOrFilter([wall_filter, floor_filter, door_filter])
10
11# Get all walls, floors, and doors
12combined_elements = FilteredElementCollector(doc) \
13 .WherePasses(or_filter) \
14 .WhereElementIsNotElementType() \
15 .ToElements()
16
17print(f"Found {len(combined_elements)} walls, floors, and doors")

Exclusive Filter#

Create a filter that excludes specific elements:

Python
1from Autodesk.Revit.DB import ExclusionFilter
2
3# Get IDs of elements to exclude
4elements_to_exclude = [ElementId(12345), ElementId(67890)]
5
6# Create exclusion filter
7exclusion_filter = ExclusionFilter(elements_to_exclude)
8
9# Collect all walls except the excluded ones
10filtered_walls = FilteredElementCollector(doc) \
11 .OfClass(Wall) \
12 .WherePasses(exclusion_filter) \
13 .ToElements()

Filter Elements Visible in View#

Collect elements that are visible in a specific view:

Python
1# Get the active view
2active_view = doc.ActiveView
3
4# Collect elements visible in this view
5visible_elements = FilteredElementCollector(doc, active_view.Id) \
6 .WhereElementIsNotElementType() \
7 .ToElements()
8
9print(f"Found {len(visible_elements)} visible elements in {active_view.Name}")

Quick Filters vs Slow Filters#

Understanding the difference is crucial for performance:

Quick Filters (Fast)#

Evaluated at the element level without opening element data:

  • OfClass()
  • OfCategory()
  • WhereElementIsElementType()
  • WhereElementIsNotElementType()

Slow Filters (More Powerful)#

Require element data to be opened:

  • ElementParameterFilter
  • BoundingBoxFilter
  • ElementIntersectsFilter

Best Practice: Always apply quick filters first, then slow filters:

Python
1# GOOD: Quick filter first, then slow filter
2elements = FilteredElementCollector(doc) \
3 .OfCategory(BuiltInCategory.OST_Walls) \
4 .WhereElementIsNotElementType() \
5 .WherePasses(slow_filter) \
6 .ToElements()
7
8# BAD: Slow filter on entire database
9elements = FilteredElementCollector(doc) \
10 .WherePasses(slow_filter) \
11 .ToElements()

Structural Element Filtering#

Filter structural elements specifically:

Python
1# Get structural walls
2structural_walls = FilteredElementCollector(doc) \
3 .OfCategory(BuiltInCategory.OST_StructuralColumns) \
4 .WhereElementIsNotElementType() \
5 .ToElements()
6
7# Get structural framing (beams)
8beams = FilteredElementCollector(doc) \
9 .OfCategory(BuiltInCategory.OST_StructuralFraming) \
10 .WhereElementIsNotElementType() \
11 .ToElements()
12
13# Get structural foundations
14foundations = FilteredElementCollector(doc) \
15 .OfCategory(BuiltInCategory.OST_StructuralFoundation) \
16 .WhereElementIsNotElementType() \
17 .ToElements()
18
19print(f"Structural elements: {len(structural_walls)} columns, {len(beams)} beams, {len(foundations)} foundations")

Labels and Localization#

Working with labels in different languages:

Python
1from Autodesk.Revit.DB import LabelUtils, BuiltInParameter, LanguageType
2
3def get_parameter_labels(language=LanguageType.English):
4 labels = []
5 for param in BuiltInParameter:
6 try:
7 label = LabelUtils.GetLabelFor(param, language)
8 labels.append(f"{param}: {label}")
9 except:
10 pass
11 return labels
12
13english_labels = get_parameter_labels()
14print("First 5 parameter labels in English:")
15for label in english_labels[:5]:
16 print(label)

Performance Best Practices#

  1. Use quick filters first — They're evaluated before element data is loaded

  2. Limit the scope — Use view-specific collectors when possible:

    Python
    1FilteredElementCollector(doc, view.Id)
  3. Use FirstElement() for single results:

    Python
    1first_wall = FilteredElementCollector(doc).OfClass(Wall).FirstElement()
  4. Use GetElementCount() for counting:

    Python
    1wall_count = FilteredElementCollector(doc).OfClass(Wall).GetElementCount()
  5. Avoid repeated collectors — Cache results when reusing:

    Python
    1walls = list(FilteredElementCollector(doc).OfClass(Wall).ToElements())

What's Next?#

Continue your Revit API journey with the next article in this series:

Questions or Feedback?

I'd love to hear your thoughts on this article. Reach out directly and let's start a conversation.

Follow me on LinkedIn for more BIM tips and updates