The Revit API gives structural engineers direct programmatic access to Autodesk Revit’s data model — letting you automate repetitive tasks, enforce office standards, extract analytical data, and build custom tools that no out-of-the-box workflow can match. If you can click it in Revit, you can script it. If you can script it, you can eliminate hours of weekly manual work.
This guide covers the complete picture: what the Revit API is, how its object model is structured, how to write your first working add-in in both C# and Python (pyRevit), practical automation scripts for structural workflows, and the engineering judgement questions you need to ask before trusting any automated output.
Whether you are evaluating whether the Revit API is worth learning, setting up your first development environment, or looking for production-ready script patterns for load extraction or schedule automation — this article is your starting point.
[toc_box]
- What Is the Revit API — and Why Should Structural Engineers Care?
- The Revit API Object Model Explained
- Setting Up Your Development Environment
- Your First Revit API Script: Hello, Structure!
- 5 Practical Structural Automation Scripts
- Transactions: The Most Critical API Concept
- C# vs Python (pyRevit): Which Should You Use?
- API Automation ≠ Engineering Judgement
- Resources & Next Steps
What Is the Revit API — and Why Should Structural Engineers Care?
The Revit API (Application Programming Interface) is a set of .NET classes and methods — exposed primarily through the RevitAPI.dll and RevitAPIUI.dll assemblies — that allows external programs and scripts to read from and write to a live Revit model. It is the same programmatic surface that Autodesk itself uses to build every tab and panel you see in the Revit ribbon.
For structural engineers specifically, the Revit API unlocks three high-value capabilities:
- Data extraction — pull every beam size, section property, material grade, and analytical node coordinate into a spreadsheet, JSON file, or directly into analysis software like ETABS or RAM.
- Model manipulation — rename elements to match your office numbering convention, batch-apply parameters, set analytical releases, or create views programmatically.
- Validation & QA — check that every structural column has a correct base fixity condition, that no beams are missing a material assignment, or that all footings carry a correct bearing capacity parameter — in seconds across a 10,000-element model.
The API is not a replacement for engineering judgement — it is a force multiplier. Once a structural engineer understands the object model, even a modest Python script can pay back its development cost in the first week of use. See also: AI in Structural Engineering: BIM Integration, Generative Design & ML Analysis for the broader automation landscape.
The Revit API Object Model Explained
Before writing a single line of code, you must understand how Revit organises its data internally. Every API call you will ever write navigates this hierarchy.
Document, Application & UIApplication
Every Revit API interaction begins with one of two root entry points:
Application— the Revit application itself. Provides version info, document management, and shared parameter file access.UIApplication— the application plus the active user interface. Required for external commands triggered by ribbon buttons; gives access toUIDocumentand the active view.Document— a single open Revit file (project or family). This is where structural elements live. Almost all structural automation targetsdoc.ActiveViewor iterates elements across the full model withFilteredElementCollector.
Element Hierarchy & FilteredElementCollector
FilteredElementCollector is the single most important class in the entire Revit API for structural engineers. It lets you efficiently query the model database without loading every element into memory.
// Collect all structural framing (beams, braces) in the document FilteredElementCollector collector = new FilteredElementCollector(doc) .OfCategory(BuiltInCategory.OST_StructuralFraming) .OfClass(typeof(FamilyInstance)); foreach (FamilyInstance beam in collector) { Parameter sectionSize = beam.LookupParameter("Structural Usage"); // process each beam element }
Key structural categories you will filter by most often:
| BuiltInCategory Constant | Structural Elements Included | Typical API Use |
|---|---|---|
OST_StructuralFraming | Beams, braces, purlins, joists | Section schedule, load path extraction |
OST_StructuralColumns | Vertical structural columns | Column mark renaming, storey assignment |
OST_StructuralFoundation | Isolated & strip footings, piles | Bearing capacity parameter population |
OST_Floors | Concrete slabs, composite decks | Thickness schedule, area computation |
OST_Walls | Shear walls, RC walls | Reinforcement tagging, area export |
OST_StructuralStiffener | Stiffeners, gusset plates | Connection check lists |
OST_AnalyticalNodes | Analytical model nodes | Coordinate export to ETABS/SAP2000 |
Parameters, Properties & Units
In the Revit API, every piece of data attached to an element is a Parameter. Parameters have four storage types you must handle correctly:
| StorageType | C# Read Method | Structural Example |
|---|---|---|
Double | param.AsDouble() | Beam length (internal units = feet) |
Integer | param.AsInteger() | Structural usage enum value |
String | param.AsString() | Mark, comments, element description |
ElementId | param.AsElementId() | Material reference, type assignment |
The Revit API returns all numeric values in internal units: lengths in decimal feet, areas in square feet, forces in kips (imperial). In Revit 2022+ use UnitUtils.ConvertFromInternalUnits(value, UnitTypeId.Millimeters). In older builds use UnitUtils.ConvertFromInternalUnits(value, DisplayUnitType.DUT_MILLIMETERS). Forgetting this is the single most common source of structural data extraction errors.
Setting Up Your Development Environment
Option A: C# Add-in with Visual Studio
- Install Visual Studio 2022 Community (free)
- Install Revit 2024 SDK from Autodesk Developer Network
- Create a new Class Library (.NET Framework 4.8) project
- Add references:
RevitAPI.dllandRevitAPIUI.dllfrom Revit install folder - Set both DLL references → Copy Local = False
- Implement
IExternalCommandorIExternalApplication - Create a
.addinmanifest file in%AppData%AutodeskRevitAddins2024 - Build → Launch Revit → command appears in Add-ins tab
<RevitAddIns> <AddIn Type="Command"> <Name>StructuralScheduleExport</Name> <Assembly>C:MyAddinsStructuralTools.dll</Assembly> <FullClassName>StructuralTools.ExportCommand</FullClassName> <ClientId>a1b2c3d4-e5f6-7890-abcd-ef1234567890</ClientId> <VendorId>YourCompany</VendorId> <VendorDescription>Structural Automation Tools</VendorDescription> </AddIn> </RevitAddIns>
Option B: pyRevit (Python, Zero Compile)
pyRevit is an open-source framework that lets you write IronPython or CPython scripts without compiling a DLL. For structural engineers who are more comfortable in Python than C#, this is often the fastest path to productivity. It installs a custom tab in the Revit ribbon and loads any .py script found in your extension folder automatically on Revit startup.
- Download and install pyRevit installer from GitHub releases
- Revit restarts — pyRevit tab appears in ribbon
- Create a folder:
MyExtension.extension/MyTools.tab/Structural.panel/ExportBeams.pushbutton/ - Place
script.pyinside the pushbutton folder - Use pyRevit Settings → Reload to update without restarting Revit
Always start a pyRevit script with from pyrevit import revit, DB, script. The revit module exposes revit.doc and revit.uidoc instantly — no boilerplate entry-point class required. Use script.get_output() to print rich HTML output tables directly into Revit’s output window.
Your First Revit API Script: Hello, Structure!
The simplest useful structural script counts and lists every structural framing element in the active document. This verifies your environment is working and teaches the core loop pattern you will reuse in every script you ever write.
from pyrevit import revit, DB, script doc = revit.doc output = script.get_output() # Collect all structural framing elements collector = DB.FilteredElementCollector(doc) .OfCategory(DB.BuiltInCategory.OST_StructuralFraming) .OfClass(DB.FamilyInstance) .ToElements() output.print_md("## Structural Framing Count: {}".format(len(collector))) output.print_table( [[e.Name, e.LookupParameter("Mark").AsString() if e.LookupParameter("Mark") else "—"] for e in collector], columns=["Family Name", "Mark"] )
using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Structure; using Autodesk.Revit.UI; using System.Text; [Transaction(TransactionMode.ReadOnly)] public class HelloStructure : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { Document doc = commandData.Application.ActiveUIDocument.Document; FilteredElementCollector col = new FilteredElementCollector(doc) .OfCategory(BuiltInCategory.OST_StructuralFraming) .OfClass(typeof(FamilyInstance)); var sb = new StringBuilder(); sb.AppendLine($"Structural framing count: {col.GetElementCount()}"); TaskDialog.Show("Hello Structure", sb.ToString()); return Result.Succeeded; } }
5 Practical Structural Automation Scripts
These are production patterns — not toy examples. Each solves a real workflow problem that costs engineering hours in practice.
1. Beam Schedule Export to CSV
Exporting a beam schedule from Revit manually takes minutes per iteration and is prone to copy-paste errors when moving data to Excel or analysis tools. This script extracts Mark, Family, Type, Length (mm), Level, and Material for every structural beam and writes a .csv file.
import csv, os from pyrevit import revit, DB, forms from Autodesk.Revit.DB import UnitUtils, UnitTypeId doc = revit.doc output_path = forms.save_file(file_ext='csv') if not output_path: raise SystemExit beams = DB.FilteredElementCollector(doc) .OfCategory(DB.BuiltInCategory.OST_StructuralFraming) .OfClass(DB.FamilyInstance).ToElements() def get_param(elem, name): p = elem.LookupParameter(name) return p.AsString() if p and p.StorageType == DB.StorageType.String else str(p.AsDouble()) if p and p.StorageType == DB.StorageType.Double else "—" with open(output_path, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['Mark', 'Family', 'Type', 'Length_mm', 'Level']) for b in beams: lp = b.LookupParameter('Length') length_mm = round(UnitUtils.ConvertFromInternalUnits( lp.AsDouble(), UnitTypeId.Millimeters), 1) if lp else '—' writer.writerow([ get_param(b, 'Mark'), b.Symbol.Family.Name, b.Symbol.Name, length_mm, doc.GetElement(b.LevelId).Name if b.LevelId.IntegerValue > 0 else '—' ]) print("Exported {} beams to {}".format(len(beams), output_path))
2. Structural Load Extraction (Point & Distributed)
Revit structural loads — defined in the OST_PointLoads, OST_LineLoads, and OST_AreaLoads categories — are regularly needed for hand-check verification or import into standalone analysis tools. The API exposes load magnitude, direction, and host element for every load object.
var loads = new FilteredElementCollector(doc) .OfCategory(BuiltInCategory.OST_PointLoads) .OfClass(typeof(PointLoad)) .Cast<PointLoad>(); foreach (var load in loads) { // Forces in kN (convert from internal kips) double Fx = UnitUtils.ConvertFromInternalUnits(load.ForceX, UnitTypeId.Kilonewtons); double Fy = UnitUtils.ConvertFromInternalUnits(load.ForceY, UnitTypeId.Kilonewtons); double Fz = UnitUtils.ConvertFromInternalUnits(load.ForceZ, UnitTypeId.Kilonewtons); // Moments in kN·m double Mx = UnitUtils.ConvertFromInternalUnits(load.MomentX, UnitTypeId.KilonewtonMeters); Console.WriteLine($"Load ID {load.Id}: Fz={Fz:F2} kN | Mx={Mx:F2} kNm"); }
3. Automated Element Renaming & Mark Assignment
Large models often have inconsistent Mark values — a mix of blank marks, duplicated IDs, and free-text entries that break schedules and sheet annotations. This script assigns sequential marks following a pattern like B-001, B-002 for beams, C-001 for columns.
from pyrevit import revit, DB doc = revit.doc CATEGORIES = { 'B': DB.BuiltInCategory.OST_StructuralFraming, 'C': DB.BuiltInCategory.OST_StructuralColumns, 'F': DB.BuiltInCategory.OST_StructuralFoundation, } with DB.Transaction(doc, "Assign Sequential Marks") as t: t.Start() for prefix, cat in CATEGORIES.items(): elems = DB.FilteredElementCollector(doc) .OfCategory(cat).WhereElementIsNotElementType().ToElements() for i, elem in enumerate(elems, start=1): mark_param = elem.LookupParameter('Mark') if mark_param and not mark_param.IsReadOnly: mark_param.Set("{}–{:03d}".format(prefix, i)) t.Commit() print("Marks assigned successfully.")
4. Section Property Checker — Verify Against Design Requirements
A section property checker reads bf (flange width), d (depth), and tf / tw values from the structural framing type properties and flags any beam whose depth-to-flange-width ratio exceeds a prescribed limit — a common preliminary check before running a full AISC or AS 4100 member capacity verification.
from pyrevit import revit, DB, script import math doc = revit.doc output = script.get_output() Fy_MPa = 250 # Grade 250 steel E_MPa = 200000 lambda_pf = 0.38 * math.sqrt(E_MPa / Fy_MPa) # ≈ 10.75 beams = DB.FilteredElementCollector(doc) .OfCategory(DB.BuiltInCategory.OST_StructuralFraming) .OfClass(DB.FamilyInstance).ToElements() flags = [] for b in beams: sym = b.Symbol bf = sym.LookupParameter('bf') tf = sym.LookupParameter('tf') if bf and tf: bf_mm = DB.UnitUtils.ConvertFromInternalUnits(bf.AsDouble(), DB.UnitTypeId.Millimeters) tf_mm = DB.UnitUtils.ConvertFromInternalUnits(tf.AsDouble(), DB.UnitTypeId.Millimeters) lam = bf_mm / (2 * tf_mm) if tf_mm > 0 else 999 if lam > lambda_pf: flags.append([b.LookupParameter('Mark').AsString(), sym.Name, round(lam,2), round(lambda_pf,2)]) output.print_md("## ⚠ Flange Slenderness Failures: {}".format(len(flags))) output.print_table(flags, columns=['Mark','Section','λf','λpf_limit'])
5. Clash Detection Export
While Navisworks handles full clash detection, a lightweight API script can identify structural-to-MEP bounding box overlaps directly in Revit by comparing element bounding boxes — useful for early coordination reviews before a Navisworks federated model is assembled.
Use BoundingBoxIntersectsFilter with an Outline derived from each structural element’s bounding box to find overlapping MEP elements. This is far faster than nested Python loops comparing geometries. On a 500-element model, it completes in under 2 seconds vs. 40+ seconds for a brute-force approach.
Transactions: The Most Critical API Concept
Any API call that modifies the Revit model — setting a parameter, creating an element, deleting geometry — must occur inside an open Transaction. Forgetting this is the most common reason beginner scripts crash or corrupt models.
using (var tx = new Transaction(doc, "Set Parameter")) { tx.Start(); try { // All model modifications happen here elem.LookupParameter("Mark").Set("B-001"); tx.Commit(); } catch (Exception ex) { tx.RollBack(); TaskDialog.Show("Error", ex.Message); } }
Revit does not support nested transactions. Use SubTransaction if you need partial rollback within a single transaction group, or TransactionGroup to wrap multiple transactions that should appear as one undo step in the Revit undo stack. Attempting to open a Transaction inside an already-open one throws a fatal InvalidOperationException.
C# vs Python (pyRevit): Which Should You Use?
| Criterion | C# Add-in | pyRevit (Python) |
|---|---|---|
| Setup time | ~2–4 hours first time | ~20 minutes |
| Iteration speed | Compile → restart Revit | Edit → Reload (no restart) |
| Performance | ~5–10× faster on large models | Adequate for most tasks |
| Distribution | Single compiled DLL | Requires pyRevit installed |
| API surface access | 100% — all .NET APIs | ~95% — some async APIs limited |
| Learning curve (for engineers) | High — need .NET/OOP knowledge | Low — Python familiar to most |
| Best for | Production tools, complex UI, deployment | Rapid automation, one-off scripts, QA checks |
Recommendation for most structural engineers: Start with pyRevit for exploratory automation. Once a script proves its value and needs to run reliably across the office without pyRevit as a dependency, port it to a C# add-in. The Revit API is identical — you are only changing the language wrapper.
API Automation ≠ Engineering Judgement
This deserves its own section because it is a professional responsibility issue, not just a technical one.
Before deploying any structural automation script to a production workflow, verify:
- Analytical model consistency settings — are analytical nodes attached to physical geometry?
- Unit conversion correctness — has every numeric output been verified against a manual spot-check?
- Scope of the FilteredElementCollector — does it include linked models when it should not?
- Transaction scope — could a partial failure leave the model in a corrupted intermediate state?
- Version locking — is the script tested against the specific Revit version in production?
- Peer review — has another engineer independently validated the script output on a known test model?
For a wider look at where automation intersects with engineering decision-making, see our article on AI in Construction: The Complete Engineering Guide.
Resources & Next Steps
| Resource | Type | Best For |
|---|---|---|
| RevitApiDocs.com | Online reference | Searching any class, method, or property |
| The Building Coder (Jeremy Tammik) | Blog — 3,000+ articles | Deep dives, edge cases, API gotchas |
| pyRevit GitHub | Open-source framework | Python scripting environment |
| Autodesk Developer Network | Official SDK + samples | SDK downloads, official tutorials |
| Dynamo Forum | Community forum | Visual scripting questions, Python nodes |
| Top Civil Engineering GitHub Repos | Internal resource | Open-source tools for civil engineers |
Structural Engineering Services — M. Haseeb
Graduate structural engineer offering BIM modelling, Revit structural documentation, and structural design services. Available for international project collaboration.
View Portfolio → LinkedInThe Revit API is one of the highest-leverage skills a structural engineer can develop in 2024 and beyond. A single well-written script can eliminate hours of weekly manual work, reduce transcription errors between BIM and analysis models, and enable QA checks that are simply impractical to run by hand. The learning curve is real — but the return on investment, measured in recovered engineering hours and reduced coordination errors, is among the highest in the profession.
Start with pyRevit, write your first beam schedule export this week, and iterate from there. Related reading: AI in Structural Engineering | Beam Design: RC, Steel & Timber | Open-Source Civil Engineering on GitHub.
