The Spreadsheet Assembly Line Nobody Wants
Every operations team has at least one person whose unofficial job title is “the one who builds the spreadsheets.” Financial close rolls around, and someone spends two days copying numbers from the system into Excel, formatting columns, adding SUM formulas, making sure the currency symbols are right, and emailing the finished file to five people who each want a slightly different layout.
The numbers themselves are already in a database, an ERP, or a JSON API response. The hard part is not the data. The hard part is turning that data into a spreadsheet that looks like a human made it — with bold headers, right-aligned currency columns, European number formatting for the Berlin office, and a summary row at the bottom that actually computes the totals.
This is the kind of work that feels like it should take five minutes and consistently takes five hours. Not because the task is intellectually hard, but because the tooling makes simple things tedious.
If you’ve tried to automate this, you’ve met the library landscape. ExcelJS in JavaScript. openpyxl in Python. Apache POI in Java. caxlsx in Ruby. Each one requires you to reason about cell references, style objects, merge ranges, and format strings. A simple four-column report with headers and currency formatting takes 40-60 lines of boilerplate before you get to the actual data.
And that’s for one format. If the accounting team wants XLSX but the data warehouse needs CSV and the Slack bot should post a Markdown table, you maintain three code paths that all do the same conceptual thing in completely different ways.
What If the Spreadsheet Was Just Data
The core insight is simple: a spreadsheet is structured data with formatting metadata. Columns have names and widths. Rows have cells. Cells have values and optional styles — bold, currency, percentage, date format, alignment. If you describe this structure as JSON, a service can produce the formatted file.
That’s what the Sheet Generation API does. You send a JSON payload describing your sheets, columns, rows, and styles. You get back a formatted XLSX, CSV, or Markdown file. Same payload, three formats — change one field.
No library installation. No cell reference math. No style object boilerplate. The spreadsheet is defined by its data, not by the mechanics of writing it to a file.
Your First Sheet in Four Lines
Here’s the simplest possible request — a two-column report with three rows:
curl -X POST https://api.iterationlayer.com/sheet-generation/v1/generate \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"format": "xlsx",
"sheets": [
{
"name": "Revenue",
"columns": [
{
"name": "Month"
},
{
"name": "Revenue"
}
],
"rows": [
["January", 48200],
["February", 51800],
["March", 55300]
]
}
]
}'import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: process.env.API_KEY });
const result = await client.generateSheet({
format: "xlsx",
sheets: [
{
name: "Revenue",
columns: [
{ name: "Month" },
{ name: "Revenue" }
],
rows: [
["January", 48200],
["February", 51800],
["March", 55300]
]
}
]
});from iterationlayer import IterationLayer
client = IterationLayer(api_key=os.environ["API_KEY"])
result = client.generate_sheet(
format="xlsx",
sheets=[
{
"name": "Revenue",
"columns": [
{"name": "Month"},
{"name": "Revenue"}
],
"rows": [
["January", 48200],
["February", 51800],
["March", 55300]
]
}
]
)client := iterationlayer.NewClient(os.Getenv("API_KEY"))
result, err := client.GenerateSheet(iterationlayer.GenerateSheetRequest{
Format: "xlsx",
Sheets: []iterationlayer.Sheet{
{
Name: "Revenue",
Columns: []iterationlayer.SheetColumn{
{Name: "Month"},
{Name: "Revenue"},
},
Rows: [][]iterationlayer.SheetCell{
{{Value: "January"}, {Value: 48200}},
{{Value: "February"}, {Value: 51800}},
{{Value: "March"}, {Value: 55300}},
},
},
},
})
The response contains a base64-encoded buffer and a MIME type. Decode the buffer to get your XLSX file. Change "format": "xlsx" to "format": "csv" or "format": "markdown" and the same data comes back in a different format.
Notice what’s missing: no cell reference calculations, no style object initialization, no workbook/worksheet creation boilerplate. The data describes what you want. The API handles the how.
Cell Formatting: Making Numbers Look Right
Raw numbers in a spreadsheet are useless without context. Is 48200 dollars, euros, or units sold? Is 0.156 a percentage or a ratio? Formatting answers these questions, and it’s where spreadsheet libraries demand the most boilerplate.
The Sheet Generation API handles formatting at the cell level. Each cell can specify a format that tells the API how to interpret and display the value.
Currency Formatting
Currency is the most common formatting headache. You need the right symbol, the right number of decimal places, and the right thousands separator — which varies by locale.
curl -X POST https://api.iterationlayer.com/sheet-generation/v1/generate \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"format": "xlsx",
"sheets": [
{
"name": "Q1 Revenue",
"columns": [
{
"name": "Region"
},
{
"name": "Revenue (EUR)",
"width": 20
},
{
"name": "Revenue (USD)",
"width": 20
}
],
"rows": [
[
"DACH",
{
"value": 142500.50,
"format": "currency",
"currency_code": "EUR",
"number_style": "period_comma"
},
{
"value": 155325.55,
"format": "currency",
"currency_code": "USD",
"number_style": "comma_period"
}
],
[
"Nordics",
{
"value": 98300.00,
"format": "currency",
"currency_code": "EUR",
"number_style": "period_comma"
},
{
"value": 107243.00,
"format": "currency",
"currency_code": "USD",
"number_style": "comma_period"
}
]
]
}
]
}'import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: process.env.API_KEY });
const result = await client.generateSheet({
format: "xlsx",
sheets: [
{
name: "Q1 Revenue",
columns: [
{ name: "Region" },
{ name: "Revenue (EUR)", width: 20 },
{ name: "Revenue (USD)", width: 20 }
],
rows: [
[
"DACH",
{
value: 142500.50,
format: "currency",
currency_code: "EUR",
number_style: "period_comma"
},
{
value: 155325.55,
format: "currency",
currency_code: "USD",
number_style: "comma_period"
}
],
[
"Nordics",
{
value: 98300.00,
format: "currency",
currency_code: "EUR",
number_style: "period_comma"
},
{
value: 107243.00,
format: "currency",
currency_code: "USD",
number_style: "comma_period"
}
]
]
}
]
});from iterationlayer import IterationLayer
client = IterationLayer(api_key=os.environ["API_KEY"])
result = client.generate_sheet(
format="xlsx",
sheets=[
{
"name": "Q1 Revenue",
"columns": [
{"name": "Region"},
{"name": "Revenue (EUR)", "width": 20},
{"name": "Revenue (USD)", "width": 20}
],
"rows": [
[
"DACH",
{
"value": 142500.50,
"format": "currency",
"currency_code": "EUR",
"number_style": "period_comma"
},
{
"value": 155325.55,
"format": "currency",
"currency_code": "USD",
"number_style": "comma_period"
}
],
[
"Nordics",
{
"value": 98300.00,
"format": "currency",
"currency_code": "EUR",
"number_style": "period_comma"
},
{
"value": 107243.00,
"format": "currency",
"currency_code": "USD",
"number_style": "comma_period"
}
]
]
}
]
)client := iterationlayer.NewClient(os.Getenv("API_KEY"))
eurStyle := "period_comma"
usdStyle := "comma_period"
currencyFormat := "currency"
eurCode := "EUR"
usdCode := "USD"
result, err := client.GenerateSheet(iterationlayer.GenerateSheetRequest{
Format: "xlsx",
Sheets: []iterationlayer.Sheet{
{
Name: "Q1 Revenue",
Columns: []iterationlayer.SheetColumn{
{Name: "Region"},
{Name: "Revenue (EUR)", Width: 20},
{Name: "Revenue (USD)", Width: 20},
},
Rows: [][]iterationlayer.SheetCell{
{
{Value: "DACH"},
{Value: 142500.50, Format: currencyFormat, CurrencyCode: eurCode, NumberStyle: eurStyle},
{Value: 155325.55, Format: currencyFormat, CurrencyCode: usdCode, NumberStyle: usdStyle},
},
{
{Value: "Nordics"},
{Value: 98300.00, Format: currencyFormat, CurrencyCode: eurCode, NumberStyle: eurStyle},
{Value: 107243.00, Format: currencyFormat, CurrencyCode: usdCode, NumberStyle: usdStyle},
},
},
},
},
})
The number_style field controls thousands and decimal separators:
-
"comma_period"— US style:142,500.50 -
"period_comma"— European style:142.500,50 -
"space_comma"— French/SI style:142 500,50 -
"space_period"— Variant:142 500.50
Combined with currency_code, this gives you locale-correct currency formatting without any locale detection or formatting library. The EUR column shows 142.500,50 EUR with European separators. The USD column shows 142,500.50 USD with US separators. Same data model, different presentation.
Percentages and Dates
Currency is the most requested format, but the same pattern applies to every cell type:
{
"value": 0.156,
"format": "percentage"
}
This renders as 15.6% in the cell. The raw value stays 0.156 for formula calculation purposes.
{
"value": "2026-04-15",
"format": "date",
"date_style": "dd.MM.yyyy"
}
Date values accept ISO 8601 strings. The date_style field controls how the date appears in the spreadsheet — dd.MM.yyyy for European format, MM/dd/yyyy for US format, or any custom pattern your team expects.
Cell Styling: Bold Headers, Colored Rows, Aligned Numbers
Formatting tells the spreadsheet what a value is. Styling tells it what a value looks like. The Sheet Generation API separates these concerns cleanly.
Every cell can carry a styles object with properties for font, color, alignment, and background:
curl -X POST https://api.iterationlayer.com/sheet-generation/v1/generate \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"format": "xlsx",
"sheets": [
{
"name": "Styled Report",
"columns": [
{
"name": "Category",
"width": 25
},
{
"name": "Budget",
"width": 18
},
{
"name": "Actual",
"width": 18
},
{
"name": "Variance",
"width": 18
}
],
"rows": [
[
"Marketing",
{
"value": 50000,
"format": "currency",
"currency_code": "EUR",
"styles": {
"horizontal_alignment": "right"
}
},
{
"value": 47200,
"format": "currency",
"currency_code": "EUR",
"styles": {
"horizontal_alignment": "right"
}
},
{
"value": 2800,
"format": "currency",
"currency_code": "EUR",
"styles": {
"horizontal_alignment": "right",
"font_color": "#16a34a"
}
}
],
[
"Infrastructure",
{
"value": 30000,
"format": "currency",
"currency_code": "EUR",
"styles": {
"horizontal_alignment": "right"
}
},
{
"value": 34500,
"format": "currency",
"currency_code": "EUR",
"styles": {
"horizontal_alignment": "right"
}
},
{
"value": -4500,
"format": "currency",
"currency_code": "EUR",
"styles": {
"horizontal_alignment": "right",
"font_color": "#dc2626"
}
}
]
]
}
],
"styles": {
"header": {
"is_bold": true,
"background_color": "#1e293b",
"font_color": "#ffffff"
}
}
}'import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: process.env.API_KEY });
const result = await client.generateSheet({
format: "xlsx",
sheets: [
{
name: "Styled Report",
columns: [
{ name: "Category", width: 25 },
{ name: "Budget", width: 18 },
{ name: "Actual", width: 18 },
{ name: "Variance", width: 18 }
],
rows: [
[
"Marketing",
{
value: 50000,
format: "currency",
currency_code: "EUR",
styles: { horizontal_alignment: "right" }
},
{
value: 47200,
format: "currency",
currency_code: "EUR",
styles: { horizontal_alignment: "right" }
},
{
value: 2800,
format: "currency",
currency_code: "EUR",
styles: { horizontal_alignment: "right", font_color: "#16a34a" }
}
],
[
"Infrastructure",
{
value: 30000,
format: "currency",
currency_code: "EUR",
styles: { horizontal_alignment: "right" }
},
{
value: 34500,
format: "currency",
currency_code: "EUR",
styles: { horizontal_alignment: "right" }
},
{
value: -4500,
format: "currency",
currency_code: "EUR",
styles: { horizontal_alignment: "right", font_color: "#dc2626" }
}
]
]
}
],
styles: {
header: {
is_bold: true,
background_color: "#1e293b",
font_color: "#ffffff"
}
}
});from iterationlayer import IterationLayer
client = IterationLayer(api_key=os.environ["API_KEY"])
result = client.generate_sheet(
format="xlsx",
sheets=[
{
"name": "Styled Report",
"columns": [
{"name": "Category", "width": 25},
{"name": "Budget", "width": 18},
{"name": "Actual", "width": 18},
{"name": "Variance", "width": 18}
],
"rows": [
[
"Marketing",
{
"value": 50000,
"format": "currency",
"currency_code": "EUR",
"styles": {"horizontal_alignment": "right"}
},
{
"value": 47200,
"format": "currency",
"currency_code": "EUR",
"styles": {"horizontal_alignment": "right"}
},
{
"value": 2800,
"format": "currency",
"currency_code": "EUR",
"styles": {
"horizontal_alignment": "right",
"font_color": "#16a34a"
}
}
],
[
"Infrastructure",
{
"value": 30000,
"format": "currency",
"currency_code": "EUR",
"styles": {"horizontal_alignment": "right"}
},
{
"value": 34500,
"format": "currency",
"currency_code": "EUR",
"styles": {"horizontal_alignment": "right"}
},
{
"value": -4500,
"format": "currency",
"currency_code": "EUR",
"styles": {
"horizontal_alignment": "right",
"font_color": "#dc2626"
}
}
]
]
}
],
styles={
"header": {
"is_bold": True,
"background_color": "#1e293b",
"font_color": "#ffffff"
}
}
)client := iterationlayer.NewClient(os.Getenv("API_KEY"))
result, err := client.GenerateSheet(iterationlayer.GenerateSheetRequest{
Format: "xlsx",
Sheets: []iterationlayer.Sheet{
{
Name: "Styled Report",
Columns: []iterationlayer.SheetColumn{
{Name: "Category", Width: 25},
{Name: "Budget", Width: 18},
{Name: "Actual", Width: 18},
{Name: "Variance", Width: 18},
},
Rows: [][]iterationlayer.SheetCell{
{
{Value: "Marketing"},
{Value: 50000, Format: "currency", CurrencyCode: "EUR",
Styles: &iterationlayer.SheetCellStyle{HorizontalAlignment: "right"}},
{Value: 47200, Format: "currency", CurrencyCode: "EUR",
Styles: &iterationlayer.SheetCellStyle{HorizontalAlignment: "right"}},
{Value: 2800, Format: "currency", CurrencyCode: "EUR",
Styles: &iterationlayer.SheetCellStyle{HorizontalAlignment: "right", FontColor: "#16a34a"}},
},
{
{Value: "Infrastructure"},
{Value: 30000, Format: "currency", CurrencyCode: "EUR",
Styles: &iterationlayer.SheetCellStyle{HorizontalAlignment: "right"}},
{Value: 34500, Format: "currency", CurrencyCode: "EUR",
Styles: &iterationlayer.SheetCellStyle{HorizontalAlignment: "right"}},
{Value: -4500, Format: "currency", CurrencyCode: "EUR",
Styles: &iterationlayer.SheetCellStyle{HorizontalAlignment: "right", FontColor: "#dc2626"}},
},
},
},
},
Styles: &iterationlayer.SheetStyles{
Header: &iterationlayer.SheetCellStyle{
IsBold: true,
BackgroundColor: "#1e293b",
FontColor: "#ffffff",
},
},
})Three things to notice here.
First, the styles object at the request level applies to all sheets. Header styles (bold, dark background, white text) are set once and apply to every column header. You don’t repeat them per cell.
Second, individual cell styles override the global ones. The variance column uses font_color to show positive values in green and negative values in red. That’s a cell-level override on top of the global body style.
Third, horizontal_alignment on the currency cells pushes numbers to the right edge of the column, which is what every accountant expects. Text cells default to left alignment. This is the kind of detail that takes five minutes to figure out in a library and one field to specify in the API.
Multi-Sheet Workbooks
Real financial reports are never a single sheet. You need a summary tab, detail tabs by department or region, and maybe a reference tab with assumptions or exchange rates. The sheets array handles this naturally — each entry becomes a separate tab in the workbook.
curl -X POST https://api.iterationlayer.com/sheet-generation/v1/generate \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"format": "xlsx",
"sheets": [
{
"name": "Summary",
"columns": [
{
"name": "Department",
"width": 20
},
{
"name": "Q1 Total",
"width": 18
},
{
"name": "Q2 Total",
"width": 18
}
],
"rows": [
[
"Engineering",
{
"value": 285000,
"format": "currency",
"currency_code": "EUR"
},
{
"value": 292000,
"format": "currency",
"currency_code": "EUR"
}
],
[
"Marketing",
{
"value": 142000,
"format": "currency",
"currency_code": "EUR"
},
{
"value": 138000,
"format": "currency",
"currency_code": "EUR"
}
]
]
},
{
"name": "Engineering Detail",
"columns": [
{
"name": "Month"
},
{
"name": "Salaries",
"width": 18
},
{
"name": "Tools",
"width": 18
},
{
"name": "Infrastructure",
"width": 18
}
],
"rows": [
[
"January",
{
"value": 82000,
"format": "currency",
"currency_code": "EUR"
},
{
"value": 4500,
"format": "currency",
"currency_code": "EUR"
},
{
"value": 8500,
"format": "currency",
"currency_code": "EUR"
}
],
[
"February",
{
"value": 82000,
"format": "currency",
"currency_code": "EUR"
},
{
"value": 4200,
"format": "currency",
"currency_code": "EUR"
},
{
"value": 9300,
"format": "currency",
"currency_code": "EUR"
}
],
[
"March",
{
"value": 82000,
"format": "currency",
"currency_code": "EUR"
},
{
"value": 4800,
"format": "currency",
"currency_code": "EUR"
},
{
"value": 7700,
"format": "currency",
"currency_code": "EUR"
}
]
]
},
{
"name": "Exchange Rates",
"columns": [
{
"name": "Currency Pair"
},
{
"name": "Rate",
"width": 12
},
{
"name": "As Of",
"width": 14
}
],
"rows": [
[
"EUR/USD",
{
"value": 1.0892,
"format": "decimal"
},
{
"value": "2026-04-15",
"format": "date",
"date_style": "dd.MM.yyyy"
}
],
[
"EUR/GBP",
{
"value": 0.8567,
"format": "decimal"
},
{
"value": "2026-04-15",
"format": "date",
"date_style": "dd.MM.yyyy"
}
]
]
}
],
"styles": {
"header": {
"is_bold": true,
"background_color": "#0f172a",
"font_color": "#ffffff"
}
}
}'import { IterationLayer } from "iterationlayer";
const client = new IterationLayer({ apiKey: process.env.API_KEY });
const result = await client.generateSheet({
format: "xlsx",
sheets: [
{
name: "Summary",
columns: [
{ name: "Department", width: 20 },
{ name: "Q1 Total", width: 18 },
{ name: "Q2 Total", width: 18 }
],
rows: [
[
"Engineering",
{ value: 285000, format: "currency", currency_code: "EUR" },
{ value: 292000, format: "currency", currency_code: "EUR" }
],
[
"Marketing",
{ value: 142000, format: "currency", currency_code: "EUR" },
{ value: 138000, format: "currency", currency_code: "EUR" }
]
]
},
{
name: "Engineering Detail",
columns: [
{ name: "Month" },
{ name: "Salaries", width: 18 },
{ name: "Tools", width: 18 },
{ name: "Infrastructure", width: 18 }
],
rows: [
[
"January",
{ value: 82000, format: "currency", currency_code: "EUR" },
{ value: 4500, format: "currency", currency_code: "EUR" },
{ value: 8500, format: "currency", currency_code: "EUR" }
],
[
"February",
{ value: 82000, format: "currency", currency_code: "EUR" },
{ value: 4200, format: "currency", currency_code: "EUR" },
{ value: 9300, format: "currency", currency_code: "EUR" }
],
[
"March",
{ value: 82000, format: "currency", currency_code: "EUR" },
{ value: 4800, format: "currency", currency_code: "EUR" },
{ value: 7700, format: "currency", currency_code: "EUR" }
]
]
},
{
name: "Exchange Rates",
columns: [
{ name: "Currency Pair" },
{ name: "Rate", width: 12 },
{ name: "As Of", width: 14 }
],
rows: [
[
"EUR/USD",
{ value: 1.0892, format: "decimal" },
{ value: "2026-04-15", format: "date", date_style: "dd.MM.yyyy" }
],
[
"EUR/GBP",
{ value: 0.8567, format: "decimal" },
{ value: "2026-04-15", format: "date", date_style: "dd.MM.yyyy" }
]
]
}
],
styles: {
header: {
is_bold: true,
background_color: "#0f172a",
font_color: "#ffffff"
}
}
});from iterationlayer import IterationLayer
client = IterationLayer(api_key=os.environ["API_KEY"])
result = client.generate_sheet(
format="xlsx",
sheets=[
{
"name": "Summary",
"columns": [
{"name": "Department", "width": 20},
{"name": "Q1 Total", "width": 18},
{"name": "Q2 Total", "width": 18}
],
"rows": [
[
"Engineering",
{"value": 285000, "format": "currency", "currency_code": "EUR"},
{"value": 292000, "format": "currency", "currency_code": "EUR"}
],
[
"Marketing",
{"value": 142000, "format": "currency", "currency_code": "EUR"},
{"value": 138000, "format": "currency", "currency_code": "EUR"}
]
]
},
{
"name": "Engineering Detail",
"columns": [
{"name": "Month"},
{"name": "Salaries", "width": 18},
{"name": "Tools", "width": 18},
{"name": "Infrastructure", "width": 18}
],
"rows": [
[
"January",
{"value": 82000, "format": "currency", "currency_code": "EUR"},
{"value": 4500, "format": "currency", "currency_code": "EUR"},
{"value": 8500, "format": "currency", "currency_code": "EUR"}
],
[
"February",
{"value": 82000, "format": "currency", "currency_code": "EUR"},
{"value": 4200, "format": "currency", "currency_code": "EUR"},
{"value": 9300, "format": "currency", "currency_code": "EUR"}
],
[
"March",
{"value": 82000, "format": "currency", "currency_code": "EUR"},
{"value": 4800, "format": "currency", "currency_code": "EUR"},
{"value": 7700, "format": "currency", "currency_code": "EUR"}
]
]
},
{
"name": "Exchange Rates",
"columns": [
{"name": "Currency Pair"},
{"name": "Rate", "width": 12},
{"name": "As Of", "width": 14}
],
"rows": [
[
"EUR/USD",
{"value": 1.0892, "format": "decimal"},
{"value": "2026-04-15", "format": "date", "date_style": "dd.MM.yyyy"}
],
[
"EUR/GBP",
{"value": 0.8567, "format": "decimal"},
{"value": "2026-04-15", "format": "date", "date_style": "dd.MM.yyyy"}
]
]
}
],
styles={
"header": {
"is_bold": True,
"background_color": "#0f172a",
"font_color": "#ffffff"
}
}
)client := iterationlayer.NewClient(os.Getenv("API_KEY"))
result, err := client.GenerateSheet(iterationlayer.GenerateSheetRequest{
Format: "xlsx",
Sheets: []iterationlayer.Sheet{
{
Name: "Summary",
Columns: []iterationlayer.SheetColumn{
{Name: "Department", Width: 20},
{Name: "Q1 Total", Width: 18},
{Name: "Q2 Total", Width: 18},
},
Rows: [][]iterationlayer.SheetCell{
{
{Value: "Engineering"},
{Value: 285000, Format: "currency", CurrencyCode: "EUR"},
{Value: 292000, Format: "currency", CurrencyCode: "EUR"},
},
{
{Value: "Marketing"},
{Value: 142000, Format: "currency", CurrencyCode: "EUR"},
{Value: 138000, Format: "currency", CurrencyCode: "EUR"},
},
},
},
{
Name: "Engineering Detail",
Columns: []iterationlayer.SheetColumn{
{Name: "Month"},
{Name: "Salaries", Width: 18},
{Name: "Tools", Width: 18},
{Name: "Infrastructure", Width: 18},
},
Rows: [][]iterationlayer.SheetCell{
{
{Value: "January"},
{Value: 82000, Format: "currency", CurrencyCode: "EUR"},
{Value: 4500, Format: "currency", CurrencyCode: "EUR"},
{Value: 8500, Format: "currency", CurrencyCode: "EUR"},
},
{
{Value: "February"},
{Value: 82000, Format: "currency", CurrencyCode: "EUR"},
{Value: 4200, Format: "currency", CurrencyCode: "EUR"},
{Value: 9300, Format: "currency", CurrencyCode: "EUR"},
},
{
{Value: "March"},
{Value: 82000, Format: "currency", CurrencyCode: "EUR"},
{Value: 4800, Format: "currency", CurrencyCode: "EUR"},
{Value: 7700, Format: "currency", CurrencyCode: "EUR"},
},
},
},
{
Name: "Exchange Rates",
Columns: []iterationlayer.SheetColumn{
{Name: "Currency Pair"},
{Name: "Rate", Width: 12},
{Name: "As Of", Width: 14},
},
Rows: [][]iterationlayer.SheetCell{
{
{Value: "EUR/USD"},
{Value: 1.0892, Format: "decimal"},
{Value: "2026-04-15", Format: "date", DateStyle: "dd.MM.yyyy"},
},
{
{Value: "EUR/GBP"},
{Value: 0.8567, Format: "decimal"},
{Value: "2026-04-15", Format: "date", DateStyle: "dd.MM.yyyy"},
},
},
},
},
Styles: &iterationlayer.SheetStyles{
Header: &iterationlayer.SheetCellStyle{
IsBold: true,
BackgroundColor: "#0f172a",
FontColor: "#ffffff",
},
},
})One API call produces a workbook with three tabs: a summary for leadership, a detail sheet for the engineering manager, and a reference sheet with the exchange rates used. The recipient opens one file and navigates between views. No manual assembly, no copy-paste between workbooks.
Custom Number Formats
For cases where the built-in formats don’t match your requirements, the number_format field in cell styles accepts custom Excel number format strings. This is the same syntax you’d use in Excel’s “Custom” number format dialog.
{
"value": 1500000,
"format": "number",
"styles": {
"number_format": "#,##0.00 \"units\""
}
}
This renders as 1,500,000.00 units. The number_format string gives you full control over how the number appears in the cell — thousands separators, decimal places, suffixes, conditional formatting.
Common patterns:
-
"#,##0"— integer with thousands separator -
"#,##0.00"— two decimal places -
"0.0%"— percentage with one decimal -
"#,##0.00 \"kg\""— number with a unit suffix
Cell Merging for Summary Rows
Financial reports often need merged cells — a total row that spans multiple columns, a section header that stretches across the sheet. The from_col, to_col, from_row, and to_row fields handle this.
{
"value": "Q1 2026 Financial Summary",
"from_col": 0,
"to_col": 3,
"styles": {
"is_bold": true,
"font_size_in_pt": 14,
"horizontal_alignment": "center",
"background_color": "#f8fafc"
}
}This merges columns 0 through 3 into a single cell with centered, bold text. The merge is specified in the data, not through a separate merge API or cell reference calculation. You describe the layout you want, and the API handles the XLSX mechanics.
The Real Win: Spreadsheets in Your Automation Pipeline
The code examples above show the API in isolation. The real value shows up when sheet generation is one step in a larger workflow.
Consider a monthly reporting pipeline. Invoice data arrives as PDFs. Someone needs to extract the line items, aggregate them by vendor, and produce a formatted spreadsheet for the finance team. Traditionally, this is three systems: an OCR tool for extraction, a script for aggregation, and a spreadsheet library for output.
With Iteration Layer, the extraction and generation use the same API key, the same credit pool, and the same error format. The Document Extraction API returns structured JSON. That JSON maps directly to sheet rows. No intermediate transformation, no format juggling, no glue code.
The same pattern applies to any workflow where structured data needs to become a spreadsheet:
- E-commerce catalog exports — product data from your database becomes a formatted XLSX with images, pricing in multiple currencies, and inventory counts
- Timesheet reports — hours logged in your project management tool become a multi-sheet workbook with per-project breakdowns and monthly summaries
- Compliance audit trails — system logs become formatted spreadsheets with timestamps, user actions, and color-coded severity levels
In each case, the spreadsheet is the last mile. The data already exists. The API bridges the gap between your data and the file format your recipients expect.
When a Spreadsheet Library Makes More Sense
The Sheet Generation API is not the right tool for every spreadsheet task.
If you need to read and modify existing spreadsheets — parse an uploaded XLSX, update specific cells, and save it back — a library like openpyxl or ExcelJS is the right choice. The API generates spreadsheets from scratch; it does not edit existing files.
If your spreadsheet needs complex formulas that reference cells across sheets with conditional logic, a library gives you more direct control over the formula engine. The API handles data presentation and formatting; it is not a replacement for Excel’s formula runtime.
If you’re generating spreadsheets inside a tight loop (hundreds per second) and latency is critical, a local library avoids the HTTP round trip. The API adds network latency that may matter for high-frequency, latency-sensitive workloads.
For everything else — turning structured data into formatted, multi-sheet, currency-aware XLSX files that someone can open in Excel and immediately understand — the API eliminates the boilerplate and lets you describe the spreadsheet you want instead of programming the mechanics of producing it.
Get Started
The Sheet Generation API is available now. Send a JSON payload, get back a formatted spreadsheet. Same auth and same credit pool as every other Iteration Layer API — if you’re already using Document Extraction or Image Generation, adding sheet generation to your pipeline takes minutes.
Install the SDK for your language and make your first request. The API documentation covers every field, format, and style option.
If your team spends hours assembling spreadsheets from data that already lives in your systems, that time is recoverable. The spreadsheet is just the last step. Let the API handle it.