# Excel Model API: Recalculate Workbooks, What-If & Goal Seek (`jumblemog/excel-model-api`) Actor

Run your Excel or Google Sheets model as an API: set inputs by cell, name or label, recalculate every formula and get the results as JSON. Scenarios, sweeps over one or two inputs, goal seek, and a list of a workbook's inputs and results.

- **URL**: https://apify.com/jumblemog/excel-model-api.md
- **Developed by:** [Jumble Mog](https://apify.com/jumblemog) (community)
- **Categories:** AI, Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Excel Model API: Recalculate Workbooks, What-If & Goal Seek

**Run your spreadsheet model as an API.** Send an Excel or Google Sheets workbook with the inputs to change, by the label next to the cell (`"Home price"`), a defined name or an address (`Inputs!B3`). Every formula is recalculated and the results come back as JSON. You can also run many scenarios, sweep one or two inputs through a range, or goal seek a result to a target. A workbook sent alone lists the inputs and results it has, with their labels and current values.

- **Your workbook, unchanged.** It runs as built, with no formulas to rewrite and no code to port. The pricing sheet, quote calculator, loan model or engineering workbook a team already trusts becomes something a program or an AI agent can call.
- **Inputs by label.** `{"Interest rate": "5.5%", "Start date": "2027-01-15", "Plan": "Business"}` is read the way a person would type it: currency, percentages, dates and TRUE/FALSE. A list fills a range, such as line items or monthly figures.
- **Scenarios, sweeps and goal seek.**
  - Up to 1,000 calculations per workbook, from a list, a CSV, an Excel file or a Google Sheet of scenarios.
  - One-way and two-way what-if tables over any input.
  - Excel-style goal seek: "what home price gives a $3,000 payment?"
  - Every request with more than one calculation also gets a CSV with one row per calculation.
- **It finds the model for you.** A workbook sent alone comes back with:
  - its inputs: the constants that formulas point at;
  - its results: the formulas nothing else uses, then the labeled ones other formulas build on;
  - the lookup tables it reads;
  - a sample request to send back.
- **Recalculated copies.** Optionally get back the workbook with the new inputs and recalculated values, as .xlsx (or .ods).

It's made for:

- AI agents that need a company's real numbers: a pricing model, a commission plan, a capacity or ROI calculator kept in Excel, called as a tool instead of guessed at.
- Back-office and SaaS apps that quote, price or forecast with a spreadsheet the business owns, without re-implementing it in code.
- Analysts who want a sensitivity table or a batch of 500 scenarios run against a model, without copying and pasting.
- Zapier, Make and n8n flows that need "put these numbers in the sheet and give me the total".

![A mortgage model's Inputs sheet with Home price changed from $450,000 to $520,000 and Interest rate from 6.25% to 5.5%, its results recalculated (monthly payment $2,963.67), next to the JSON request and answer, and a goal seek finding the home price for a $3,000 payment](https://api.apify.com/v2/key-value-stores/Km5b4PG3kYHdZSb1E/records/excel-model-api-example.png?signature=1aRRVlcqA7QZoGRZrrS)

### How to use it

1. Give the **workbook**: a link to the .xlsx, .xlsm, .xlsb, .xls or .ods file, a Google Sheets link shared with "Anyone with the link", or upload it.
2. Put the **inputs to change** and the **outputs to read** in by their labels, names or addresses. Leave the outputs empty to get every result the workbook has.
3. Click **Start**. Each calculation is a row in the results, with the inputs set, the outputs read and their values as the workbook shows them.

Not sure what a workbook takes? Run it with the workbook alone and you get its inputs and results, with a sample request to send back.

#### Naming cells

| Write | Means |
|---|---|
| `"Home price"`, `"home_price"`, `"Term (years)"` | The cell next to that label: the value to its right, or below a column heading. Case, spaces and units in brackets don't matter. |
| `"closing_costs"` | A defined name. |
| `"B3"`, `"Inputs!B3"`, `"'Rate Table'!A2:B4"` | An address (without a sheet: the first sheet, or the one you set as `sheet`). |
| `"Revenue (2027)"` | In a table with years across the top, the row label with the column heading. |

#### Values

`520000`, `"$520,000"`, `"5.5%"` (0.055), `"2027-01-15"` (a date), `true`, `"Business"` (text). A number given for a cell formatted as a percentage is read as a percentage when it's over 1: `5.5` becomes 5.5%, and the result says so. `[1200, 1350, 1500]` fills a range down from the cell; `[[...], [...]]` fills rows. Inputs are put back after each calculation, so scenarios never leak into each other.

#### What you can send

```json
{
  "workbook": "https://docs.google.com/spreadsheets/d/<id>/edit",
  "inputs": {"Home price": 520000},
  "sweep": [{"input": "Interest rate", "from": 0.04, "to": 0.08, "step": 0.005},
            {"input": "Term (years)", "values": [15, 30]}],
  "outputs": ["Monthly payment", "Total interest"]
}
```

| Field | What it takes |
|---|---|
| `workbook` | A link, a `data:` URI or base64. Or upload it in `uploads`. |
| `inputs` | Cells to change and their values, applied to every calculation. |
| `outputs` | Cells or ranges to read. A range (`"Schedule!A2:E13"`) comes back as rows of values. |
| `scenarios` | A list of input sets, one calculation each. A `scenario` key names each one. Or `scenariosUrl`: a CSV, Excel or JSON link, a Google Sheet, or CSV text, one scenario per row. |
| `sweep` | One input stepped `from`/`to` by `step` (or `steps` points, or `values`), or two inputs for a two-way table. |
| `goalSeek` | `[{"set": "Monthly payment", "to": 3000, "by": "Home price"}]`: the input value that makes the result hit the target. |
| `sheet` | The sheet that plain addresses refer to. |
| `returnWorkbook` | `true` saves the recalculated workbook for each calculation (the first 20). |
| `requests` | More workbooks in one run, each with its own fields. |

### Examples

These use the example workbook in the input form, a mortgage model with a 360-month schedule sheet.

- **New inputs.** `{"Home price": 520000, "Interest rate": "5.5%"}` gives a monthly payment of $2,963.67, total interest of $434,321 and a payoff date of 2056-11-01.
- **Goal seek.** Setting "Monthly payment" to 3000 by changing "Home price" finds a home price of $492,092.
- **The workbook alone.** It lists:
  - 8 inputs: Home price, Down payment, Interest rate, Term (years) and the rest;
  - its results: Monthly payment, Total interest, Payoff date, Cash needed at closing, plus the schedule's Date column as one range;
  - a sample request.
- **Two-way sweep.** Interest rate 4% to 8% by 0.5%, crossed with a 15-year or 30-year term, makes 18 calculations: 18 rows plus one CSV table.

### How-to examples

These use the example workbook in the input form, a mortgage model with a 360-month schedule sheet.

#### How to run an Excel model with new inputs through an API

```json
{"workbook": "https://example.com/models/mortgage.xlsx",
 "inputs": {"Home price": 520000, "Interest rate": "5.5%"},
 "outputs": ["Monthly payment", "Total interest", "Payoff date"]}
```

Result: a monthly payment of $2,963.67, total interest of $434,321 and a payoff date of 2056-11-01, read from the recalculated workbook. Cells are named by the label next to them, so you don't need cell addresses.

#### How to goal seek in Excel from code

```json
{"workbook": "https://example.com/models/mortgage.xlsx",
 "goalSeek": [{"set": "Monthly payment", "to": 3000, "by": "Home price"}]}
```

Result: the home price that gives a $3,000 monthly payment, $492,092.

#### How to make a sensitivity table from a spreadsheet

```json
{"workbook": "https://example.com/models/mortgage.xlsx", "inputs": {"Home price": 520000},
 "sweep": [{"input": "Interest rate", "from": 0.04, "to": 0.08, "step": 0.005}, {"input": "Term (years)", "values": [15, 30]}],
 "outputs": ["Monthly payment", "Total interest"]}
```

Result: 18 calculations, every rate from 4% to 8% by 0.5% for both terms, as 18 rows and one CSV table.

#### How to let an AI agent use your spreadsheet

Add this Actor to the [Apify MCP server](https://mcp.apify.com) with `https://mcp.apify.com/?tools=jumblemog/excel-model-api`. The agent first sends the workbook alone and gets its inputs, its results and a sample request. Then it sends the values the user gave, and the answer comes from your own formulas instead of the model's guess.

### FAQ

**Do I have to rewrite my formulas?** No. The workbook runs as it is, recalculated by LibreOffice Calc. You only name the inputs to change and the results to read.

**Which files work?** Excel .xlsx, .xlsm, .xlsb and .xls, OpenDocument .ods, and Google Sheets links shared with "Anyone with the link".

**Are macros run?** No. Macros are never run and external links are never updated, so the result comes from the formulas alone.

**Can I run many scenarios at once?** Yes, as a list in `scenarios`, from a CSV or Google Sheet with one scenario per row, or as a one-way or two-way `sweep`.

**Does it work with Zapier, Make or n8n?** Yes. Call the Actor with its Apify app or an HTTP request, for example to turn a form's answers into a quote from your pricing workbook.

### Output

There is one dataset item per calculation. They are also saved together in the run's `OUTPUT` record:

```json
{
  "request": 1, "status": "ok", "type": "calculation", "calculation": 1, "calculations": 1,
  "title": "mortgage-model-example: With Home price = 520,000, Interest rate = 5.5%",
  "summary": "With Home price = 520,000, Interest rate = 5.5%. Monthly payment = $2,963.67; Total interest = $434,321; Payoff date = 2056-11-01.",
  "inputs": [{"key": "Interest rate", "address": "Inputs!B5", "label": "Interest rate", "previous": 0.0625, "value": 0.055}],
  "outputs": [{"key": "Monthly payment", "address": "Inputs!B16", "label": "Monthly payment", "value": 2963.6689122702, "formatted": "$2,963.67"}],
  "values": {"Monthly payment": 2963.6689122702, "Total interest": 434320.808417271, "Payoff date": "2056-11-01"},
  "goal": null, "workbookUrl": null, "tableUrl": null, "warnings": []
}
```

How values come back:

- Dates come back as ISO text and TRUE/FALSE as booleans. Each value also comes formatted as the workbook shows it.
- A formula that fails (`#DIV/0!`, `#N/A`, `#NAME?`) comes back as `error`, with what it usually means and a warning.

Other item types:

- A goal seek item has `"type": "goalSeek"` and `goal`: the value found, the result achieved and whether it converged.
- A workbook sent alone gives `"type": "describe"` with `inputs`, `results`, `tables`, `sample` and the workbook's defined names.

Every item has `warnings`, `notes` and a Markdown `report`. A workbook that can't be read, or a label that isn't in it, gives `"status": "failed"`. The `error` explains why and lists the labels it does have. Failed requests aren't billed.

### How it works

1. **Reading the workbook.** [LibreOffice](https://www.libreoffice.org/) Calc opens it headless, with macros never run and external links never updated. It reads Excel (.xlsx, .xlsm, .xlsb, .xls), OpenDocument and Google Sheets (exported as .xlsx).
2. **Finding the model.** Every formula's references are read, including other sheets, ranges, whole columns and defined names. From those:
   - the inputs are the constants that formulas point at one by one;
   - the results are the formulas no other formula uses;
   - a column of a table is reported as one range;
   - each cell is labeled with the text to its left or the heading above it.
3. **Calculating.** For each calculation:

   - the inputs are set;
   - the whole workbook is recalculated (a hard recalculation, so cached values never leak through);
   - the outputs are read, rounded to Excel's 15 significant digits;
   - the inputs are restored.

   Goal seek is Calc's own.

### Pricing

You pay one low price per calculation, at the price shown on this page:

- Each scenario, sweep point or goal seek is one calculation.
- Listing a workbook's inputs and results counts as one.
- Recalculated copies and the CSV table are included.
- Failed requests are never charged.
- If you set a maximum cost per run, the Actor runs only the calculations that fit.

### Speed

The example model (about 1,450 formulas) takes about 0.03 s per calculation, so a 100-point sweep runs in about 5 seconds. A run through the API answers in about 10 to 15 seconds for one calculation, which includes starting the cloud container and LibreOffice.

### Tips and limits

- **Functions.** LibreOffice Calc has nearly all Excel functions, including XLOOKUP, XMATCH, FILTER, SORT, UNIQUE, SEQUENCE and LET. LAMBDA and a few of the newest Excel functions aren't available, and neither are Google-only functions (QUERY, GOOGLEFINANCE, IMPORTRANGE). Formulas using them come back as `#NAME?` with a warning.
- **Macros and add-ins.** VBA macros, Power Query, data connections and add-in functions don't run. Their last values stay unless a formula depends on them.
- **Iterative calculation.** Workbooks with intentional circular references calculate if iterative calculation is turned on in the file.
- **Limits:**
  - 20 workbooks per run;
  - 1,000 calculations per workbook;
  - 30 MB per workbook;
  - 20,000 cells per output range;
  - for finding inputs and results, the first 400,000 cells of each sheet are read (any cell can still be named by address).

### For AI agents and developers

This Actor works with the [Apify MCP server](https://mcp.apify.com), so AI agents can call it as a tool. First call it with the workbook alone to get `inputs`, `results` and `sample`. Then call it with `inputs` filled in and read `values`.

A direct API call that waits for the answer:

```bash
curl -X POST "https://api.apify.com/v2/acts/jumblemog~excel-model-api/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"workbook": "https://example.com/pricing.xlsx", "inputs": {"Plan": "Business", "Seats": 40}, "outputs": ["Monthly total"]}'
```

`run-sync` returns the run's `OUTPUT` instead: the status, the counts and every result as JSON.

In Python, with the `apify-client` package, here is a batch of scenarios from a local workbook and CSV:

```python
import base64
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("jumblemog/excel-model-api").call(run_input={
    "workbook": base64.b64encode(open("commission-plan.xlsx", "rb").read()).decode(),
    "scenariosUrl": open("reps.csv").read(),      # one row per sales rep: scenario, Quota, Bookings, ...
    "outputs": ["Commission", "Attainment"],
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["scenario"], item["values"])
```

### Credits

- [LibreOffice](https://www.libreoffice.org/) (MPL-2.0) Calc recalculates the workbooks, driven through its UNO API.
- [openpyxl](https://openpyxl.readthedocs.io/) (MIT) reads scenario spreadsheets.
- The Carlito and Liberation fonts (SIL Open Font License) give text the widths of Calibri and Arial.

# Actor input Schema

## `workbook` (type: `string`):

A link to the .xlsx, .xlsm, .xlsb, .xls or .ods file, or a Google Sheets link (shared so anyone with the link can view); Google Drive, Dropbox and OneDrive share links work. Base64 works too. Send it alone to list its inputs and results.

## `uploads` (type: `array`):

Upload one workbook from your computer, or pick one already in an Apify key-value store.

## `inputs` (type: `object`):

Each cell to change and its new value. Name a cell by its label (the text next to it, such as "Home price"), a defined name, or an address ("B3", "Inputs!B3"). Values are read as typed: "$520,000", "5.5%", "2026-12-01", true. A list fills a range (one value per row, or rows of values).

## `outputs` (type: `array`):

Cells or ranges to read after recalculating, by label, name or address ("Schedule!A2:E13" for a table). Empty: the workbook's results, found from its formulas.

## `scenarios` (type: `array`):

Several sets of inputs, one calculation each, e.g. \[{"scenario": "low", "Interest rate": "5%"}, {"scenario": "high", "Interest rate": "7%"}]. The inputs above apply to every scenario. Up to 1,000.

## `scenariosUrl` (type: `string`):

A CSV, Excel or JSON link or a Google Sheet, or CSV text: one scenario per row, the first row naming the inputs (a column named scenario names each one).

## `sweep` (type: `array`):

Step one input through values, or two for a two-way table: \[{"input": "Interest rate", "from": 0.04, "to": 0.08, "step": 0.005}, {"input": "Term (years)", "values": \[15, 30]}]. Each point is one calculation.

## `goalSeek` (type: `array`):

Find the input value that makes a result hit a target, like Excel's Goal Seek: \[{"set": "Monthly payment", "to": 3000, "by": "Home price"}].

## `sheet` (type: `string`):

The sheet that addresses without a sheet name ("B3") refer to. By default the first sheet.

## `returnWorkbook` (type: `boolean`):

Save a copy of the workbook with the new inputs and recalculated values for each calculation (the first 20).

## `requests` (type: `array`):

More workbooks in one run: each entry is {"workbook": link or base64, "inputs", "outputs", "scenarios", "sweep", "goalSeek", "sheet", "returnWorkbook"}, or a link alone to list its inputs and results. Up to 20.

## Actor input object example

```json
{
  "workbook": "https://api.apify.com/v2/key-value-stores/Km5b4PG3kYHdZSb1E/records/mortgage-model-example.xlsx?signature=1pA7r4DdacNwvOiezUSFh",
  "inputs": {
    "Home price": 520000,
    "Interest rate": "5.5%"
  },
  "outputs": [
    "Monthly payment",
    "Total interest",
    "Payoff date"
  ],
  "returnWorkbook": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row per calculation: the inputs set and the outputs read.

## `output` (type: `string`):

The OUTPUT record: every result in one JSON object.

## `files` (type: `string`):

Recalculated workbooks, CSV tables of all calculations and reports in the run's key-value store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "workbook": "https://api.apify.com/v2/key-value-stores/Km5b4PG3kYHdZSb1E/records/mortgage-model-example.xlsx?signature=1pA7r4DdacNwvOiezUSFh",
    "inputs": {
        "Home price": 520000,
        "Interest rate": "5.5%"
    },
    "outputs": [
        "Monthly payment",
        "Total interest",
        "Payoff date"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jumblemog/excel-model-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "workbook": "https://api.apify.com/v2/key-value-stores/Km5b4PG3kYHdZSb1E/records/mortgage-model-example.xlsx?signature=1pA7r4DdacNwvOiezUSFh",
    "inputs": {
        "Home price": 520000,
        "Interest rate": "5.5%",
    },
    "outputs": [
        "Monthly payment",
        "Total interest",
        "Payoff date",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("jumblemog/excel-model-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "workbook": "https://api.apify.com/v2/key-value-stores/Km5b4PG3kYHdZSb1E/records/mortgage-model-example.xlsx?signature=1pA7r4DdacNwvOiezUSFh",
  "inputs": {
    "Home price": 520000,
    "Interest rate": "5.5%"
  },
  "outputs": [
    "Monthly payment",
    "Total interest",
    "Payoff date"
  ]
}' |
apify call jumblemog/excel-model-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jumblemog/excel-model-api"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/Iba5OMvyjYVMpaPZM/builds/NklVflQo3OdvPpX9u/openapi.json
