Macros in FastSheet are Python. Copy this page into ChatGPT or Claude, say what you want, and paste the answer into Tools ▸ Macros. In the app, Tools ▸ AI ▸ Copy Macro Guide does the same with your sheet's headers included, and Tools ▸ AI ▸ Ask AI writes the macro for you.
FastSheet macro guide (Tools > Macros). Macros are Python (fastpy): the
standard library works; math, statistics, datetime, re, json and random are
already imported.
CELLS AND RANGES
Cell("A1") one cell Cells(row, col) 1-based, Cells(1, 1) is A1
Range("A1:C10") a block Range("A1", "C10") the same block
Range("2:13"), Range("D:D") whole rows / columns (the used part)
ActiveCell, Selection the focused cell and the selected range when the macro starts
Worksheets("Data").Cell("A1"), Worksheets("Data").Range("A1:B5") another sheet
TABLES BY HEADER NAME (row 1 holds the headers) - the easy way to read data
for row in Rows(): ... each data row as a dict: row["Region"], row["Units"]
row["Units"] = 5 writes the cell
row["Size"] = "Big" a new header adds a new column at the right
row.cell("Units") its Cell, to format: .fill("#ffff00")
row.delete() deletes the row (when the macro ends)
row["_row"] its row number
Column("Units") the cells under that header, a Range: .sum() .values() ...
for c in Column("Units"): if c.value != "" and c.value > 20: c.fill("#ffff00")
CELL PROPERTIES (read and write)
.value the value (number or text). Setting it stores a constant.
.formula the formula text, e.g. cell.formula = "=SUM(A1:A10)". Excel syntax.
.row .column (1-based) .address ("B7") .offset(dr, dc) -> the cell dr rows down, dc right
.Value / .Formula VBA-style aliases
FORMATTING (each returns the cell or range, so calls chain)
.bold() .italic() .underline() .fill("#e8f5e9") .color("#c00000") .align("center")
.border() .number("cur" | "pct" | "num") .note("text") .hyperlink("https://...")
.clear() (value and format) .clear_contents()
RANGES
for c in Range("A2:A20"): ... iterate the cells, row by row
.value = v put one value in every cell (or a list, cell by cell)
.formula = "=D2*E2" fill a block like Excel: F3 gets =D3*E3, F4 =D4*E4 ...
.values() the values as a list .sum() .avg() .min() .max() .count()
.sort("Units", descending=True) sort the rows by a column (header, letter or 1, 2 ...)
.find(v) the first cell holding v, or None
.copy("Sheet2!A1") copy the block (values, formulas, formats) to a cell, here or on another sheet
.delete() delete the block's whole rows (when the macro ends; the rows below move up)
Reads see what the macro wrote before. Empty cells read as "".
A date cell reads as a Python date (datetime.date): d.strftime("%A"), d.year, d + datetime.timedelta(days=7).
Writing a date (or "2026-01-02") back keeps it a date; date functions also read such cells: =WEEKDAY(A2).
OUTPUT AND CHARTS
log(...) / print(...) write to the macro output box
Plot("A2:A50", "B2:B50, C2:C50") X-Y chart; PlotTitle("..."), PlotXLabel("..."), PlotYLabel("...")
PlotFx("sin(x)/x", -10, 10) plot a function PlotPie("A1:A5") labels left, values in col B
PlotSurface("A1:F20") 3D surface Plot3D("A1:C50") 3D scatter (x, y, z columns)
RULES
- Formulas use Excel syntax ("=A1*2", "=SUM(B2:B9)", "=IF(C2>0,"yes","no")"); 400+ Excel
functions work, plus AI("prompt", cells...) when AI is on.
- Inside a .formula only Excel works: cell ranges (E2:E13) and Excel functions.
Column(), Range(), Rows(), row[...] and Python are macro names, not Excel.
- Do not wrap a formula in IFERROR to hide a mistake: FastSheet checks each formula.
- A new column: write its header in row 1 first, then the data rows.
- A percent is a fraction: '=D2/SUM($D$2:$D$13)', then .number("pct") on those cells.
Never multiply by 100. A total is a SUM of the data rows, not the last row.
- Rows copied to another sheet go one under another: keep a counter for the next free row.
- The macro runs, then FastSheet recalculates and repaints once.
- Macros script the sheet only: no windows, files dialogs or app UI.
EXAMPLE: totals by region in H1:I5, bold header
totals = {}
for row in Rows():
totals[row["Region"]] = totals.get(row["Region"], 0) + row["Units"]
Range("H1").value = "Region"; Range("I1").value = "Units"
Range("H1:I1").bold().fill("#e8eef7")
r = 2
for region, total in sorted(totals.items()):
Cells(r, 8).value = region; Cells(r, 9).value = total; r += 1
log("wrote", len(totals), "regions")
EXAMPLE: a formula column (live: it follows the data), numbered rows, a copy to Sheet2
Range("F1").value = "Size"
Range("F2:F13").formula = '=IF(D2>=20,"Big","Small")'
Column("No").value = range(1, 13)
Range("A1:E1").copy("Sheet2!A1")
n = 2
for row in Rows():
if row["Region"] == "West":
row.copy("Sheet2!A" + str(n)); n += 1
EXAMPLE: a Revenue column, the rows sorted by it, a chart
Range("F1").value = "Revenue"
Range("F2:F13").formula = "=D2*E2"
Range("A1:F13").sort("Revenue", descending=True)
Plot("E2:E13", "D2:D13"); PlotTitle("Units against Price")