Import Excel into Revit

install bim-cli · windows · per-user · no admin
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command 'iwr -useb https://bimcli.com/install.ps1 -OutFile $env:TEMP\bim-install.ps1; powershell -NoProfile -ExecutionPolicy Bypass -File $env:TEMP\bim-install.ps1'

Most teams do this by opening an add-in, pointing it at a spreadsheet, and clicking through a mapping wizard. Or building a Dynamo graph. With bim-cli it is a short script: read the spreadsheet, hand the rows to Revit.

Read an Excel file and update parameters across any number of elements in a Revit model -- no add-in UI, no Dynamo graph, no copy-paste.

The pipeline

read Excel, write parameters to Revit (PowerShell)
$rows = @(bim excel read params.xlsx | ConvertFrom-Json)
@{ rows = $rows } | ConvertTo-Json -Depth 4 -Compress | Set-Content rows.json -Encoding utf8
bim revit exec --file set-by-mark.cs --args-json "@rows.json"

bim excel read streams rows from the spreadsheet as NDJSON. The next line wraps them as { "rows": [...] } in rows.json, and bim revit exec runs set-by-mark.cs inside one Revit transaction with those rows as args. The script matches each row to an element by its Mark value and writes the remaining columns as parameters. Column headers must match Revit parameter names.

set-by-mark.cs
// set-by-mark.cs: args.rows is a list of { "Mark": "D-01", "<parameter name>": "<value>", ... }
var byMark = new FilteredElementCollector(doc)
    .WhereElementIsNotElementType()
    .Where(e => !string.IsNullOrEmpty(e.get_Parameter(BuiltInParameter.ALL_MODEL_MARK)?.AsString()))
    .ToLookup(e => e.get_Parameter(BuiltInParameter.ALL_MODEL_MARK).AsString());
int updated = 0, skipped = 0;
var notFound = new List<string>();
foreach (var row in argList("rows").OfType<IDictionary<string, object>>())
{
    var mark = row.TryGetValue("Mark", out var m) ? m?.ToString() : null;
    if (mark == null || !byMark.Contains(mark)) { notFound.Add(mark ?? ""); continue; }
    foreach (var el in byMark[mark])
        foreach (var kv in row.Where(c => c.Key != "Mark"))
        {
            var p = el.LookupParameter(kv.Key);
            var value = kv.Value?.ToString() ?? "";
            var ok = p != null && !p.IsReadOnly &&
                (p.StorageType == StorageType.String ? p.Set(value) : p.SetValueString(value));
            if (ok) updated++; else skipped++;
        }
}
return new { updated, skipped, notFound };

Add --rollback to the bim revit exec line to run the whole write and then undo it: the counts come back and the model is left unchanged.

Example: update door hardware specifications

Your door schedule lives in Excel. Column headers match Revit parameter names:

Mark Hardware Set Fire Rating Comments
D-01 HW-3 90 min Confirmed
D-02 HW-1 60 min Pending
D-03 HW-3 90 min Confirmed

The same three lines write all rows back to the model:

write hardware specs from Excel into Revit (PowerShell)
$rows = @(bim excel read door-hardware.xlsx | ConvertFrom-Json)
@{ rows = $rows } | ConvertTo-Json -Depth 4 -Compress | Set-Content rows.json -Encoding utf8
bim revit exec --file set-by-mark.cs --args-json "@rows.json"

The script returns how many parameter writes it made (updated), how many it could not make (skipped), and any Marks it did not find (notFound). Each column becomes a parameter write on the matched element. If a column header doesn't match a writable parameter, that write is counted in skipped and the rest continue.

Round-trip workflow

Export the current schedule, edit in Excel, import back:

export door schedule to Excel (PowerShell)
(bim revit schedule-read --name "Door Schedule" | ConvertFrom-Json).rows | ConvertTo-Json | bim excel write doors.xlsx --data -

edit doors.xlsx in Excel

import updated values back into Revit (PowerShell)
$rows = @(bim excel read doors.xlsx | ConvertFrom-Json)
@{ rows = $rows } | ConvertTo-Json -Depth 4 -Compress | Set-Content rows.json -Encoding utf8
bim revit exec --file set-by-mark.cs --args-json "@rows.json"

The Mark column that identifies each row on export is the same key used to match elements on import. No mapping wizard, no extra config step. To export a schedule first, see export Revit schedule to Excel.

Agent use

An AI coding tool can run the full round-trip without you touching the terminal:

paste into your AI coding tool
Update Revit element parameters from an Excel file using bim-cli.

Step 1 -- install bim-cli if not already installed: powershell.exe -NoProfile -ExecutionPolicy Bypass -Command 'iwr -useb https://bimcli.com/install.ps1 -OutFile $env:TEMP\bim-install.ps1; powershell -NoProfile -ExecutionPolicy Bypass -File $env:TEMP\bim-install.ps1'

Step 2 -- refresh PATH: pwsh: $env:Path += ";$env:LOCALAPPDATA\bim-cli"

Step 3 -- open Revit with the model you want to update.

Step 4 -- read the Excel file: bim excel read params.xlsx

Step 5 -- write the values with bim revit exec. Save the rows as {"rows": [...]} in rows.json, write a short C# script (set-by-mark.cs, see https://bimcli.com/import-excel-revit/) that matches each row to an element by Mark and sets the other columns as parameters, then run: bim revit exec --file set-by-mark.cs --args-json "@rows.json" --rollback Check the counts, then run it again without --rollback to keep the changes.

The Excel file must have a "Mark" column matching element Marks in the model. All other columns are written as Revit parameters by their header name. Columns that don't match a writable parameter are counted as skipped, not errors.

Install

iwr -useb https://bimcli.com/install.ps1 -OutFile $env:TEMP\bim-install.ps1; powershell -NoProfile -ExecutionPolicy Bypass -File $env:TEMP\bim-install.ps1

Works on Revit 2024 through 2027. No admin rights. No add-in install wizard. The first bim revit command places the add-in in %APPDATA%\Autodesk\Revit\Addins\ and nothing leaves your machine.

install bim-cli · windows · per-user · no admin
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command 'iwr -useb https://bimcli.com/install.ps1 -OutFile $env:TEMP\bim-install.ps1; powershell -NoProfile -ExecutionPolicy Bypass -File $env:TEMP\bim-install.ps1'

Then ask your assistant to run bim describe --json. See what the installer does.