yeah as BVS is indicating, MEP elements for levels don’t follow a “Norm” persee when it comes to collecting thier level.
You can use something like this to collect the levels of lots of different types of MEP elements.
(Noting that this isn’t the most efficient way to do this but is fairly readable for someone not familiar with Python)
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *
clr.AddReference('System')
from System.Collections.Generic import List
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
def get_level_element(e, param):
try:
level_id = e.get_Parameter(param).AsElementId()
return doc.GetElement(level_id)
except:
return None
# Assign Document
doc = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
# Preparing input
if isinstance(IN[0], list):
elements = UnwrapElement(IN[0])
else:
elements = [UnwrapElement(IN[0])]
output = []
# Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
for e in elements:
elem = []
# Levels
level_element = get_level_element(e, BuiltInParameter.LEVEL_PARAM)
if not level_element:
level_element = get_level_element(e, BuiltInParameter.RBS_START_LEVEL_PARAM)
if not level_element:
level_element = get_level_element(e, BuiltInParameter.FAMILY_BASE_LEVEL_PARAM)
if not level_element:
level_element = get_level_element(e, BuiltInParameter.FAMILY_LEVEL_PARAM)
if not level_element:
try:
level_element = e.get_Parameter(BuiltInParameter.SCHEDULE_LEVEL_PARAM).AsElementId()
level_element = doc.GetElement(level_element)
except:
level_element = None
# Output
elem.append(level_element)
output.append(elem)
TransactionManager.Instance.TransactionTaskDone()
# Flatten the output list
OUT = [item for sublist in output for item in sublist]
The kicker is that with differing version of revit the parameter you are looking for actually differs, so to start you can use RevitLookUp
Start by selecting an element and snooping the current selection
Next is a little bit more but stick with me, once you have the element snooped you can scroll down till you’ll find “Parameters” click it (In the example below i clicked it twice to bring up 2 windows since the element i selected ectually had 2 different level parameters associated with it.
after clicking the parameters, i searched for “level” in both windows and it is the “Definition name” that you are looking for.
Once you have your deffinition, you can continue to add more if not statements to the python code above until you cover all the varaible types of parameters you are looking for.
You can continue to add statements as this.
The big kicker here is if your element looks like this and you need to report its level, then you need to correct this before any of the above will work. You can, inshort find the nearest level by comparing the elevation of the element to the elevation of levels in your project (This would vary depending on element type ie: sanitary pipes ref wants to be the level above the element, where lights want to be the level below the element. (at least in my world)

If you want to take this approach then there is a node in the genius loci package that is great at grabbing the element elevation to compare to.
I hope this helps you solve your issue.
Good Luck!