Studio Matrx Monthly · Volume 1 · Issue 3 · August 2026
Amogh N P
 In loving memory of Amogh N P — Architect · Designer · Visionary 
The Revit API BasicsLesson 7.2
PSD for Architecture, Planning & Urban Design/Module 7 · Scripting Revit & Dynamo

Lesson 7.2 · Scripting Revit & Dynamo

The Revit API Basics

The model as objects you can query - elements, parameters, transactions, and the collector that finds them

13 min Interactive lessonFree · open lessonByAmogh N P· Architect & interior designer
The hook

Every wall, door and sheet in a Revit model is already an object with a name, an id and a bag of parameters. The API is the door that lets your code reach in and read - or change - all of them.

A Revit model is not really a drawing - it is a database of objects. A wall is an object. It has an id, a type, a location, and a set of parameters: height, fire rating, comments. The same is true of doors, rooms, views and sheets. When you edit a value in Revit's properties palette, you are changing that object.

The Revit API (Application Programming Interface) is the published set of objects and methods that lets code do the same thing the interface does - find elements, read their parameters, and change them. This lesson is not an exhaustive tour; it is the four ideas that unlock everything else: what an element is, how parameters work, how the FilteredElementCollector finds the elements you want, and why every change must sit inside a transaction. Get these four and the model becomes something you can program.

Element -> parameters. Collector finds them. Transaction makes changes stick. That is the whole door.

Elements and parameters: the model as objects

In the Revit API, nearly everything in the model is an Element. Walls, doors, rooms, views, sheets, even wall types are elements. Every element has an ElementId - a stable numeric identity that never changes for the life of that object - which is how you refer to it unambiguously in code, and, as a later lesson shows, how you match a spreadsheet row back to the right wall.

What makes elements useful to script is their parameters. A parameter is a named slot holding a value: Comments, Fire Rating, Mark, Height. You read one by name with LookupParameter, then ask for its value in the right type, because Revit stores different parameters as text, numbers, yes/no flags or references to other elements:

python
p = wall.LookupParameter("Comments")
if p:                         # None if the parameter does not exist
    text = p.AsString()       # AsDouble / AsInteger / AsValueString / AsElementId
    print(text)

Two honest cautions save hours. First, `LookupParameter` returns `None` when the name does not exist - a different type, a typo, or a shared parameter that is not loaded - so you must check before using it. Second, Revit stores lengths internally in decimal feet regardless of your project units, so a wall height read with AsDouble comes back in feet even in a millimetre project; convert with UnitUtils when you need real-world units. Elements carrying parameters is the whole mental model: to automate Revit is mostly to find the right elements and read or write the right parameters on them.

One more distinction pays off early: parameters come in flavours. Built-in parameters are the ones Revit defines for every project - Comments, Mark, Fire Rating - reachable by name with LookupParameter or, more robustly, through the BuiltInParameter enumeration, which does not break if the interface language changes. Project and shared parameters are ones your template or a family author added, and a shared parameter must be loaded into the project before it exists to look up - a common reason LookupParameter returns None. You do not need to master the taxonomy now; just know that if a parameter you can see in Revit comes back as None in code, the name, the type, or a missing shared-parameter definition is usually why.

ELEMENT -> PARAMETERSWallan ElementId: 348921LookupParameterComments"Checked" (AsString)Fire Rating"2 hr" (AsString)Mark"W-14" (AsString)Height9.84 ft (AsDouble)Parameters are named slots. Height reads in internal feet even in a millimetre project - convert deliberately.
Zoom
An element is an object carrying named parameters. A wall has a stable ElementId and a bag of key-value parameters - Comments, Fire Rating, Mark, Height. LookupParameter fetches one by name (returning None if it is absent), and you read the value in its type. To script Revit is mostly to find elements and read or write their parameters.

FilteredElementCollector: finding the elements you want

You rarely want every element - you want all the walls, or every sheet, or all the doors on one level. The tool for that is the FilteredElementCollector, the standard way the API searches the model. You start a collector on the document, then narrow it with filters, then pull the results into a list.

The two filters you will use constantly are by category and element-versus-type. A category is a kind of thing - OST_Walls, OST_Doors, OST_Sheets in the BuiltInCategory enumeration. And every category has both instances (the actual walls placed in the model) and types (the wall definitions in the family). WhereElementIsNotElementType() keeps the placed instances; you almost always want that:

python
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory

walls = (FilteredElementCollector(doc)
         .OfCategory(BuiltInCategory.OST_Walls)
         .WhereElementIsNotElementType()
         .ToElements())

print(len(walls))   # how many walls in the model

Where doc comes from depends on how you run the script, which the next section covers. The collector is efficient - it queries Revit's own database rather than looping over the whole model - and it composes: chain .OfClass(...) to filter by API class, or pass a level id to limit by level. Reading it aloud almost describes itself: collect from the document, of category walls, not the types, to elements. Once you can reliably gather exactly the set you mean, everything downstream - reading, auditing, changing - is just a loop over that set.

FILTEREDELEMENTCOLLECTORdocthe modelOfCategoryOST_Wallsa categoryWhereElementIsNotElementTypeplaced, not typesToElementslist of wallsRead it aloud: collect from the document, of category walls, not the types, to elements.Then loop the list to read, audit or (inside a transaction) change each wall.
Zoom
The FilteredElementCollector as a pipeline: start on the document, narrow by category, keep placed instances rather than types, then pull the results to a list. It queries Revit's database efficiently instead of looping the whole model, and reads almost like plain English.

Transactions: why a change only sticks inside one

Reading the model is free. Changing it is not: the Revit API refuses to modify anything unless the change happens inside a transaction. A transaction is a named, all-or-nothing bundle of edits - it either commits as a whole or rolls back as a whole, which is what keeps the model consistent and gives you a single undo step. Try to set a parameter outside one and the API throws an error immediately.

The raw pattern is start, change, commit:

python
from Autodesk.Revit.DB import Transaction

t = Transaction(doc, "Set wall comments")
t.Start()
for w in walls:
    p = w.LookupParameter("Comments")
    if p:
        p.Set("Checked 2026-08")
t.Commit()

The name you give the transaction ("Set wall comments") is what appears in Revit's undo history, so make it descriptive. Notice that many edits sit inside one transaction - that is deliberate and fast; you do not start a new transaction per wall. How you open a transaction depends on your host. In a Dynamo Python node you do not use Transaction directly - you call TransactionManager.Instance.EnsureInTransaction(doc) before your edits and TransactionManager.Instance.TransactionTaskDone() after, letting Dynamo manage it. In pyRevit you use the raw Transaction above, or the tidy with revit.Transaction("name"): helper that starts and commits for you. The concept is identical everywhere: no change without a transaction, and one transaction can carry a whole batch of edits.

Where doc comes from: Dynamo vs pyRevit

Every script above assumed a variable doc - the open model. Getting it is the one piece that differs by how you run the code, and there are two common hosts worth knowing.

Dynamo you already met: a visual graph with a Python Script node, run from inside Revit. There, you reach the document through Dynamo's services:

python
import clr
clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager

doc = DocumentManager.Instance.CurrentDBDocument

pyRevit is a different and very popular route: a free, open-source add-in that turns Python scripts into buttons on a Revit ribbon tab. You write a .py file, and pyRevit gives it a toolbar button; click it and the script runs against the live model. In pyRevit the current model is handed to you as __revit__:

python
doc = __revit__.ActiveUIDocument.Document

Which should you learn? They are complementary. Dynamo shines for graph-shaped, data-flow work and for people who like seeing the pipeline; pyRevit shines for turning a script into a reusable tool your whole office clicks like any other Revit command, and it is closer to writing plain Python. Both call the exact same Revit API - the same FilteredElementCollector, the same parameters, the same transactions - so nothing you learn here is wasted whichever you pick. Start with whichever your studio already uses; the four ideas transfer completely.

Same API everywhere. Only how you get doc (and open a transaction) changes by host.

You will always look things up - and that is normal

A quiet fear stops people using the Revit API: that you must memorise it. You do not, and no one does. The API is vast - thousands of classes and methods across dozens of namespaces - and even seasoned BIM programmers keep the documentation open constantly. Fluency here is not recall; it is knowing the shape of things well enough to find the specific method you need and read whether it does what you want.

The canonical reference is the Revit API documentation for your version, which lists every class, its methods and properties, and what each returns. Because the API changes between releases, the version matters - a method that exists in one year may be renamed or take different arguments in another, which is why an undated forum snippet sometimes fails. A widely used community resource, the RevitAPIDocs site, presents the same reference in a searchable form, and the Revit and Dynamo developer forums are where real problems get worked out. When you search, include the version and the class name; you are looking for the method signature - what it takes and what it gives back - not a finished script to paste.

There is also a fast way to explore the API from inside a running script: print what an object offers. Python's dir() lists the attributes and methods of any object, and type() tells you which class you are holding. Dropped into a Python node or pyRevit script, they turn abstract documentation into something concrete about the exact element in front of you:

python
wall = walls[0]
print(type(wall))          # the element's class
print([m for m in dir(wall) if not m.startswith("_")])  # its members

This is not cheating; it is the normal working method. You form a rough idea from the docs, confirm it against a real object with dir and print, and adjust. Modern AI coding assistants (covered in Module 10) accelerate the first step - describe what you want and they suggest the collector or method - but they hallucinate API calls that do not exist, especially for the version you are on, so you still verify against the docs and a live object. The literacy that makes all of this work is exactly what this module builds: understand elements, parameters, collectors and transactions, and the rest is a lookup away. Nobody holds the whole API in their head, and you do not need to either.

Nobody memorises the API. dir() and type() reveal any object; the docs confirm it; AI suggests but you verify.

Tools & terms you'll meet in this lesson

Element / ElementId

The objects in the model and their stable numeric ids

Almost everything is an Element; its ElementId never changes and is how you refer to it - and later match it to a spreadsheet row.

LookupParameter

Reads a named parameter off an element

Returns None if the parameter does not exist, so always check; then ask for the value with AsString/AsDouble and mind that lengths come back in feet.

FilteredElementCollector

The standard way to query elements from the model

Chain OfCategory and WhereElementIsNotElementType, then ToElements; it queries Revit's database efficiently rather than looping the whole model.

Transaction

An all-or-nothing bundle that any model change must sit inside

Start, edit, Commit; the API refuses changes outside one. Dynamo uses TransactionManager; pyRevit uses Transaction or a with-helper.

Hands-on workshop

Workshop - count and read your model

A read-only script is the safest first API contact: it cannot damage anything, needs no transaction, and proves your collector works. You will count a category and print one parameter per element.

Revit with Dynamo, or Revit with pyRevit installed. Any model with a few doors will do - a sample or template file is fine.

Given & goal
Goal: collect a category, count it, and print one parameter each
Inputs: any Revit model open in Dynamo or pyRevit
Time: ~30 minutes
  1. 1Get the document: DocumentManager.Instance.CurrentDBDocument in a Dynamo Python node, or __revit__.ActiveUIDocument.Document in a pyRevit script.
  2. 2Build a FilteredElementCollector on doc, filter with .OfCategory(BuiltInCategory.OST_Doors) and .WhereElementIsNotElementType(), and call .ToElements(). Print the count with len(...).
  3. 3Loop the doors and read each one's Mark parameter with LookupParameter, guarding against None. Collect the marks into a list and return or print it.
  4. 4Spot the gaps: build a second list of the doors whose Mark is empty or None. That is a missing-data audit - the seed of the next lesson.
  5. 5Do NOT wrap any of this in a transaction - you are only reading. Confirm it runs with no transaction, then note where a transaction WOULD be needed if you changed a value.

You’ll walk away with
A read-only script that counts the doors in a model, lists their Mark values, and flags any door with a missing Mark - all without a transaction, proving your collector and parameter reads work.

The worked example

Three altitudes on the same idea

Read the band that fits you — or all three.

For the architectAutomate busywork & build custom tools

The API turns the model into something you can audit and edit at scale. Instead of clicking through hundreds of elements, a collector gathers every wall or door in a line, a loop reads or checks a parameter, and one transaction writes a correction across the whole set. Model-checking - every door has a fire rating, every sheet a valid number - stops being a manual chore and becomes a script you run before each issue.

For the interior designerScripts for data, schedules & layouts

Your finishes, fixtures and furniture live as elements with parameters, and the API reaches all of them. Want every FF&E item's product code set from a rule, or a check that no room finish is blank? That is a collector over a category and a transaction that writes the values. You do not need to be a programmer - you need the four ideas here, and the model becomes a database you can tidy in seconds.

For the studentA hireable computational skill

The Revit API is where BIM job listings and computational-design coursework actually point. Understanding elements, parameters, collectors and transactions is the vocabulary of every Revit automation you will read or write. Learn it against a small practice model, and you can talk credibly about BIM automation in an interview - and the same four ideas underpin pyRevit tools and Dynamo graphs alike, so the knowledge compounds across the whole ecosystem.

Misconception check

The Revit API is only for professional software developers building plug-ins - it is out of reach for a designer.

Building a robust, distributed Revit add-in is genuinely a developer's job. But using the API for your own automation is far more approachable, because the day-to-day surface is small: find elements with a collector, read and write their parameters, and wrap changes in a transaction. Those four ideas cover the overwhelming majority of the scripts a designer actually needs, and you run them through friendly hosts - Dynamo's Python node or a pyRevit button - not a full Visual Studio project. The API is enormous, but you do not learn the whole thing; you learn the handful of moves your tasks require and look up the rest. A designer who understands elements, parameters, collectors and transactions can automate real work without ever calling themselves a software developer.
Try it

Do it yourself

Check the four ideas.

  1. 1What does LookupParameter return when the parameter name does not exist, and why must you check for it?
  2. 2Write the three collector steps that gather all the placed walls (not wall types).
  3. 3Why does reading a wall's height with AsDouble sometimes surprise you in a millimetre project?
  4. 4What happens if you try to set a parameter outside a transaction?
  5. 5Name one thing Dynamo and pyRevit each do better, given both call the same API.
Take this with you

The one line to carry out

The Revit model is objects you can program: find elements with a FilteredElementCollector, read and write their parameters, and wrap every change in a transaction. Those four ideas - element, parameter, collector, transaction - are the whole foundation; the rest is looking things up.
Take it further
References & further reading

Peer-reviewed journals & authoritative standards

  1. 01Autodesk RevitWikipedia, 2026.
  2. 02Application programming interfaceWikipedia, 2026.
  3. 03Building information modelingWikipedia, 2026.
  4. 04Dynamo (software)Wikipedia, 2026.
Related lessons
Recap
The Revit API exposes the model as elements, each with a stable ElementId and a set of named parameters you read with LookupParameter (checking for None, and minding that lengths come back in feet). The FilteredElementCollector queries exactly the elements you want by category and instance-versus-type. Any change to the model must sit inside a transaction - Dynamo manages it via TransactionManager, pyRevit via Transaction - and one transaction can carry a whole batch of edits.
Carry forward →

With the four ideas in hand, we can stop reading and start doing. Next: real automations - renaming views and sheets, setting parameters in bulk, placing tags and auditing for missing data - the drudgery BIM scripting exists to remove.

A

The author

Amogh N P

Architect, interior designer, and creative polymath. Studio Matrx began in his notebooks — his vision of design made honest, useful, and open to everyone. Its Academy is written and taught in his memory, and free, forever.

More about Amogh →