Compliance scripting against the Revit API is the practice of writing programs that read a building information model, evaluate it against regulatory or internal standards, and report violations without a human clicking through every wall and door. The Revit API exposes nearly every element in a project database — walls, doors, rooms, stairs, railings, spaces, and their parameters — through the .NET framework, which means compliance logic can be expressed as C# or Python (via pyRevit or Dynamo) code that runs inside Revit itself. This article walks through what such scripts look like, why they matter, how to build them step by step, where they fall short, and how they compare with alternative approaches.
What Compliance Scripting Actually Means in the Revit API Context
Also worth reading: What does the EU AI Act mean for BIM workflows, and is there a practical compliance checklist for architecture firms? · What are the best BIM to code conversion tools in 2026 for automated compliance checking? · How will AI building code compliance change by 2027?
The Revit API is a managed .NET library (RevitAPI.dll and RevitAPIUI.dll) that Autodesk ships with every installation of Revit since roughly version 2010. A compliance script is any external application, add-in, macro, or Dynamo graph that opens a transaction-safe connection to the active document, iterates over elements using a FilteredElementCollector, reads geometry and parameter data, and evaluates rules. The key distinction from manual checking is determinism: a script applies the same threshold to every element every time, whereas a human reviewer's attention degrades over a 400-sheet set.
A typical rule might be "every door leaf in an accessible route must have a clear opening width of at least 815 mm" or "corridor width in an office occupancy must exceed 1120 mm." The script does not interpret the law; it encodes one specific numeric requirement drawn from a code section, then flags elements that fail. This framing matters because many teams overestimate what automation can do — a script cannot decide whether a room needs two exits, but it can verify that the two exits you drew are each at least the required width apart from each other along the wall they serve.
Why Teams Move From Manual Review to Scripted Checks
Manual code review of a mid-size commercial project typically involves several hundred doors, thousands of walls, dozens of stair assemblies, and egress paths that change weekly during design development. Studies of QA workflows in AEC firms consistently show that repetitive geometric checks consume a disproportionate share of senior staff time while catching fewer errors per hour than automated checks catch per minute. A script that sweeps all doors for clear-width compliance runs in seconds on a model with 2,000 doors; the same sweep by hand takes days and produces inconsistent results between reviewers.
There is also a timing argument. Manual review usually happens late, near permit submission, when fixing a violation means re-coordinating structure, mechanical systems, and possibly the facade. Scripted checks can run continuously — on model open, on demand, or triggered by a sync — so a door placed too close to a wall corner gets flagged the day it is drawn rather than three weeks before deadline. Firms that adopt this workflow generally report moving code issues left in the schedule, which reduces rework cost substantially because early-stage changes touch far fewer downstream documents.
Core Building Blocks: Collectors, Parameters, and Transactions
Every compliance script rests on three API constructs. First, the FilteredElementCollector retrieves candidate elements: new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Doors).WhereElementIsNotElementType() returns all door instances in the document. Second, parameter access pulls the values you need — instance parameters like INSTANCE_WIDTH_PARAM for doors, or computed values such as a room's area via its spatial element. Third, geometry extraction uses Options and GeometryElement objects when a rule depends on actual shape rather than stored parameters, for example measuring the true clear width of a corridor by ray-casting between wall faces instead of trusting a type parameter.
Transactions deserve special mention. Reading a model requires no transaction, but if your script wants to annotate failures — placing color fills, tagging non-compliant doors, or writing a shared parameter like CodeCheck_Status — it must wrap those writes in a Transaction object and commit it. A common pattern is a read-only evaluation pass followed by a single transaction that applies all annotations at once, which keeps the undo stack clean and avoids hundreds of micro-transactions that bloat file size and slow synchronization in workshared models.
Example 1: Door Clear-Width Compliance Check
Here is the canonical first script most teams write. In C#, after collecting all door family instances, iterate and read the effective width: for single doors use the Width instance parameter; for double doors sum both leaves or read the clear-opening parameter if the family provides one. Compare against a threshold constant — 815 mm (32 inches) for accessible routes under ICC A117.1-style requirements, or whatever figure your jurisdiction specifies. Record failures into a list of tuples containing element ID, level, room name, measured value, and required value.
The output stage matters more than beginners expect. Writing results to a TaskDialog is fine for testing, but production scripts should export a CSV or Excel sheet with hyperlinks back to element IDs, because reviewers need to locate and fix each failure. Some teams go further and apply a red override graphic display setting to failing doors plus a view filter named CODE_FAIL_DOORS, so anyone opening the model sees violations immediately. A complete version of this script is typically 80–150 lines of C# and runs in under five seconds on models with several thousand doors.
Example 2: Egress Path and Travel Distance Evaluation
Travel distance checks are harder because they require pathfinding, not just parameter reads. The practical approach is to build a graph of rooms and doors: collect all rooms (spatial elements), find door instances whose FromRoom and ToRoom properties link them, construct an adjacency graph, then run a shortest-path algorithm (Dijkstra or BFS, both trivially implementable in .NET) from every occupied room to the nearest exit-marked door. Sum segment lengths using room center-to-door-center distances refined by wall-face offsets, and compare totals against limits such as 60 meters (200 feet) for typical sprinklered business occupancies, adjusting for dead-end corridors and common-path-of-travel rules.
This example illustrates both the power and the ceiling of scripting. The graph approach catches gross errors — a suite whose only door leads to a storage room with no second exit — reliably and cheaply. But it approximates geometry: centerline paths overstate travel distance in large open plans and understate it around obstructions. Mature implementations refine the path with grid-based walkable-area sampling inside rooms, which increases runtime from seconds to tens of seconds but produces defensible numbers. Teams should document which approximation their tool uses, because a reviewer who discovers optimistic pathing will distrust every other check the tool performs.
Example 3: Stair, Railing, and Guard Geometry Validation
Stairs expose rich instance data through the API: riser height, tread depth, actual run, number of risers, and the stair's boundary curves. A compliance script collects all stair instances, reads these parameters, and tests them against thresholds — commonly a maximum riser height near 178–190 mm (7–7.5 inches), minimum tread depth around 254–280 mm depending on the code edition, and riser-to-tread consistency within about 9.5 mm across a flight. Because Revit enforces uniform risers within a stair by default, the more valuable checks involve railings and guards: verifying guard height meets the 1067 mm (42 inch) commercial standard, confirming railing continuity along open sides, and detecting landings missing where a run exceeds 12 risers.
Railing checks require geometry analysis because railing length and coverage are not always exposed as clean parameters. A robust script samples the stair stringer curve, projects guard locations onto it, and looks for gaps longer than a small tolerance (say 50 mm). This is genuinely difficult code — curve projection, tolerance handling, and multistory stair stacking all introduce edge cases — and it is where many in-house tools stall. It is honest to say that stair and railing validation is the point where firms often decide whether to keep investing in custom scripting or to buy a purpose-built checking product.
Comparing Your Options: Custom Scripts, Dynamo, pyRevit, and Commercial Tools
| Feature | Hand-written C# add-in | Dynamo / pyRevit graphs | Commercial checking platform |
|---|---|---|---|
| Setup cost | High (weeks to months of dev time) | Low (days) | Low (subscription setup) |
| Ongoing maintenance | You own every bug | Moderate; nodes break across Revit versions | Vendor handles updates |
| Rule flexibility | Unlimited | Good for simple rules | Limited to vendor's rule library |
| Code citation traceability | Manual | Manual | Often built-in |
| Best fit | Large firms with dev teams | Small teams, prototyping | Firms wanting audit-ready reports |
| Typical annual cost | $0 licensing, high labor | Free to ~$500/seat | Roughly $1,000–$5,000 per seat |
Common Mistakes That Undermine Compliance Scripts
The most frequent error is trusting type parameters over as-built geometry. A door family may declare 900 mm width while the placed instance was modified, or a wall's function parameter says "Fire Rating 2HR" while the actual assembly fails — scripts that read labels instead of verified values produce false confidence. Always measure geometry where feasible and treat parameters as hints requiring spot verification. The second mistake is ignoring units: the Revit API returns internal units (feet for length), and forgetting to convert has produced countless scripts that flag every door in a metric office as compliant because 0.9 feet looked smaller than 32. Convert explicitly with UnitUtils on every read.
Third, scripts that write results directly into the model without a disciplined annotation scheme create mess — hundreds of stray text notes and color schemes that reviewers then have to clean up. Prefer external reporting (CSV, dashboard, PDF) as the primary output and keep in-model graphics minimal and on dedicated views. Fourth, hardcoding code values in source files means every jurisdictional amendment requires a developer. Store thresholds in a versioned configuration file (JSON works well) so a technically minded code consultant can update values without touching compiled binaries. Finally, never let a script be the sole check: automation catches what it was written to catch, and novel design conditions slip past any rule set. Treat scripted output as a screening layer that prioritizes human judgment, not a replacement for it.
When to Introduce Scripted Checks Into Your Workflow
Timing follows the design phase. During schematic design, only coarse checks make sense — program area targets, occupancy load estimates, exit counts per floor — because geometry is too fluid for dimensional rules. Design development is the sweet spot for door widths, corridor dimensions, and stair proportion checks, when layouts are stable enough to test but changes remain cheap. Construction documents call for the heaviest automation: full egress path audits, fire-rating continuity between rated rooms, fixture counts against occupant loads, and dimension-string verification before issuing for permit. Running the full battery weekly during DD and CD phases, with results tracked over time, shows whether violations are being resolved or merely accumulating.
Cost-wise, the investment case is straightforward for firms above roughly ten architects producing permit sets regularly. An internal tool costing 200–400 developer hours (at $100–$180 per hour, so $20,000–$70,000) pays back within one or two projects if it prevents even a handful of late-stage redesigns, given that a single egress-related revision during CDs routinely costs $10,000–$50,000 in coordination effort. Smaller firms or those with irregular permit work should start with free pyRevit community scripts or trial periods of commercial checkers before committing to custom development. Whatever the path, budget for governance: someone must own rule definitions, record which code edition each threshold comes from, and retire checks that no longer match current regulations — an unowned rule library decays into liability within a couple of years as codes amend.
Where Automated Drawing Conversion Fits Alongside Compliance Scripting
A related frontier is converting legacy 2D drawings into Revit models automatically, then running compliance scripts on the result. Platforms in this space parse PDF or CAD linework, infer walls, doors, and rooms, and generate a BIM model that downstream rule engines can evaluate. The pairing is powerful for renovation and adaptive-reuse work, where existing-conditions drawings exist but no model does: convert the scanned set, run egress and accessibility checks against the inferred geometry, and get a gap list before design begins. Accuracy caveats apply — inference from raster scans still misclassifies perhaps 5–15% of elements depending on drawing quality, so converted models need human verification before their compliance results are trusted for anything beyond preliminary screening. Used with that caveat, conversion-plus-checking compresses weeks of existing-conditions modeling and survey into days, and it represents the direction the whole category is heading: compliance evaluated continuously on living models rather than once, manually, at submission.