Skip to content
PyPath

FilteredElementCollector explained, with Python examples

Every Revit script starts by finding elements. How the collector works, which filters you need, and why you sometimes find too many elements or none at all.

4 min readUpdated October 10, 2026Checked against the Revit API 2025 documentation

Short answer: FilteredElementCollector(doc) searches the model for elements. You add one or more filters, such as .OfCategory(BuiltInCategory.OST_Doors) or .OfClass(Wall), and usually .WhereElementIsNotElementType() to leave out the types. Then you read the result with ToElements(), FirstElement(), GetElementCount() or a for loop.

What the collector does

A Revit model is one big collection of elements: walls, doors, pipes, but also views, levels and types. With the FilteredElementCollector you ask the model for exactly the part you need. You always start like this:

script.py
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

doc = __revit__.ActiveUIDocument.Document

You may not read a collector without a filter. Revit then stops with The collector does not have a filter applied. Extraction or iteration of elements is not permitted without a filter. So you always have to say what you are looking for.

Filtering by category: OfCategory

The category is what you see in Revit under Visibility/Graphics: doors, walls, pipes. In code it is a BuiltInCategory: OST_Doors, OST_Walls, OST_PipeCurves (pipes), OST_CurtainWallPanels (curtain wall panels). This is the filter you will use most.

Types or instances?

The classic pitfall. A category also contains the types: the door types from your family are counted too if you do not say otherwise. On the simulated office model it looks like this:

CountDoors.pushbutton/script.py
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

doc = __revit__.ActiveUIDocument.Document

everything = (FilteredElementCollector(doc)
              .OfCategory(BuiltInCategory.OST_Doors)
              .GetElementCount())
doors = (FilteredElementCollector(doc)
         .OfCategory(BuiltInCategory.OST_Doors)
         .WhereElementIsNotElementType()
         .GetElementCount())
print("Including types: {}".format(everything))
print("Doors only:      {}".format(doors))
Output on the simulated office model
Including types: 17
Doors only:      13

If you do want the types (for example all wall types), use WhereElementIsElementType().

Filtering by class: OfClass

OfClass filters on the kind of object in the API. OfClass(Wall) returns only real walls, no wall types, because those are of class WallType:

WallTypes.pushbutton/script.py
from Autodesk.Revit.DB import FilteredElementCollector, Wall

doc = __revit__.ActiveUIDocument.Document

names = set()
for wall in FilteredElementCollector(doc).OfClass(Wall):
    names.add(wall.Name)
for name in sorted(names):
    print(name)
Output on the simulated office model (Dutch type names)
Binnenwand 100 metal stud
Buitenwand 300 kalkzandsteen

For a wall, Name returns the name of its wall type. That is why there are two names, even though there are twenty walls.

Reading: ToElements, FirstElement, GetElementCount

  • ToElements(): a list of all elements. Handy if you need them more than once; len() works on it.
  • FirstElement(): just the first element, or None if there is nothing.
  • GetElementCount(): just the number. Faster than fetching everything and counting.
  • ToElementIds(): just the ElementIds, for example to make a selection.
  • A for loop straight over the collector, if you visit each element once.

Filtering on a parameter value

You can also have the collector filter on the value of a parameter, for example all pipes thicker than 32 mm. That works with a rule and WherePasses. Note: Revit works in feet internally, so convert the limit first.

LargePipes.pushbutton/script.py
from Autodesk.Revit.DB import *

doc = __revit__.ActiveUIDocument.Document

# Rule: diameter greater than 32 mm (Revit works in feet internally)
limit = UnitUtils.ConvertToInternalUnits(32, UnitTypeId.Millimeters)
rule = ParameterFilterRuleFactory.CreateGreaterRule(
    ElementId(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM), limit, 1e-6)

large = (FilteredElementCollector(doc)
         .OfCategory(BuiltInCategory.OST_PipeCurves)
         .WhereElementIsNotElementType()
         .WherePasses(ElementParameterFilter(rule))
         .GetElementCount())
print("Pipes larger than 32 mm: {}".format(large))
Output on the simulated greenhouse model
Pipes larger than 32 mm: 20

The 1e-6 is the tolerance for rounding differences in decimal numbers.

Pitfall: OfClass(Panel) and OfClass(Mullion)

Curtain wall panels and mullions seem to have their own class, but OfClass(Panel) raises an error:

Error message
Autodesk.Revit.Exceptions.ArgumentException: Input type(Autodesk.Revit.DB.Panel) is of an element type that exists in the API, but not in Revit's native object model. Try using Autodesk.Revit.DB.FamilyInstance instead, and then post-processing the results to find the elements of interest.

For panels and mullions, use the category:

Panels.pushbutton/script.py
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

doc = __revit__.ActiveUIDocument.Document

panels = (FilteredElementCollector(doc)
          .OfCategory(BuiltInCategory.OST_CurtainWallPanels)
          .WhereElementIsNotElementType()
          .ToElements())
print("Panels: {}".format(len(panels)))
Output on the simulated Venlo greenhouse
Panels: 608

For mullions the category is OST_CurtainWallMullions.

Speed: let the collector do the work

In a large model it makes a big difference whether you fetch every element and filter in Python, or let the collector find the right ones straight away. So filter as much as possible in the collector (category, class, parameter rule) and only then loop in Python over what is left. Your code gets shorter too.

All collector methods are in the Revit API documentation at revitapidocs.com.