# Getting started

The infinite spreadsheet with Python, SQL, JavaScript, and AI.

Quadratic is a modern AI-enabled spreadsheet. Quadratic is purpose built to make working with data easier and faster than ever.

Quadratic combines a familiar spreadsheet and formulas with the power of AI and modern coding languages like Python, SQL, and JavaScript.&#x20;

{% embed url="<https://www.youtube.com/watch?v=QbGeH_n5SCA>" %}

Skip to a section of interest or get started in [the short walkthrough below.](#getting-started)

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f916">🤖</span> Learn how to use AI in Quadratic</td><td></td><td></td><td><a href="/pages/JorvVZZs5rwKwiOjRoqV">/pages/JorvVZZs5rwKwiOjRoqV</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f40d">🐍</span> Learn how Python works in Quadratic -></td><td></td><td></td><td><a href="/pages/VxbchdoguETkfdbxgOZQ">/pages/VxbchdoguETkfdbxgOZQ</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f50c">🔌</span> Learn how to connect to your databases with SQL -></td><td></td><td></td><td><a href="/pages/vpOwt9cqw8l0nRTcroNT">/pages/vpOwt9cqw8l0nRTcroNT</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f7e8">🟨</span> Learn how JavaScript works in Quadratic -></td><td></td><td></td><td><a href="/pages/swsKO6SJgJdy6adUtRR3">/pages/swsKO6SJgJdy6adUtRR3</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f522">🔢</span> Learn how Formulas work in Quadratic -></td><td></td><td></td><td><a href="/pages/DUyBOqbtHbyLBGgY5Rzj">/pages/DUyBOqbtHbyLBGgY5Rzj</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1fac2">🫂</span> Learn how Teams work in Quadratic -></td><td></td><td></td><td><a href="/pages/LXLgbxuSUpzar0PMAj7T">/pages/LXLgbxuSUpzar0PMAj7T</a></td></tr></tbody></table>

If you have any questions or feedback, we’d love to hear it! Share by [contacting us.](https://www.quadratichq.com/contact)

## Getting started

Get started in Quadratic by [creating your first blank sheet](https://app.quadratichq.com/files/create) or [picking an example file.](<https://app.quadratichq.com/examples >)

Here is a list of some common first things to do in Quadratic:

### Build something instantly with AI&#x20;

The AI Chat is open by default in your first sheet or can be accessed via the sparkle menu item in the sidebar. Simply ask questions in natural language to start writing code, inserting data, and making direct edits to your spreadsheet.&#x20;

{% @arcade/embed flowId="WmxlS1QtUZ0acwCuIBVJ" url="<https://app.arcade.software/share/WmxlS1QtUZ0acwCuIBVJ>" %}

### Write your first line of Python code :snake:

Start your first first line of code by simply pressing `/` after clicking a cell. This will open the command palette.&#x20;

<figure><img src="/files/lspB3Ya5Pv9kyPb3FGTV" alt="" width="563"><figcaption></figcaption></figure>

Picking your cell type will open the editor where you can start writing code.&#x20;

In Quadratic you can reference cells in the spreadsheet to use in your code, and you can return results from your Python analysis back to the spreadsheet. By default, the last line of code is returned to the spreadsheet.&#x20;

Single referenced cells are put in a variable with the appropriate data type. Multi-line references are placed in a DataFrame. Learn more [here](/python/getting-started)[.](/python/reference-cells)&#x20;

<figure><img src="/files/o9sacAmJJGsgZIQRE41Q" alt=""><figcaption></figcaption></figure>

To learn more about Python, [visit the Python section of the docs.](/python/getting-started)

### Use Formulas :1234:

Formulas in Quadratic are similar to how you'd expect in any spreadsheet. Formulas are relatively referenced as seen below.&#x20;

<figure><img src="/files/8gmm0osY5aiEk7VlSNK2" alt="" width="563"><figcaption><p>Basic Formula usage in Quadratic</p></figcaption></figure>

To learn more about Formulas, [visit the Formulas section of the docs. ](/formulas/getting-started)

### Connect to your databases with SQL :house\_with\_garden:

With SQL in Quadratic you can create live connections from your spreadsheets to your data sources. Data from your SQL queries is returned to the sheet, anchored at the position of the SQL cell.&#x20;

Use the AI assistant to rapidly generate accurate SQL queries, and view your SQL schema in the editor's schema viewer. You can learn more [here](/connections/sql-getting-started) or watch a quick SQL demo below.&#x20;

{% @arcade/embed flowId="CItgrkKi7r7wWYwiboxV" url="<https://app.arcade.software/share/CItgrkKi7r7wWYwiboxV>" %}

To learn more about SQL and connections, [visit the SQL section of the docs.](/connections/sql-getting-started)


# Getting started

Get started generating more powerful analytics using AI.

## Getting started

### AI Chat

Quadratic AI Chat is built to turbocharge your work. Every step of your analyses can be sped up and made more accessible, from generating code to generating new datasets entirely from scratch. Create, edit, and refine, all with Quadratic AI.

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f469-1f4bb">👩‍💻</span> Learn how to use Quadratic AI to generate code -></td><td></td><td></td><td></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f522">🔢</span> Learn how to use Quadratic AI to generate datasets -></td><td></td><td></td><td></td></tr></tbody></table>

{% @arcade/embed url="<https://app.arcade.software/share/WmxlS1QtUZ0acwCuIBVJ>" flowId="WmxlS1QtUZ0acwCuIBVJ" %}

#### Generate code from scratch and edit existing code

* Develop entire models and analyses from scratch
* Build infinitely flexible charts without writing a line of code
* Debug and fix errors in existing code and formulas

Learn how to generate code using Quadratic AI ->

#### Generate datasets

* Create new datasets from scratch
* Augment and edit existing data
* Smart insert and relocate data via chat

Learn how to generate datasets using Quadratic AI ->

### Reference your data with @-mentions

Type `@` in the chat to reference sheets, tables, cell ranges, or connections. Mentions become pills in your message and give the AI full context on whatever you referenced — including your live database connections.

### Attach files

Attach PDFs, images, Excel, CSV, or Parquet files to the chat with the paperclip, by pasting, or by dragging them in. Spreadsheet files import directly; PDFs and images are read by the AI so you can extract exactly what you need. Learn more in Import PDFs and Import images.

### The AI asks before it guesses

When your request is ambiguous, Quadratic AI asks a clarifying multiple-choice question in the chat instead of guessing. Answer with a click — or pick "Other" and type your own answer — and it continues from there.

### Trace any value

Right-click a cell and choose **Trace value** to have the AI explain where a value comes from — the formula, code cell, table, or connection that produced it, and everything upstream of it.

### Pick your model

Choose the AI model that powers your chat from the model menu at the bottom of the chat panel. Claude Sonnet 5 is the default; additional models are available on paid plans. A context gauge next to the menu shows how much of the model's context window the current chat has used. Learn more in AI models.

### Memory

Quadratic AI remembers durable context at a personal, team, and connection level, so you don't repeat yourself between chats. Learn more in AI memory & context.

### Notifications

Working on something else while the AI runs? Quadratic can send a browser notification when a long-running response finishes while you're in another tab.


# Generate code

Build analyses from scratch or edit existing code.

Quadratic AI Chat can build whole analyses from scratch or edit and assist with existing code. Quadratic AI will default to Python, but you can ask for additional languages like JavaScript or Formulas.

## Insert and view code

Quadratic AI will default to inserting generated code. You can always view the code generated, make edits, move the code cell, or re-add the code cell.&#x20;

<figure><img src="/files/r04MkOFqz4AQKVWN4dEw" alt=""><figcaption></figcaption></figure>

## Build models and analytics workflows from scratch

The AI can see and understand the data in your sheet, allowing you to ask almost any question without needing to tell the AI any extra context. It will generate the analysis and automatically place the code in the sheet.&#x20;

Sometimes, the AI generates multiple code cells, data + code, or any possible combination that best answers your query.

<figure><img src="/files/lYIJnIzUXqqfj8fcMJig" alt=""><figcaption></figcaption></figure>

## Create charts

Create infinitely flexible charts with Quadratic AI. If you need to make edits, either do it directly in code or ask the AI to do it directly.

You can ask for edits like chart type, data used, titles, background colors, layouts, axes styling, etc. &#x20;

<figure><img src="/files/0ERMPYejR65neMTN2JMt" alt=""><figcaption></figcaption></figure>

## Fixing errors &#x20;

Quadratic AI is great at fixing errors in your code. Whenever there's an error, you can ask for the fix directly in chat or use the `Fix with AI` button. The AI will attempt to fix it and explain how it works. You can then either accept or reject the code change.

<figure><img src="/files/6YQrVMlOQTCm7GmFjpHm" alt=""><figcaption></figcaption></figure>


# Generate data

Insert data from AI into your sheets.

## Create datasets from scratch&#x20;

Generate entire datasets from scratch. The AI will automatically insert your data in the sheet, ready for you to perform your analysis.

Note, in the following example, the AI both creates a dataset from scratch and then augments the data in two separate steps:

1. Creates the data for the **top 10 countries by GDP**
2. Adds the **next 5 by GDP** to the existing dataset

<figure><img src="/files/T15G8yhZwscOxNU2Txjb" alt=""><figcaption></figcaption></figure>


# Import PDFs

Import data from your PDFs directly into a spreadsheet.

You can directly import PDFs to your spreadsheet using the AI chat.&#x20;

{% embed url="<https://demo.arcade.software/aOZQifiUrbKLZ29fBUD3?embed&embed_mobile=tab&embed_desktop=inline&show_copy_link=true>" %}

## Drag and drop or use the attach button

Drag the file directly into the AI chat to add it to context. Once inserted, you can ask any natural language question to extract what you'd like from the PDF.

<figure><img src="/files/EWpEucWkM3HPlJ6VPDHW" alt=""><figcaption></figcaption></figure>

Use the file picker to select your PDF.&#x20;

<figure><img src="/files/xardmd8S4Kti1aYh2pD8" alt=""><figcaption></figcaption></figure>


# Import images

Import images to Quadratic with AI.

You can directly import images to your spreadsheet using the AI chat.&#x20;

<figure><img src="/files/JcU4A5N4liVjlC2il5Ac" alt=""><figcaption></figcaption></figure>

## Drag and drop or use the attach button

Drag the file directly into the AI chat to add it to context. Once inserted, you can ask any natural language question to extract what you'd like from the image.

<figure><img src="/files/EWpEucWkM3HPlJ6VPDHW" alt=""><figcaption></figcaption></figure>

Use the file picker to select your image.&#x20;

<figure><img src="/files/xardmd8S4Kti1aYh2pD8" alt=""><figcaption></figcaption></figure>


# AI models

## AI models

Quadratic AI uses frontier models tuned for spreadsheet work. Pick a model from the model menu at the bottom of the AI chat panel.

### Available models

| Model                         | Notes                                                               | Availability |
| ----------------------------- | ------------------------------------------------------------------- | ------------ |
| **Claude Sonnet 5** (default) | Best balance of quality and speed for analysis, code, and data work | All plans    |
| **Claude Opus 4.8**           | Most capable model for complex, multi-step analyses                 | Paid plans   |
| **Claude Haiku 4.5**          | Fastest responses for lightweight edits and questions               | Paid plans   |
| **Gemini 3.5 Flash**          | Fast Google model                                                   | Paid plans   |
| **Gemini 3.1 Pro**            | Capable Google model                                                | Paid plans   |

Quadratic routes some background work (like PDF imports and prompt suggestions) to specialized models automatically — you don't need to configure anything.

### Context window indicator

Next to the model menu, a small gauge shows how much of the model's context window your current chat is using. Hover it for a breakdown across conversation, sheet data, memory, and tools. If a long chat approaches the limit, start a fresh chat for better response quality.

### Data handling

Every model provider Quadratic uses has a zero-day data retention policy. See AI security for details, or the self-hosting docs for Bring Your Own Model options.


# AI memory & context

## AI memory & context

Quadratic AI can remember durable context between chats so you don't repeat yourself. Memory lives at three levels: personal, team, and connection.

### Personal memory

**Settings > AI** holds your personal memory — preferences and facts included in every chat you start. For example: "I prefer charts with a dark theme" or "Fiscal year starts in February."

You can edit it directly, and the AI may propose updates as it learns your preferences. Version history lets you review and roll back changes.

### Team memory

Your team settings hold shared context for everyone on the team: business definitions, naming conventions, metric formulas, and domain knowledge. Team editors can update it.

Example: "ARR is calculated from the `subscriptions` table excluding trials. Our customer IDs look like CUST-XXXX."

### Connection context

Each database connection can carry its own context — schema quirks, which tables matter, what joins to use. The AI reads it whenever it writes SQL against that connection.

### How the AI uses memory

Memory is automatically included in AI chats. The AI can also update memory itself when you tell it something worth remembering ("remember that we report revenue in EUR") — updates are versioned, so you can always see what changed and revert.

{% hint style="info" %}
You can disable prompt storage entirely from your team's Settings page — see AI security.
{% endhint %}


# Security

How data is handled in Quadratic AI.

**Quadratic AI** takes opinionated choices on the best AI models for each prompt. A variety of model providers are used; each has a zero day data retention policy with Quadratic.

Quadratic is SOC-II and HIPAA certified, learn more [here.](https://trust.quadratichq.com/)

### Bring Your Own Model (BYOM)

With *Quadratic BYOM*, you can use your models in the self-hosted version of Quadratic, ensuring that data never leaves your servers or infrastructure. If interested, please [contact us](https://quadratichq.com/contact); we are looking for design partners to use our self-hosting and BYOM setups.

### Improve AI results

You can disable prompt storage from the Settings page of your team's dashboard.&#x20;

<figure><img src="/files/sn1OIVUqzHdIrNL4Q3ym" alt=""><figcaption></figcaption></figure>


# SQL - getting started

## SQL - getting started

Use SQL to create live connections from your spreadsheets to your databases.

Once established, you have a live connection that can be rerun, refreshed, read from, and written to your SQL database.

{% hint style="warning" %}
You can both read and write to your databases from Quadratic.
{% endhint %}

#### Supported connections

**Databases**: PostgreSQL, MySQL, MS SQL Server, Snowflake, BigQuery, CockroachDB, MariaDB, Supabase, Neon

**SaaS & analytics** (synced connections): Mixpanel, Google Analytics, QuickBooks

**Financial institutions**: bank accounts, brokerages, and credit cards via Plaid

**Anything with a REST API**: see Agent connections

#### Create a connection

Create an SQL connection by pressing `/` inside a cell. From here, you can access, add, or edit connections. Here are some helpful links to get started, or follow the [quick start guide](#quick-start) below.

You can also create a connection from the **Dashboard** > **Connections** under the **Team** menu.

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f418">🐘</span> Connect your PostgreSQL database -></td><td></td><td></td><td></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f42c">🐬</span> Connect your MySQL database -></td><td></td><td></td><td></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f570">🕰️</span> Request a data connection -></td><td></td><td></td><td></td></tr></tbody></table>

### Quick start

#### Create a connection

#### Step 1: press `/`

You can create data connections from either your spreadsheets or team's dashboard. From your sheet, press `/` to open the code selection menu. You'll see both your existing connections and the prompt to manage your connections (create, edit, delete). To get started with creating a connection, choose the **Manage connections** option.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FzPsTwAQS0IO8kXSUogyU%2FCleanShot%202024-07-17%20at%2014.03.17%402x.png?alt=media&#x26;token=24a8cb54-1268-4a1d-9c69-ff03c5047a28" alt="" width="375"><figcaption></figcaption></figure>

#### Step 2: pick the connection type

With the connection management screen open, you can edit an existing connection or create a new one. To create a new connection, select the connection type you want to create.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FJ2haE4X17IJqH02oxVFm%2FScreenshot%202024-07-24%20at%2012.34.54%E2%80%AFPM.png?alt=media&#x26;token=0cb73940-54e9-4224-a20c-092de362bb68" alt="SQL connection type"><figcaption><p>Select connection type screen</p></figcaption></figure>

#### Step 3: follow the instructions for creating your connection

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2F2pvbBh6NXidpRZsh0kjO%2FScreenshot%202024-07-24%20at%2012.37.17%E2%80%AFPM.png?alt=media&#x26;token=28001b29-66c4-4bfe-96b7-b8df8ff7710c" alt=""><figcaption><p>Create connection screen</p></figcaption></figure>

You can also follow this same create connection process from your team's dashboard under the connections section.

### Use connection

#### Step 1: use your connection

Once your connection has been made you can use your connection directly in the sheet. Open the code cell selection menu with `/` and select your database from the list - in this example it's named **Quadratic Postgres**.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FN1vGMfXGwUOFAYoB73Nl%2FCleanShot%202024-07-17%20at%2013.49.12%402x.png?alt=media&#x26;token=bddebef8-2f6e-4bd5-8b1d-2485a29a79ba" alt="" width="375"><figcaption></figcaption></figure>

You can now query your database from your newly opened SQL code editor. You can view the schema or open the AI assistant in the bottom.

The results of your SQL queries are returned to the sheet, with column 0, row 0 anchored to the cell location.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FbwUeXy7Ny0c61rUISEqg%2FCleanShot%202024-07-17%20at%2014.28.45%402x.png?alt=media&#x26;token=3e0c6ad8-64e1-42ad-9416-bc1bd9eb8d05" alt=""><figcaption></figcaption></figure>

You can read the data returned from queries in Python, Formulas, Javascript, etc.

Read and manipulate your data returned from SQL to summarize results, create charts, or anything else you might want to use your data for!

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f522">🔢</span> Learn about the SQL AI assistant -></td><td></td><td></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f513">🔓</span> Learn about permissions and security -></td><td></td><td></td></tr></tbody></table>

#### Synced connections

Mixpanel, Google Analytics, QuickBooks, and Plaid connections sync data into your sheet on a schedule rather than running live queries. Once connected, pick the datasets to sync and Quadratic keeps them refreshed automatically.

#### AI context for connections

Each connection can store AI context — notes about schemas, important tables, and join patterns. The AI assistant reads it every time it writes SQL for that connection, so set it up once and every query gets smarter.

### Helpful queries

If you need help generating queries, we recommend first trying the AI assistant in your Quadratic code editor - its outputs are very helpful with writing everything from the simplest to most complex SQL queries.

#### Read data into the spreadsheet <a href="#block-9871b7ac3fa341da856503066f6f8e8c" id="block-9871b7ac3fa341da856503066f6f8e8c"></a>

**Query all data from single table into the spreadsheet**

```sql
SELECT * FROM table_name
```

**Query a limited selection (100 rows) from single table into spreadsheet**

```sql
SELECT * FROM table_name 
LIMIT 100
```

**Query specific columns from single table into the spreadsheet**

```sql
SELECT column_name1, column_name2 
FROM table_name 
LIMIT 100
```

**Query all unique values in a column**

```sql
SELECT DISTINCT column_name1 
FROM table_name 
LIMIT 100
```

**Query data conditionally**

```sql
-- selects 3 specific columns from a table where column1 equals some value
SELECT column1, column2, column3
FROM table_name
WHERE column1 = 'some_value';
```

```sql
-- selects 3 specific columns from a table where column1 equals some value and column2 equals some value 
SELECT column1, column2, column3
FROM table_name
WHERE column1 = 'some_value' AND column2 = 5;
```

**Extra considerations**

You cannot do two queries at once in SQL in Quadratic. For example, you can not create a table and then query that table in the same SQL query.

There are some slight differences between SQL syntax across databases.

* In Postgres it is best practice use quotes around table names and column names.
* In MySQL it is best practice to use backticks around table names and column names.
* In MS SQL Server it is best practice to use double quotes around table names and column names.

Stuck? The AI assistant in your code editor is ready and waiting to help with your queries.


# Agent connections

## Agent connections

Agent connections let you pull data from third-party services — CRMs, payment processors, analytics tools, internal APIs, and more — directly into your spreadsheets. Unlike database connections, agent connections work with any service that exposes a REST API.

### Create an agent connection

1. In a sheet, press `/` in a cell and choose **Connections**, or go to **Dashboard > Connections** under the Team menu.
2. Pick a service from the catalog, or choose **Connect to anything** to set up a custom API.
3. The AI walks you through setup conversationally: it researches the service's API, proposes a connection plan, collects credentials, tests the connection, and finalizes it.

For services that use OAuth, you'll be redirected to sign in to the service and grant access. For API-key services, the AI asks for your key and stores it securely as a team secret.

{% hint style="info" %}
AI messages during agent connection setup are free — they don't count against your team's AI usage.
{% endhint %}

### Use an agent connection

Once created, agent connections can be used in two ways:

#### From AI chat

Mention the connection with `@` in the AI chat. The AI can query the connected service and combine its data with your sheet data.

#### From code cells

Agent connections are available from Python and JavaScript code cells. Requests run through Quadratic's authenticated proxy, which injects your stored credentials.

Reference team secrets with `{{SECRET_NAME}}` placeholders in URLs, headers, or request bodies — the actual values never appear in your code or your sheet:

```python
import requests
import pandas as pd

response = requests.get(
    'https://api.example.com/v1/customers',
    headers={'Authorization': 'Bearer {{EXAMPLE_API_KEY}}'}
)
df = pd.DataFrame(response.json()['data'])
df
```

### Team secrets

Secrets are managed in your team settings under **Secrets**. Each secret has a name and a value; the value is write-only after creation. Secrets are scoped to your team and only substituted for requests to the domains allowed by the connection.

### Permissions

Agent connections follow the same permission model as database connections: team editors can use connections in sheets; owners can create, edit, and remove them.


# PostgreSQL

Create your PostgreSQL connection.

You can create your PostgreSQL connection from either your team's dashboard or directly inside a spreadsheet. From the **Dashboard**, navigate to the **Connections** menu item. Or from inside a spreadsheet, press `/` and then Manage connections.&#x20;

## Connection details&#x20;

To connect to your PostgreSQL database, you'll need to enter the parameters in the **Team connections** modal:&#x20;

<figure><img src="/files/zAFJ1AvUPPR4ZQm9GcCv" alt=""><figcaption></figcaption></figure>

### Connection name

You can name your connection however you'd like. This is the display name for your connection across Quadratic and your team.&#x20;

```
example: Sales Database
```

### Host name&#x20;

This is the connection host, the direct connection to your database. It can be an IP address or URL. You might see a connection string that looks something like this:&#x20;

<pre><code><strong>postgres://default:xyz123@postgres-hostname.com:5432
</strong></code></pre>

The hostname is the IP address or URL. From the above connection string, this would be the host, which you would add to the **Hostname** field:

```
postgres-hostname.com
```

### Port number

This is the connection port; you might see it at the end of your hostname after the colon. It is typically a four-digit number. From the above example, add this number to the **Port** field. `5432` is the default port number for Postgres.

```
5432
```

### Database name

This is the name of the database you want to connect to. Your database can have multiple tables - be sure to use the **database name** and not the name of a table. Example database name:

```
postgres_db
```

### **Username**

This is an authentication detail that depends on your database account username. Some common defaults include `admin` or `default`. This information is in your database's authentication details, found in your database's authentication settings, or received from your database administrator. Example:

```
admin
```

### **Password**

This is the password you use to connect to your database. This information is in your database's authentication details, found in your database's authentication settings, or received from your database administrator. Example:

```
1234abcd
```

## IP allow-list

{% hint style="info" %}
You must whitelist both Quadratic IP addresses to make connections, which are:\
**44.240.255.40** \
**54.68.134.35**

*\* These IP addresses do not apply to self-hosted instances*&#x20;
{% endhint %}

If you need help setting up your data sources, [contact us](https://quadratichq.com/contact). Some extra resources and instructions vary depending on where your database lives.

[AWS](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-groups.html), [Azure](/), [GCP](https://cloud.google.com/firewall/docs/firewalls)

Once you've made your connection, you can use it directly in spreadsheets to read data to your spreadsheets. Learn how [here](/connections/sql-getting-started#step-1-use-your-connection).&#x20;


# MySQL

Create your MySQL connection.

You can create your MySQL connection from either your team's dashboard or directly from a spreadsheet. From the dashboard, navigate to the **Connections** page or from a spreadsheet press / and then **Manage connections**.&#x20;

## Connection details&#x20;

To connect to your MySQL database, you'll need to enter the parameters in the **Team connections** modal:&#x20;

<figure><img src="/files/wzkddRjpTNsPc9OsAz3I" alt=""><figcaption></figcaption></figure>

### Connection name

You can name your connection however you'd like. This is the display name for your connection across Quadratic and your team.&#x20;

```
example: Sales Database
```

### Host name&#x20;

This is the connection host, the direct connection to your database. It can be an IP address or URL. You might see a connection string that looks something like this:&#x20;

```
mysql://default:xyz123@mysql-hostname.com:5432
```

The hostname is the IP address or URL. From the above connection string, this would be the host, which you would add to the **Hostname** field:

```
mysql-hostname.com
```

### Port number

This is the connection port; you might see it at the end of your hostname after the colon. It is typically a four-digit number. From the above example, add this number to the **Port** field. `3306` is the default port number for MySQL.

```
3306
```

### Database name

This is the name of the database you want to connect to. Your database can have multiple tables - be sure to use the **database name** and not the name of a table. Example database name:

```
mysql_db
```

### **Username**

This is an authentication detail that depends on your database account username. Some common defaults include `admin` or `default`. This information is in your database's authentication details, found in your database's authentication settings, or received from your database administrator. Example:

```
admin
```

### **Password**

This is the password you use to connect to your database. This information is in your database's authentication details, found in your database's authentication settings, or received from your database administrator. Example:

```
1234abcd
```

## IP allow-list

{% hint style="info" %}
You must whitelist both Quadratic IP addresses to make connections, which are:\
**44.240.255.40** \
**54.68.134.35**

*\* These IP addresses do not apply to self-hosted instances*&#x20;
{% endhint %}

If you need help setting up your data sources, [contact us](https://quadratichq.com/contact). Some extra resources and instructions vary depending on where your database lives.

[AWS](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-groups.html), [Azure](/), [GCP](https://cloud.google.com/firewall/docs/firewalls)

Once you've made your connection, you can use it directly in spreadsheets to read data to your spreadsheets. Learn how [here](/connections/sql-getting-started#step-1-use-your-connection).&#x20;


# MS SQL Server

Create your MS SQL Server connection.

You can create your MS SQL Server connection from either your team's dashboard or directly from a spreadsheet. From the dashboard, navigate to the **Connections** page or from a spreadsheet press / and then **Manage connections**.&#x20;

## Connection details&#x20;

To connect to your MS SQL Server database, you'll need to enter the parameters in the **Team connections** modal:&#x20;

<figure><img src="/files/fYYxzveG9FLIzJgWSUZ7" alt=""><figcaption></figcaption></figure>

### Connection name

You can name your connection however you'd like. This is the display name for your connection across Quadratic and your team.&#x20;

```
example: Sales Database
```

### Host name&#x20;

This is the connection host, the direct connection to your database. It can be an IP address or URL. You might see a connection string that looks something like this:&#x20;

```
mysql://default:xyz123@mysql-hostname.com:5432
```

The hostname is the IP address or URL. From the above connection string, this would be the host, which you would add to the **Hostname** field:

```
mysql-hostname.com
```

### Port number

This is the connection port; you might see it at the end of your hostname after the colon. It is typically a four-digit number. From the above example, add this number to the **Port** field. `3306` is the default port number for MySQL.

```
3306
```

### Database name

This is the name of the database you want to connect to. Your database can have multiple tables - be sure to use the **database name** and not the name of a table. Example database name:

```
mysql_db
```

### **Username**

This is an authentication detail that depends on your database account username. Some common defaults include `admin` or `default`. This information is in your database's authentication details, found in your database's authentication settings, or received from your database administrator. Example:

```
admin
```

### **Password**

This is the password you use to connect to your database. This information is in your database's authentication details, found in your database's authentication settings, or received from your database administrator. Example:

```
1234abcd
```

## IP allow-list

{% hint style="info" %}
You must whitelist both Quadratic IP addresses to make connections, which are:\
**44.240.255.40** \
**54.68.134.35**

*\* These IP addresses do not apply to self-hosted instances*&#x20;
{% endhint %}

If you need help setting up your data sources, [contact us](https://quadratichq.com/contact). Some extra resources and instructions vary depending on where your database lives.

[AWS](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-groups.html), [Azure](/), [GCP](https://cloud.google.com/firewall/docs/firewalls)

Once you've made your connection, you can use it directly in spreadsheets to read data to your spreadsheets. Learn how [here](/connections/sql-getting-started#step-1-use-your-connection).&#x20;


# Supabase

Create your Supabase connection.

You can create your Supabase connection from either your team's dashboard or directly inside a spreadsheet. From the **Dashboard**, navigate to the **Connections** menu item. Or from inside a spreadsheet, press `/` and then Manage connections.&#x20;

## Connection details&#x20;

To connect to your Supabase database, you'll need to enter the parameters in the **Team connections** modal:&#x20;

<figure><img src="/files/OtOo25on1j3UUw4LdIH2" alt=""><figcaption></figcaption></figure>

## Credentials

To find your Supabase credentials, click the connect button at the top of your Supabase dashboard. &#x20;

<figure><img src="/files/QOYacTRPdwj081I9ujlI" alt=""><figcaption></figcaption></figure>

Once in this screen, you must use the Session Pooler options. Press view parameters and use the associated details to create the connection in Quadratic.

<figure><img src="/files/MREgQcN56xiTx3BC8XqJ" alt=""><figcaption></figcaption></figure>

You can ignore the "pool\_mode" parameter. All others should be copied exactly as they appear into Quadratic.&#x20;

## IP allow-list

{% hint style="info" %}
You may need to whitelist both Quadratic IP addresses to make connections, which are:\
**44.240.255.40** \
**54.68.134.35**

*\* These IP addresses do not apply to self-hosted instances*&#x20;
{% endhint %}

If you need help setting up your data sources, [contact us](https://quadratichq.com/contact). Some extra resources and instructions vary depending on where your database lives.

Once you've made your connection, you can use it directly in spreadsheets to read data to your spreadsheets. Learn how [here](/connections/sql-getting-started#step-1-use-your-connection).&#x20;


# BigQuery

How to connect to BigQuery from Quadratic.

You can create your BigQuery connection from either your team's dashboard or directly inside a spreadsheet. From the **Dashboard**, navigate to the **Connections** menu item. Or from inside a spreadsheet, press `/` and then Manage connections.&#x20;

## Connection details&#x20;

To connect to your BigQuery database, you'll need to enter the parameters in the **Team connections** modal:&#x20;

<figure><img src="/files/08zuiaq0dWRhABCyYxqm" alt=""><figcaption></figcaption></figure>

### Connection name

You can name this whatever you'd like. This is the name you'll see in Quadratic any time you use the connection.&#x20;

### Project ID

This can be found in the project selector at the top of our Google Cloud Console. Do not mistake the project name for the ID; they can be the same but are often different. Make sure you select the ID from the list.&#x20;

<figure><img src="/files/7PRRl6PiX4jWKuOS6O7D" alt=""><figcaption></figcaption></figure>

### Dataset&#x20;

Datasets can be found inside your BigQuery instance in the sidebar.&#x20;

<figure><img src="/files/7xhMAFYiVGArflrOClfj" alt=""><figcaption></figcaption></figure>

### Service account configuration

Acquire your service account JSON key from the keys section under service accounts in Google Cloud Console.&#x20;

<figure><img src="/files/sCu7ogCO3yXvHqTn8wrX" alt=""><figcaption></figcaption></figure>

### IP allow-list

{% hint style="info" %}
You may need to whitelist both Quadratic IP addresses to make connections, which are:\
**44.240.255.40** \
**54.68.134.35**

*\* These IP addresses do not apply to self-hosted instances*&#x20;
{% endhint %}

If you need help setting up your data sources, [contact us](https://quadratichq.com/contact). Some extra resources and instructions vary depending on where your database lives.&#x20;


# Snowflake

Create your Snowflake connection.

You can create your Snowflake connection from either your team's dashboard or directly from a spreadsheet. From the dashboard, navigate to the **Connections** page or from a spreadsheet press / and then **Manage connections**.&#x20;

## Connection details&#x20;

To connect to your Snowflake database, you'll need to enter the parameters in the **Team connections** modal:&#x20;

<figure><img src="/files/u63ohVp36oKPmzRZy2At" alt=""><figcaption></figcaption></figure>

These details can all easily be found in Snowflake by navigating to the "Connect a tool" page.&#x20;

<figure><img src="/files/wbcWog1iaPfsUDmZeTII" alt=""><figcaption></figcaption></figure>


# CockroachDB

## CockroachDB

You can create your CockroachDB connection from either your team's dashboard or directly inside a spreadsheet. From the Dashboard, navigate to the Connections menu item. Or from inside a spreadsheet, press `/` and then Manage connections.

CockroachDB is PostgreSQL-compatible, so the connection details mirror a Postgres connection.

### Connection details

To connect to your CockroachDB cluster, enter the following parameters in the Team connections modal:

* **Connection name** — the display name for your connection across Quadratic and your team
* **Hostname** — your cluster's host (for CockroachDB Cloud, copy it from the cluster's **Connect** dialog)
* **Port** — typically `26257`
* **Database** — the database name
* **Username / Password** — credentials with the access you want available in Quadratic

CockroachDB Cloud clusters require SSL; Quadratic connects securely by default.

### IP allow-list

{% hint style="info" %}
You must whitelist both Quadratic IP addresses to make connections, which are:\
44.240.255.40\
54.68.134.35

* These IP addresses do not apply to self-hosted instances
  {% endhint %}

Once you've made your connection, you can use it directly in spreadsheets to read data to your spreadsheets. Learn how in SQL - getting started.


# MariaDB

## MariaDB

You can create your MariaDB connection from either your team's dashboard or directly inside a spreadsheet. From the Dashboard, navigate to the Connections menu item. Or from inside a spreadsheet, press `/` and then Manage connections.

MariaDB connections use the same details as MySQL.

### Connection details

To connect to your MariaDB database, enter the following parameters in the Team connections modal:

* **Connection name** — the display name for your connection across Quadratic and your team
* **Hostname** — your database's host, an IP address or URL
* **Port** — typically `3306`
* **Database** — the database name
* **Username / Password** — credentials with the access you want available in Quadratic

### IP allow-list

{% hint style="info" %}
You must whitelist both Quadratic IP addresses to make connections, which are:\
44.240.255.40\
54.68.134.35

* These IP addresses do not apply to self-hosted instances
  {% endhint %}

Once you've made your connection, you can use it directly in spreadsheets to read data to your spreadsheets. Learn how in SQL - getting started.


# Neon

## Neon

You can create your Neon connection from either your team's dashboard or directly inside a spreadsheet. From the Dashboard, navigate to the Connections menu item. Or from inside a spreadsheet, press `/` and then Manage connections.

### Connection details

To connect to your Neon database, enter the following parameters in the Team connections modal. You can find them all in your Neon project's connection details:

* **Connection name** — the display name for your connection across Quadratic and your team
* **Hostname** — your Neon endpoint host (looks like `ep-xxxx.region.aws.neon.tech`)
* **Port** — `5432`
* **Database** — the database name
* **Username / Password** — credentials with the access you want available in Quadratic

Neon endpoints enforce SSL; Quadratic connects securely by default.

### IP allow-list

{% hint style="info" %}
If your Neon project uses IP Allow, add both Quadratic IP addresses:\
44.240.255.40\
54.68.134.35

* These IP addresses do not apply to self-hosted instances
  {% endhint %}

Once you've made your connection, you can use it directly in spreadsheets to read data to your spreadsheets. Learn how in SQL - getting started.


# Google Analytics

<figure><img src="/files/Kj8y6WXHGIACNyMyfTDH" alt=""><figcaption></figcaption></figure>

### How to find Property ID

To find your Google Analytics Property ID:

1. Sign in to your [Google Analytics account](https://analytics.google.com/).
2. Select the Admin gear icon located at the bottom left of the screen.
3. In the "Property settings" section, select "Property details".
4. The Property ID is displayed on the top right of the screen. It is a purely numeric identifier for the entire property container (e.g., `123456789`).

### How to find service account JSON

1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
2. Create a new project or select an existing project.
3. Navigate to the "IAM & Admin" section and select "Service Accounts."
4. Click "Create Service Account."
5. Enter a Service Account name and description, then click "Create."
6. Assign roles as needed, such as "Viewer" for read-only access.
7. Click "Done" after configuring roles.
8. In the "Actions" column for your service account, click the three-dot menu and select "Manage keys."
9. Click "Add Key," choose "JSON," and click "Create."
10. Download the JSON file to your computer. This file contains the credentials for your service account.

### How to enable API access

1. Open [Google Cloud Console](https://console.cloud.google.com/) and select the GCP project that owns your service account.
2. Go to APIs & Services → Library.
3. Search for "Google Analytics Data API" and click Enable.
4. (Recommended) Also enable "Google Analytics Admin API" for metadata access.
5. Confirm both APIs appear under APIs & Services → Enabled APIs for the same project.

### How to grant the Service Account access in GA4

1. Copy the client\_email value from your service account JSON (ends with .iam.gserviceaccount.com).
2. Open [Google Analytics](https://analytics.google.com/) and navigate to Admin for the correct GA4 property.
3. Select Property access management.
4. Click "Add users" and paste the service account email.
5. Assign the Editor role (recommended for setup; you can downgrade to Viewer later).
6. Save changes.

Watch this video for a detailed walk-through of the setup process:

{% embed url="<https://www.youtube.com/watch?v=FJvcuvSFTtM>" %}


# Mixpanel

Connect to your Mixpanel data for advanced product analytics use-cases.

Quadratic allows a Mixpanel connection that will enable using rich spreadsheet AI to work with your Mixpanel data in a variety of ways. Build custom analysis, visualizations, and more rich analytics with the data that lives in your Mixpanel.&#x20;

### Creating connection

Once in the connection creation screen, you'll see two options that need retrieved from your Mixpanel account: Project ID, API Secret.&#x20;

<figure><img src="/files/ZvLsdNALOjcsLzgwiRjh" alt=""><figcaption></figcaption></figure>

Both details can be found in your Mixpanel account by navigating to your team's Project Settings page.&#x20;

<figure><img src="/files/3hU54hevwejxvk0SzSWv" alt=""><figcaption></figcaption></figure>

Access the Project ID and Secret Key you need from the Project Settings page.&#x20;

<figure><img src="/files/ghzZBlbt5HiGQZ3ie6UF" alt=""><figcaption></figcaption></figure>

Once ready, enter your details and create your connection.&#x20;


# Parametrize queries

Insert data from sheet into your queries with parametrized queries.

### Parametrized reference

You can reference sheet data in SQL. Use {{}} notation for references. You can only reference single cells at a time.&#x20;

```sql
SELECT * FROM {{A1}} WHERE salary > {{A4}}
```

Note in the following example where the query output changes based on what table is selected in cell B1.&#x20;

### Parametrize a reference from another sheet

```sql
select abc from def where ghi="{{'Sheet_name'!H3}}"
```

<figure><img src="/files/qSIt3zJ0uZtjLUqq9Yva" alt=""><figcaption></figcaption></figure>


# SQL AI assistant

Use the AI assistant to drastically speed up your SQL efforts.

Writing SQL is now accessible for anyone who can sufficiently describe in language the data they would like to retrieve.&#x20;

The AI assistant understands your database schema so you can ask specific questions about your data and get the SQL output you want. The AI never reads your database data, only the database schema (table names, column names, column data types)

To access the AI assistant, open the SQL code editor from your SQL connection by pressing `/` and then open the AI assistant tab.&#x20;

<figure><img src="/files/u8pvYsd6aZTHF5nY5h43" alt=""><figcaption></figcaption></figure>


# Security

Details for permissions and security of connections.

## User permissions&#x20;

User permissions are based on a team member's credentials. To edit team member permissions, visit the members page in your team's dashboard.

**Owner:** has access to create, delete, use, and edit all connections

**Editor:** has access to use all connections but cannot create, delete, or edit connections

**Viewer:** has no access to connections but can view data displayed from connections&#x20;

## Database access

By default, Quadratic allows all the queries and table access granted by the database user credentials. This means if the database user credentials used in the connection are granted full permissions, then Quadratic users of that connection will have the same permissions. Only make database connections that align with what access you want your users to have.&#x20;

{% hint style="warning" %}
If you want your users to have read-only access to Quadratic, consider creating read-only database users for connections. Only create connections with access to the tables you want your users to have.&#x20;
{% endhint %}

## IP allow-list

{% hint style="info" %}
You must whitelist both Quadratic IP addresses to make connections, which are:\
**44.240.255.40** \
**54.68.134.35**
{% endhint %}

If you need help whitelisting, you can [contact us](https://quadratichq.com/contact) to help set up your data sources. Here are some extra resources; instructions vary depending on where your database lives.

[AWS](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-security-groups.html), [Azure](/), [GCP](https://cloud.google.com/firewall/docs/firewalls)

## Data security and compliance

Quadratic is partnered with Vanta for our security and data controls. You can view our live trust center here: <https://trust.quadratichq.com/>

### Certifications

Quadratic is currently in the audit period for SOC II and HIPAA compliance.&#x20;

Need more details about our security, or are you seeking a self-hosted option? Feel free to [contact us](https://quadratichq.com/contact).


# API requests

For any data sources you can't connect directly to with SQL, we recommend using API requests via Python or JavaScript. Alternatively, you can [import Excel files](/import-data/import-excel-files), [drag and drop CSVs](/import-data/drag-and-drop-.csv), or [let us know if you need help](https://quadratichq.com/contact) with the specific data source you're trying to connect.&#x20;

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f40d">🐍</span> Make an API request from Python -></td><td></td><td></td><td><a href="/pages/gonxIBjOhzIg4gQLfvxq">/pages/gonxIBjOhzIg4gQLfvxq</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f7e8">🟨</span> Make an API request from JavaScript -></td><td></td><td></td><td><a href="/pages/v9OuRPCASJZxnZu2fsMr">/pages/v9OuRPCASJZxnZu2fsMr</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f7e9">🟩</span> Import Excel files -></td><td></td><td></td><td><a href="/pages/1RtflwVfZutTB9pRxEWZ">/pages/1RtflwVfZutTB9pRxEWZ</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f4c1">📁</span> Drag and drop .CSV files -></td><td></td><td></td><td><a href="/pages/VEoSfKAoFlsspnVMA5Mg">/pages/VEoSfKAoFlsspnVMA5Mg</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f5c4">🗄️</span> Drag and drop .Parquet files -></td><td></td><td></td><td><a href="/pages/qyg2fTsqHd04LwPB4IXF">/pages/qyg2fTsqHd04LwPB4IXF</a></td></tr></tbody></table>


# Request a connection

Request a connection you'd like to see built in Quadratic.

The order in which we build new connections is based on user requests. [Contact us](https://quadratichq.com/contact) to let us know what connection you'd like built next.&#x20;


# Connect via MCP

## Connect via MCP

Quadratic provides a hosted Model Context Protocol (MCP) server that lets AI assistants read and write your Quadratic spreadsheets. Connect it to any MCP-compatible client to create files, import data, run code, and build analyses from outside the app.

### Connect

Add the Quadratic MCP server to your AI tool of choice:

```
https://mcp.quadratichq.com/
```

For example, in Cursor or Claude, add a remote MCP server with the URL above. The first time you use it, you'll be prompted to sign in to your Quadratic account through a browser-based flow.

### What it can do

The MCP server exposes tools for working with your files:

* **auth** — sign in and out of your Quadratic account
* **files\_read** — list your files and get file metadata
* **files\_write** — create, open, and close files
* **read\_data** — read cell data, code cell contents, table outlines, database schemas, dependencies, and search text
* **write\_data** — set cell values, write Python/JavaScript/SQL/Formula code cells, import CSV/Excel/Parquet files, create data tables, and more

Read and write tools support batching, so AI clients can perform many operations efficiently in a single call.

### Example prompts

Once connected, try prompts like:

* "Create a new Quadratic file and import this CSV."
* "Read the table in my Sales file and summarize revenue by region."
* "Add a Python chart to my Q3 report file."

### Security

The MCP server acts on your behalf using your Quadratic account. It has access to the same files and connections you do. Sessions are authenticated via OAuth and can be revoked by logging out.

{% hint style="info" %}
Prefer calling Quadratic from your own code instead of an AI agent? Use the Developer API.
{% endhint %}


# Developer API

## Developer API

The Quadratic Developer API lets you programmatically create and edit spreadsheets: write cells, run code, import files, manage sheets, and read results. Use it to integrate Quadratic into data pipelines, internal tools, and scheduled jobs.

### Authentication

Create an API token from your team settings under **API tokens**. Tokens are team-scoped and shown once at creation — store them like passwords.

Pass the token in the Authorization header:

```
Authorization: Bearer qdx_live_...
```

### What you can do

| Area                | Capabilities                                                                                        |
| ------------------- | --------------------------------------------------------------------------------------------------- |
| Files               | List, get, create, and import files                                                                 |
| Sheets              | Add, rename, delete, duplicate, reorder, color, freeze panes                                        |
| Cells               | Get/set values, code cells (Python, JavaScript, SQL, Formula), rerun code, formats, borders, merges |
| Tables              | Create data tables, convert ranges, column settings                                                 |
| Rows & columns      | Insert, delete, resize                                                                              |
| Validations         | List, add, remove                                                                                   |
| Conditional formats | Get and update                                                                                      |
| Search & context    | File outline, cell data, text search                                                                |
| History             | Undo, redo                                                                                          |
| Connections         | List connections and fetch database schemas                                                         |

Files cannot be deleted through the API — deletion stays in the app.

### Client libraries

Generated TypeScript and Python client libraries are available, so you can call the API with typed methods instead of raw HTTP.

### Developer API vs. MCP

Both surfaces expose the same underlying operations. Use the **Developer API** for deterministic automation from your own code; use the MCP server when an AI agent should operate Quadratic on your behalf.


# Getting started

Get started writing rich Python code inside the spreadsheet.

Python is a first-class citizen in Quadratic that integrates seamlessly with the spreadsheet. Below is a quick start to get going with Python in Quadratic.&#x20;

## Quick start

1. Select the cell where you want to write Python code.&#x20;

<figure><img src="/files/kObSJ7iiZYOc3Pn6I8fG" alt=""><figcaption></figcaption></figure>

1. Press `/` to bring up the cell type menu. Select Python.

<figure><img src="/files/Ga9ZoC6TYiPuKhFD0XE7" alt=""><figcaption></figcaption></figure>

3. Write Python in the code editor. The last line of code is returned to the sheet at the cell position.

<figure><img src="/files/i09CkH6rsI0ZPayLrN64" alt=""><figcaption></figcaption></figure>

## Learn more

The following is a list of essential information for learning how Python works in Quadratic.&#x20;

1. [Reference cells](/python/reference-cells) - get data from the sheet into Python with cell references&#x20;
2. [Return data to the sheet](/python/return-data-to-the-sheet) - return Python outputs from code to the sheet&#x20;
3. [Import packages](/python/packages) - import packages for use in your Python code&#x20;
4. [Make API requests](/python/make-an-api-request) - use the Requests library to query APIs
5. [Visualize data](/python/charts-visualizations) - turn your data into beautiful charts and graphs with Plotly&#x20;

The most common starting point is [learning how to reference spreadsheet cells from Python in Quadratic.](/python/reference-cells)&#x20;

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f449">👉</span> <strong>Reference cells in the sheet from Python</strong></td><td></td><td><a href="/pages/JvPShV6XX5kbDAKxqszy">/pages/JvPShV6XX5kbDAKxqszy</a></td></tr><tr><td><p></p><p>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f4e5">📥</span> <strong>Display data in the sheet from Python</strong></p></td><td></td><td></td><td><a href="/pages/BHGbZPBfj9EfsLlCvLUg">/pages/BHGbZPBfj9EfsLlCvLUg</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f4c8">📈</span> <strong>Ingest data using Python</strong> </td><td></td><td><a href="/pages/gonxIBjOhzIg4gQLfvxq">/pages/gonxIBjOhzIg4gQLfvxq</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f4ca">📊</span> <strong>Visualize data using Python</strong> </td><td></td><td><a href="/pages/EpwLo8u7EKuJfPSI4Riy">/pages/EpwLo8u7EKuJfPSI4Riy</a></td></tr><tr><td><br>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f4e6">📦</span> <strong>Use Python packages in Quadratic</strong> </td><td></td><td></td><td><a href="/pages/ivx3NplMmQtR5f5McFGc">/pages/ivx3NplMmQtR5f5McFGc</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f9f9">🧹</span> <strong>Clean data using Python</strong> </td><td></td><td><a href="/pages/HWN2jn8RT78M3ZKrVUZx">/pages/HWN2jn8RT78M3ZKrVUZx</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f3a5">🎥</span> <strong>Manipulate data using Python</strong> </td><td></td><td><a href="/pages/Q2pZJ7AxFdq9NPKbgHPX">/pages/Q2pZJ7AxFdq9NPKbgHPX</a></td></tr></tbody></table>


# Reference cells

Reference cells from Python.

In Quadratic, [reference tables](#block-9871b7ac3fa341da856503066f6f8e8c) and named outputs from Python for simplest references, [reference individual cells](#block-9871b7ac3fa341da856503066f6f8e8c) from Python for single values or [reference a range of cells](#block-aea52dff303745f6b08db8ab3b24c4f6) for multiple values.&#x20;

## Referencing tables (and named outputs)  <a href="#block-9871b7ac3fa341da856503066f6f8e8c" id="block-9871b7ac3fa341da856503066f6f8e8c"></a>

Much of Quadratic's data is formatted in Data Tables for ease of use. Data Tables also make references more straightforward. To reference a table you can use `q.cells` which will bring the table into a DataFrame.&#x20;

```python
# Note: uses same table reference style as Formulas
# References entire table, including headers 
df = q.cells("Table1[#ALL]")

# Reads the values in Table1 and places them into variable df
# Note: this only retrieves the values, not the column names/headers
df_values = q.cells("Table1")

# Get a single column out of table into DataFrame
# Note: this only retrieves the column's data, not the header
df_column = q.cells("Table1[column_name]")

# Creates an empty DataFrame with just the headers as column names of the table referenced
df_headers = q.cells("Table1[#HEADERS]")

# Reference a range of columns from a table
df_columns = q.cells("Table1[[Column 1:Column 3]]")
```

All code outputs are also named and in tables by default; they can be referenced in the same fashion, using their names. Wherever possible you are encouraged to perform references using tables for simplest possible user experience.&#x20;

## Referencing individual cells <a href="#block-9871b7ac3fa341da856503066f6f8e8c" id="block-9871b7ac3fa341da856503066f6f8e8c"></a>

To reference an individual cell, use the global function `q.cells` which returns the cell value.

<pre class="language-python"><code class="lang-python"><strong># NOTE: uses the same A1 notation as Formulas
</strong><strong># Reads the value in cell A1 and places in variable x 
</strong><strong>x = q.cells("A1")
</strong></code></pre>

You can reference cells and use them directly in a Pythonic fashion.&#x20;

```python
q.cells("A1") + q.cells("A2") # Adds the values in cells A1 and A2 
```

Any time cells dependent on other cells update, the dependent cell will also update. This means your code will execute in one cell if it is dependent on another. This is the behavior you want in almost all situations, including user inputs in the sheet that cause calculation in a Python cell.&#x20;

## Referencing a range of cells <a href="#block-aea52dff303745f6b08db8ab3b24c4f6" id="block-aea52dff303745f6b08db8ab3b24c4f6"></a>

To reference a range of cells, use the same global function `q.cells()` which returns a [Pandas DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html).

<pre class="language-python"><code class="lang-python"><strong>q.cells("A1:A5") # Returns a 1x5 DataFrame spanning from A1 to A5
</strong><strong>
</strong><strong>q.cells("A1:C7") # Returns a 3x7 DataFrame spanning from A1 to C7
</strong><strong>
</strong><strong>q.cells("A") # Returns all values in column A into a single-column DataFrame
</strong><strong>
</strong><strong>q.cells("A:C") # Returns all values in columns A to C into a three-column DataFrame
</strong><strong>
</strong>q.cells("A5:A") # Returns all values in column A starting at A5 and going down

q.cells("A5:C") # Returns all values in column A to C, starting at A5 and going down

q.cells("A1:1") # Returns all values in row 1, starting at column A

q.cells("C5:5") # Returns all values in row 5, starting at column C
</code></pre>

If the first row of cells is a header, you should set `first_row_header` as an argument. This makes the first row of your DataFrame the column names, otherwise will default to integer column names as 0, 1, 2, 3, etc.

Use first\_row\_header when you have column names that you want as the header of the DataFrame. This should be used very commonly. You can tell when a column name should be a header when the column name describes the data below.&#x20;

```python
# first_row_header=True will be used any time the first row is the intended header for that data.
q.cells("A1:B9", first_row_header=True) # returns a 2x9 DataFrame with first rows as DataFrame headers
```

## Referencing another sheet

To reference another sheet's table, individual cells, or range of cells use the following:&#x20;

<pre class="language-python"><code class="lang-python"><strong># Use the sheet name as an argument for referencing range of cells 
</strong><strong>q.cells("'Sheet_name_here'!A1:C9")
</strong><strong>
</strong><strong># For individual cell reference 
</strong>q.cells("'Sheet_name_here'!A1")

# Since tables are global to a file, they can be referenced across sheets without defining sheet name
q.cells("Table1")
</code></pre>

## Unbounded references

### Unbounded column references

To reference all the data in a column or set of columns without defining the range, use the following syntax.&#x20;

Unbounded column references span from the row set (row 1 if not defined) to wherever the content in that column ends. Ranged references are always returned as DataFrames. Wherever gaps in data exist, `None` is filled in instead.

<pre class="language-python"><code class="lang-python"># references all values in the column from row 1 to the end of the content 
<strong>q.cells("A") # returns all the data in the column starting from row 1 to end of data 
</strong>
q.cells("A:D") # returns all the data in columns A to D starting from row 1 to end of data in longest column

q.cells("A5:A") # returns all values from A5 to the end of the content in column A 

q.cells("A5:C") # returns all values from A5 to end of content in C

q.cells("A:C", first_row_header=True) # same rules with first_row_header apply 

q.cells("'Sheet2'!A:C", first_row_header=True) # same rules to reference in other sheets apply
</code></pre>

### Unbounded row references

To reference all the data in a row or set of rows without defining the range, use the following syntax.&#x20;

Row references span from the row set to wherever the content in that row ends.&#x20;

```python
# Returns all values in Row 1
q.cells("1") 

# Returns all values in rows 1 to 3 
q.cells("1:3") 

# Returns all values in Row 1 starting at column A
q.cells("A1:1")

# Returns all values in Row 1 starting at column C
q.cells("C1:1")

# Returns all values in Row 3 starting at column A
q.cells("A3:3")

# Returns all values in Row 3 starting at column C 
q.cells("C3:3")
```

## Relative vs absolute references

By default when you copy paste a reference it will update the row reference unless you use `$` notation in your references.&#x20;

```python
# Copy pasting this one row down will change reference to A2
q.cells("A1")

# Copy pasting this one row down will keep reference as A1
q.cells("A$1")

# Example using ranges - row references will not change
q.cells("A$1:B$20")

# Only A reference will change when copied down
q.cells("A1:B$20")
```


# Return data to the sheet

Return the data from your Python code to the spreadsheet.

Quadratic is built to seamlessly integrate Python to the spreadsheet. This means being able to manipulate data in code and very simply output that data into the sheet.&#x20;

By default, the last line of code is output to the spreadsheet. This should be one of the five basic types:&#x20;

1. [Single value](#id-1.-single-value): for displaying the single number result of a computation&#x20;
2. [List of values:](#id-2.-list-of-values) for displaying a list of values from a computation
3. [DataFrame:](#id-3.-dataframe) for displaying the workhorse data type of Quadratic
4. [Chart:](#id-4.-charts) for displaying Plotly charts in Quadratic
5. [Function outputs:](#returning-function-outputs-to-the-sheet) return the results of functions to the sheet

All code outputs by default are given names that can be referenced, regardless of their return type.&#x20;

{% hint style="info" %}
You can expect to primarily use DataFrames as Quadratic is heavily built around Pandas DataFrames due to widespread Pandas adoption in almost all data science communities!
{% endhint %}

## 1. Single Value

Note the simplest possible example, where we set `x = 5` and then return `x` to the spreadsheet by placing `x` in the last line of code.

```python
# create variable 
x = 5 

# last line of code gets returned to the sheet, so x of value 5 gets returned
x
```

<figure><img src="/files/xlyqIhf0eEHrg0pLQOU7" alt=""><figcaption></figcaption></figure>

## 2. List of values&#x20;

Lists can be returned directly to the sheet. They'll be returned as tables with default column headings. You can edit or remove those headers in the table menu. &#x20;

<pre class="language-python"><code class="lang-python"><strong># create a list that has the numbers 1 through 5 
</strong><strong>my_list = [1, 2, 3, 4, 5]
</strong>
# returns the list to the spreadsheet 
my_list
</code></pre>

<figure><img src="/files/qATqttljwuE4YnL3HtGU" alt=""><figcaption></figcaption></figure>

## 3. DataFrame

You can return your DataFrames directly to the sheet by putting the DataFrame's variable name as the last line of code. DataFrames are returned to the sheet as Tables. The DataFrame's column names will be returned to the sheet as table headers.&#x20;

```python
# import pandas 
import pandas as pd
 
# create some sample data 
data = [['tom', 30], ['nick', 19], ['julie', 42]]
 
# Create the DataFrame
df = pd.DataFrame(data, columns=['Name', 'Age'])
 
# return DataFrame to the sheet
df
```

<figure><img src="/files/3TgdmzfsRhiRrnf4wUVx" alt=""><figcaption></figcaption></figure>

Note that if your DataFrame has an index it will not be returned to the sheet. If you want to return the index to the sheet use the following code:

<pre class="language-python"><code class="lang-python"><strong># use reset_index() method where df is the dataframe name
</strong><strong>df.reset_index()
</strong></code></pre>

An example of when this is necessary is any time you use the describe() method in Pandas. This creates an index so you'll need to use reset\_index() if you want to correctly display the index in the sheet when you return the DataFrame.&#x20;

## 4. Charts

Build your chart and return it to the spreadsheet by using the `fig` variable name or `.show()`

```python
# import plotly
import plotly.express as px

# replace this df with your data
df = px.data.gapminder().query("country=='Canada'")

# create your chart type, for more chart types: https://plotly.com/python/
fig = px.line(df, x="year", y="lifeExp", title='Life expectancy in Canada')

# display chart, alternatively can just put fig without the .show()
fig.show()
```

<figure><img src="/files/wn0QT1rMkad8NHvPoxw9" alt=""><figcaption></figcaption></figure>

## 5. Function outputs

You cannot use the `return` keyword to return data to the sheet, as that keyword only works inside of Python functions. Here is an example of using a Python function to return data to the sheet.&#x20;

```python
def do_some_math(x): 
    return x+1

# returns the result of do_some_math(), which in this case is 6 
do_some_math(5)
```

<figure><img src="/files/tGztPNXnH6j0qWG7Z7Os" alt=""><figcaption></figcaption></figure>

**Note:** Quadratic returns can only support a single variable being returned. For example, if I want to return a list I must return a variable that is a single list. I can not try to return a list by attempting to return multiple variables, e.g. I can not try `x, y` as a return statement. One must combine x and y into a single variable and return that single variable.


# Packages

Using and installing Python packages.

## Default Packages

Many libraries are included by default, here are some examples:

* Pandas (<https://pandas.pydata.org/>)
* NumPy (<https://numpy.org/>)
* SciPy (<https://scipy.org/>)

Default packages can be imported like any other native Python package.

```python
import pandas as pd
import numpy as np 
import scipy
```

[Micropip](https://micropip.pyodide.org/en/stable/project/api.html) can be used to install additional Python packages that aren't automatically supported (and their dependencies).

```python
import micropip

# `await` is necessary to wait until the package is available
await micropip.install("faker")

# Import installed package
from faker import Faker

# Use the package!
fake = Faker()
fake.name()
```

This only works for packages that are either pure Python or for packages with C extensions that are built in Pyodide. If a pure Python package is not found in the Pyodide repository, it will be loaded from PyPI. [Learn more about how packages work in Pyodide](https://pyodide.org/en/stable/usage/loading-packages.html#loading-packages).


# Make an API request

Get the data you want, when you want it.

API requests are made seamless in Quadratic by allowing you to use Python and then display the result of the request directly to the sheet.&#x20;

## Query API - GET request

Let's break our GET request down into a few different pieces.&#x20;

Import the basic requests library you're familiar with, query the API, and get the data into a Pandas DataFrame.&#x20;

<pre class="language-python"><code class="lang-python"><strong># Imports
</strong><strong>import requests
</strong>import pandas as pd

# Request
response = requests.get('your_API_url_here')

# JSON to DataFrame
df = pd.DataFrame(response.json())

# Display DataFrame in the sheet 
df
</code></pre>

## **Query API - POST request**

```python
import requests

# API url
url = 'your_API_url_here'
# API call body 
obj = {'somekey': 'somevalue'}

# create request 
x = requests.post(url, json = myobj)

# return the API response to the sheet
x.text
```

## **Going from CSV to DataFrame**&#x20;

Bringing your CSV to Quadratic is as simple as a drag and drop. Once your CSV is in the spreadsheet, reference the range of cells in Python to get your data into a DataFrame.&#x20;

You use the argument `first_row_header=True` to avoid the first row of your DataFrame being what is intended to be our header. Note that the output, in this case, is printed to the console since you already have your initial CSV in the sheet. After some manipulation of the data, perhaps you would want to display your new DataFrame. In that case, leave `df` as the last line of code.

In this case, the spreadsheet reflects `cells('A1:B161')` since we want the full span of data in both columns A and B spanning from rows 1 to 161.

```python
df = q.cells('A1:B161', first_row_header=True))
```


# Financial data

## Financial data

Quadratic includes a built-in financial data API for Python — no API keys or external accounts required. Use `q.financial` to pull stock prices, company fundamentals, financial statements, news, and technical indicators straight into your sheet.

### Quick start

All methods are async — use `await`:

```python
# Daily stock prices as a DataFrame
prices = await q.financial.stock_prices('AAPL', start='2025-01-01')
prices
```

```python
# Latest real-time quote
quote = await q.financial.realtime_price('NVDA')
quote['last_price']
```

### Available methods

| Method                                                                    | Returns                                                                                                                     |
| ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `stock_prices(identifier, start, end, frequency)`                         | Historical prices (DataFrame)                                                                                               |
| `realtime_price(identifier)`                                              | Real-time quote (dict)                                                                                                      |
| `intraday_prices(identifier)`                                             | Intraday bars                                                                                                               |
| `company(identifier)`                                                     | Company profile (dict)                                                                                                      |
| `company_news(identifier, start_date)`                                    | Recent news                                                                                                                 |
| `financial_statements(identifier, statement, fiscal_year, fiscal_period)` | Income statement, balance sheet, or cash flow (DataFrame)                                                                   |
| `dividends(identifier)`                                                   | Dividend history                                                                                                            |
| `splits(identifier)`                                                      | Split history                                                                                                               |
| `data_point(identifier, tag)`                                             | Single numeric metric                                                                                                       |
| `data_point_text(identifier, tag)`                                        | Single text metric                                                                                                          |
| `historical_data(identifier, tag)`                                        | Metric time series                                                                                                          |
| `technical_indicator(identifier, indicator, ...)`                         | Technical indicators (DataFrame): `sma`, `rsi`, `macd`, `bollinger_bands`, `vwap`, `atr`, `adx`, `obv`, `stochastic`, `cci` |

{% hint style="info" %}
Prefer adjusted prices for historical analysis — they account for splits and dividends.
{% endhint %}

### Prefer formulas?

The Excel-compatible `STOCKHISTORY` function retrieves historical prices without writing Python:

```
=STOCKHISTORY("MSFT", "2025-01-01", "2025-12-31")
```

### Rate limits

Financial data requests are limited to 500 requests per team per minute. If your team hits the limit, requests briefly pause and a notification appears in the app.


# Clean data

Get your data ready for analysis.

Cleaning data in Quadratic is more seamless than you may be used to, as your data is viewable in the sheet as you step through your DataFrame. Every change to your DataFrame can be reflected in the sheet in real-time. Some data cleaning steps you may be interested in taking (very much non-exhaustive!):&#x20;

1. [View select sections of your DataFrame in the sheet ](#1.-view-select-sections-of-your-dataframe-in-the-sheet)
2. [Drop specified columns](#2.-drop-specified-columns)
3. [Field-specific changes](#3.-field-specific-changes)
4. [Clean columns ](#4.-clean-columns)
5. [Delete select rows ](#5.-delete-select-rows)
6. [Delete empty rows](#6.-delete-empty-rows)
7. [Change data types](#7.-change-data-types)&#x20;
8. [Remove duplicates](#8.-remove-duplicates)&#x20;

## 1. View select sections of your DataFrame in the sheet

Assume DataFrame named `df`. With `df.head()` you can display the first x rows of your spreadsheet. With this as your last line the first x rows will display in the spreadsheet. You can do the same except with the last x rows via `df.tail()`

```python
# Display first five rows
df.head(5)

# Display last five rows
df.tail(5)
```

## 2. Drop specified columns

Deleting columns point and click can be done by highlighting the entire column and pressing `Delete`. Alternatively, do this programmatically with the code below.&#x20;

```python
# Assuming DataFrame df, pick the columns you want to drop
columns_to_drop = ['Average viewers', 'Followers']

df.drop(columns_to_drop, inplace=True, axis=1)
```

## 3. Field-specific changes

There are many ways to make field-specific changes, but this list will give you some ideas.&#x20;

<pre class="language-python"><code class="lang-python"><strong># Replace row 7 in column 'Duration' with the value of 45
</strong> df.loc[7, 'Duration'] = 45
</code></pre>

## 4. Clean columns

Going column by column to clean specific things is best done programmatically.&#x20;

```python
# Specify things to replace empty strings to prep drop 
df['col1'].replace(things_to_replace, what_to_replace_with, inplace=True)
```

## 5. Delete select rows

With the beauty of Quadratic, feel free to delete rows via point and click; in other cases, you may need to do this programmatically.&#x20;

```python
# Knowing your row, you can directly drop via
df.drop(x)

# Select a specific index, then drop that index
x = df[((df.Name == 'bob') &( df.Age == 25) & (df.Grade == 'A'))].index
df.drop(x)
```

## 6. Delete empty rows

Identifying empty rows should be intuitive in the spreadsheet via point-and-click; in other cases, you may need to do this programmatically.&#x20;

```python
# Replace empty strings to prep drop 
df['col1'].replace('', np.nan, inplace=True)

# Delete where specific columns are empty 
df.dropna(subset=['Tenant'], inplace=True)
```

## 7. Change data types

By default, Quadratic inputs will be read as strings by Python code. Manipulate these data types as you see fit in your DataFrame. &#x20;

```python
# Specify column(s) to change data type
df.astype({'col1': 'int', 'col2': 'float'}).dtypes

# Common types: float, int, datetime, string
```

## 8. Remove duplicates

Duplicates are likely best removed programmatically, not visually. Save some time with the code below.&#x20;

```python
# Drop duplicates across DataFrame
df.drop_duplicates()

# Drop duplicates on specific columns 
df.drop_duplicates(subset=['col1'])

# Drop duplicates; keep the last 
df.drop_duplicates(subset=['col1', 'col2'], keep='last')
```


# Charts/visualizations

Glean insights from your data, visually.

Create beautiful visualizations using our in-app Plotly support. Plotly support works just as you're used to in Python, displaying your chart straight to the spreadsheet.&#x20;

## Getting started

Building charts in Quadratic is centered around Python charting libraries, starting with Plotly. Building charts in Plotly is broken down into 3 simple steps:&#x20;

1. [Create and display a chart ](#create-and-display-a-chart)
2. [Style your chart ](#styling)
3. [Chart controls](#3.-chart-controls)

## 1. Create and display a chart

### Line charts

```python
# import plotly
import plotly.express as px

# replace this df with your data
df = px.data.gapminder().query("country=='Canada'")

# create your chart type, for more chart types: https://plotly.com/python/
fig = px.line(df, x="year", y="lifeExp", title='Life expectancy in Canada')

# make chart prettier
fig.update_layout(
    plot_bgcolor="White",
)

# display chart 
fig.show()
```

<figure><img src="/files/ioAWWozmJpVs9zFW9YNg" alt="" width="563"><figcaption></figcaption></figure>

### Bar charts

```python
import plotly.express as px

# replace this df with your data
df = px.data.gapminder().query("country == 'Canada'")

# create your chart type, for more chart types: https://plotly.com/python/
fig = px.bar(df, x='year', y='pop')

# make chart prettier
fig.update_layout(
    plot_bgcolor="White",
)

# display chart
fig.show()
```

<figure><img src="/files/f7VGKC3beEj0V0C0RXwP" alt=""><figcaption></figcaption></figure>

### Histograms&#x20;

<pre class="language-python"><code class="lang-python"><strong># Import Plotly
</strong><strong>import plotly.express as px
</strong>
# Create figure - replace df with your data
fig = px.histogram(df, x = 'output')

# Display to sheet 
fig.show()
</code></pre>

<figure><img src="/files/Uy9GTWZ0AAXJeAXjhsXn" alt=""><figcaption></figcaption></figure>

### Scatter plots&#x20;

```python
import plotly.express as px

# replace df, x, and y and color with your data
fig = px.scatter(df, x="col1", y="col2", color="col3")
fig.update_traces(marker_size=10)
fig.update_layout(scattermode="group")
fig.show()
```

<figure><img src="/files/3fnyCMod3vNjTmhSRr7v" alt=""><figcaption></figcaption></figure>

### Heatmaps

```python
# Import library
import plotly.express as px

# Assumes 2d array Z
fig = px.imshow(Z, text_auto=True)

# Display chart
fig.show()
```

<figure><img src="/files/1S2uz65tuDkNfSQvJwc9" alt=""><figcaption></figcaption></figure>

### More chart types

For more chart types, explore the Plotly docs: <https://plotly.com/python/>

## 2. Styling

For more styling, explore the Plotly styling docs: <https://plotly.com/python/styling-plotly-express/>

```python
# Example chart styling options to get started
fig.update_layout(
    xaxis=dict(
        showline=True,
        showgrid=False,
        showticklabels=True,
        linecolor='rgb(204, 204, 204)',
        linewidth=2,
        ticks='outside',
        tickfont=dict(
            family='Arial',
            size=12,
            color='rgb(82, 82, 82)',
        ),
    ),
    yaxis=dict(
        showgrid=False,
        zeroline=False,
        showline=False,
        showticklabels=True,
    ),
    autosize=False,
    showlegend=False,
    plot_bgcolor='white',
    title='Historical power usage by month (1985-2018)'
)
```

## 3. Chart controls

&#x20;Resize by dragging the edges of the chart.&#x20;

<figure><img src="/files/GCcmvyDKpOt0TbcaiZZc" alt=""><figcaption></figcaption></figure>


# Manipulate data

Perform novel analysis on your data.

Manipulating data in Quadratic is easier than ever as you can view your changes in the sheet in real-time. Here is a non-exhaustive list of ways to manipulate your data in Quadratic:&#x20;

1. [Find correlations ](#1.-find-correlations)
2. [Basic stats - max, min, average, selections, etc.](#2.-basic-stats-max-min-average-selections-etc.)
3. [DataFrame math ](#3.-dataframe-math)
4. [Data selections ](#4.-data-selections)

## 1. Find correlations

```python
# Get the correlation and show the value in the sheet
data['col1'].corr(data['col2'], method='pearson')

# possible methods: pearson, kendall, spearman 
```

## 2. Basic stats - max, min, mean, selections, etc.

Reference: <https://pandas.pydata.org/docs/getting_started/intro_tutorials/06_calculate_statistics.html>

```python
# Get the max value of a column
df["col1"].max()
```

```python
# Get the min value of a column
df["col1"].min()
```

```python
# Get the mean value of a column
df["col1"].mean()
```

```python
# Get the median value of a column
df["col1"].median()
```

```python
# Get the skew for all columns
df.skew()
```

```python
# Count the values in a column
df["col1"].value_counts()
```

```python
# Get the summary of a column
df["col1"].describe()
```

## 3. DataFrame math

Do math on data in the DataFrame. Alternatively, use formulas in the sheet on the values.&#x20;

```python
# Add, subtract, multiply, divide, etc., will all work on all values in a column
df['col1'] + 1
df['col1'] - 1
df['col1'] * 2
df['col1'] / 2 
```

```python
# Do any arbitrary math column-wise with the above or do DataFrame-wise via
df + 1 
```

## 4. Data selections

Alternatively, cut/copy/paste specific values in the sheet.&#x20;

```python
# get a column 
df['col1'] 
```

```python
# get multiple columns 
df[['col1', 'col2']] 
```


# Getting started

Get started writing JavaScript, the world's most popular programming language.

**Note:** *this feature is experimental* - users of JavaScript should expect things to break or lack complete functionality. If testing experimental software is your jam, you can use JavaScript experimentally in the app and report any bugs you find [on our GitHub](https://github.com/quadratichq/quadratic/issues) or by [reaching out to us directly](https://www.quadratichq.com/contact).&#x20;

With JavaScript in Quadratic, the world's most popular programming language meets the world's most popular tool for working with data - spreadsheets.&#x20;

## Quick start

1. Select a cell where you want to write JavaScript.

<figure><img src="/files/kObSJ7iiZYOc3Pn6I8fG" alt=""><figcaption></figcaption></figure>

2. Press `/` to bring up the cell type menu. Select JavaScript.

<figure><img src="/files/Ga9ZoC6TYiPuKhFD0XE7" alt=""><figcaption></figcaption></figure>

3. Write JavaScript in the code editor. Return statements are returned to the sheet at the cell position.

<figure><img src="/files/XlnJYGAD0NVVxBeXjm95" alt=""><figcaption></figcaption></figure>

## Learn more

Below are a bunch of quick links to find more details on how to write JavaScript in Quadratic.

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f9f9">🧹</span> <strong>Reference cells using JavaScript</strong></td><td></td><td><a href="/pages/CnTacYBNc0vHwooM9uXx">/pages/CnTacYBNc0vHwooM9uXx</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f449">👉</span> <strong>Return data to the sheet using JavaScript</strong></td><td></td><td><a href="/pages/qGXPsRGHDElYn8LbaInl">/pages/qGXPsRGHDElYn8LbaInl</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f4c8">📈</span> <strong>Make API requests using JavaScript</strong></td><td></td><td><a href="/pages/v9OuRPCASJZxnZu2fsMr">/pages/v9OuRPCASJZxnZu2fsMr</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f916">🤖</span> <strong>Make API requests using JavaScript</strong></td><td></td><td><a href="/pages/v9OuRPCASJZxnZu2fsMr">/pages/v9OuRPCASJZxnZu2fsMr</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f4ca">📊</span> <strong>Visualize data using JavaScript</strong></td><td></td><td><a href="/pages/nKDlNWiA6Qok6hKjCoVj">/pages/nKDlNWiA6Qok6hKjCoVj</a></td></tr><tr><td></td><td>Learn how to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f468-1f4bb">👨‍💻</span> <strong>Import JavaScript packages using ESM</strong></td><td></td><td><a href="/pages/assZbgsKPjnmARv7h0tn">/pages/assZbgsKPjnmARv7h0tn</a></td></tr></tbody></table>


# Reference cells

Reference cells from JavaScript.

In Quadratic, [reference tables](#block-9871b7ac3fa341da856503066f6f8e8c) and named outputs for simplest reference, [reference individual cells](#block-9871b7ac3fa341da856503066f6f8e8c) from JavaScript for single values or [reference a range of cells](#block-aea52dff303745f6b08db8ab3b24c4f6) for multiple values.&#x20;

## Referencing tables (and named outputs) <a href="#block-9871b7ac3fa341da856503066f6f8e8c" id="block-9871b7ac3fa341da856503066f6f8e8c"></a>

To reference a table, use the global function `q.cells` along with the table's name in the fashion outlined below.&#x20;

```javascript
// NOTE: uses the same A1 notation as Formulas
// References existing Table1 and is read in as array of arrays
let x = q.cells("Table1")

// Get a single column out of table into an array
let x = q.cells("Table1[column_name]")

// Get the table headers 
let x = q.cells("Table1[#HEADERS]")

// Reference a range of columns in a table
let x = q.cells("Table_name[[column_name:column_name]]")
```

## Referencing individual cells <a href="#block-9871b7ac3fa341da856503066f6f8e8c" id="block-9871b7ac3fa341da856503066f6f8e8c"></a>

To reference an individual cell, use the global function `q.cells` which returns the cell value.

<pre class="language-javascript"><code class="lang-javascript"><strong>// Reads the value in cell A1 and places in variable x 
</strong>let x = q.cells("A1")
</code></pre>

Any time cells dependent on other cells update, the dependent cell will also update. This means your code will execute in one cell if it is dependent on another. This is the behavior you want in almost all situations, including user inputs in the sheet that cause calculation in a JavaScript cell.&#x20;

## Referencing a range of cells <a href="#block-aea52dff303745f6b08db8ab3b24c4f6" id="block-aea52dff303745f6b08db8ab3b24c4f6"></a>

To reference a range of cells, use the same global function `q.cells()` which returns an array of arrays.&#x20;

```javascript
let let x = q.cells('A1:A5') // Returns a 1x5 array spanning from A1 to A5

let let x = q.cells('A1:C7') // Returns a 3x7 array of arrays spanning from A1 to C7

let let x = q.cells('A') // Returns all values in column A into a single array

let let x = q.cells('A:C') // Returns all values in columns A to C into an array of three arrays 

let let x = q.cells('A5:A') // Returns all values in column A starting at A5 and going down as an array 

let let x = q.cells('A5:C') // Returns all values in column A to C, starting at A5 and going down as an array of arrays
```

## Referencing another sheet

To reference another sheet's cells or range of cells use the following:&#x20;

<pre class="language-javascript"><code class="lang-javascript"><strong>// Use the sheet name as an argument for referencing range of cells 
</strong><strong>let x = q.cells("'Sheet_name_here'!A1:C9")
</strong><strong>
</strong><strong>// For individual cell reference 
</strong>let x = q.cells("'Sheet_name_here'!A1")
</code></pre>

## Unbounded ranges

### Unbounded column references&#x20;

To reference all the data in a column or set of columns without defining the range, use the following syntax.&#x20;

Column references span from set row (row 1 if not defined) to wherever the content in that column ends.&#x20;

<pre class="language-javascript"><code class="lang-javascript">// references all values in the column from row 1 to the end of the content 
<strong>let x = q.cells("A") // returns all the data in the column starting from row 1 to end of data 
</strong>
let x = q.cells("A:D") // returns all the data in columns A to D starting from row 1 to end of data in longest column

let x = q.cells("A5:A") // returns all values from A5 to the end of the content in column A 

let x = q.cells("A5:C") // returns all values from A5 to end of content in C

let x = q.cells("'Sheet2'!A:C") // same rules to reference in other sheets apply
</code></pre>

### Unbounded row references

To reference all the data in a row or set of rows without defining the range, use the following syntax.&#x20;

Row references span from the row set to wherever the content in that row ends.&#x20;

```javascript
// Returns all values in Row 1
let x = q.cells("1") 

// Returns all values in rows 1 to 3 
let x = q.cells("1:3") 

// Returns all values in Row 1
let x = q.cells("A1:1")

// Returns all values in Row 1 starting at column C
let x = q.cells("C1:1")

// Returns all values in Row 3 starting at column A
let x = q.cells("A3:3")

// Returns all values in Row 3 starting at column C 
let x = q.cells("C3:3")
```

## Relative vs absolute references

By default when you copy paste a reference it will update the row reference unless you use `$` notation in your references.&#x20;

```javascript
// Copy pasting this one row down will change reference to A2
let x = q.cells("A1")

// Copy pasting this one row down will keep reference as A1
let x = q.cells("A$1")

// Example using ranges - row references will not change
let x = q.cells("A$1:B$20")

// Only A reference will change when copied down
let x = q.cells("A1:B$20") 
```


# Return data to the sheet

Single values, arrays, and charts are the JavaScript types that can be returned to the sheet. Any data can be structured as an array and returned to the sheet.&#x20;

## Single value&#x20;

```javascript
// from variable with assigned value
let data = 5; 

// return this value to the sheet
return data;
```

## 1-d array&#x20;

```javascript
let data = [1, 2, 3, 4, 5];

return data;
```

## 2-d array&#x20;

```javascript
let data = [[1,2,3,4,5],[1,2,3,4,5];

return data;
```

## Charts

```javascript
import Chart from 'https://esm.run/chart.js/auto';

let canvas = new OffscreenCanvas(800, 450);
let context = canvas.getContext('2d');

// create data 
let data = [['Africa', 'Asia', 'Europe', 'Latin America', 'North America'],[2478, 5267, 734, 784, 433]]

// print data to console 
console.log(data);

// Create chart 
new Chart(canvas, {
    type: 'bar',
    data: {
        labels: data[0],
        datasets: [
        {
            label: "Population (millions)",
            backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
            data: data[1]
        }
        ]
    },
    options: {
        legend: { display: false },
        title: {
        display: true,
        text: 'Predicted world population (millions) in 2050'
        }
    }
});

// return chart to the sheet 
return canvas;
```


# API Requests

How to make API requests in JavaScript.

## GET request

Perform API requests using the standard JavaScript approach of Fetch.&#x20;

```javascript
// API for get requests
let res = await fetch("https://jsonplaceholder.typicode.com/todos/1");
let json = await res.json();

console.log(json);

return [Object.keys(json), Object.values(json)];
```

## GET request with error handling&#x20;

```javascript
async function getData() {
  const url = "https://jsonplaceholder.typicode.com/todos/1";
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`Response status: ${response.status}`);
    }

    const json = await response.json();
    // Return the JSON object as a 2D array
    return [Object.keys(json), Object.values(json)];
  } catch (error) {
    console.error(error.message);
    // Return the error message to the sheet
    return `Error: ${error.message}`;
  }
}

// Call the function and return its result to the sheet
return await getData();
```

## POST request with body

```javascript
async function getData() {
  // replace with your API URL and body parameters 
  const url = "https://example.org/products.json";
  const requestBody = {
    key1: "value1",
    key2: "value2"
  };

  try {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(requestBody)
    });

    if (!response.ok) {
      throw new Error(`Response status: ${response.status}`);
    }

    const json = await response.json();
    // Return the JSON object as a 2D array
    return [Object.keys(json), Object.values(json)];
  } catch (error) {
    console.error(error.message);
    // Return the error message to the sheet
    return `Error: ${error.message}`;
  }
}

// Call the function and return its result to the sheet
return await getData();
```

If you ever get stuck with JavaScript code (especially requests) that doesn't seem to be working but is showing no error, you may be missing an `Await` somewhere that it is needed.&#x20;


# Charts/visualizations

Charts are supported in JavaScript using [charts.js](https://www.chartjs.org/). No other libraries are currently supported.&#x20;

## Bar chart

```javascript
import Chart from 'https://esm.run/chart.js/auto';

let canvas = new OffscreenCanvas(800, 450);
let context = canvas.getContext('2d');

// create data 
let data = [['Africa', 'Asia', 'Europe', 'Latin America', 'North America'],[2478, 5267, 734, 784, 433]]

// print data to console 
console.log(data)

// Create chart 
new Chart(canvas, {
    type: 'bar',
    data: {
        labels: data[0],
        datasets: [
        {
            label: "Population (millions)",
            backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
            data: data[1]
        }
        ]
    },
    options: {
        legend: { display: false },
        title: {
        display: true,
        text: 'Predicted world population (millions) in 2050'
        }
    }
});

// return chart to the sheet 
return canvas;
```

## Line chart

```javascript
import Chart from 'https://esm.run/chart.js/auto';

let canvas = new OffscreenCanvas(800, 450);
let context = canvas.getContext('2d');

// create data 
let data = [['1999', '2000', '2001', '2002', '2003'],[2478, 5267, 734, 784, 433]]

// print data to console 
console.log(data)

// Create chart 
new Chart(canvas, {
    type: 'line',
    data: {
        labels: data[0],
        datasets: [
        {
            label: "Population (millions)",
            backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
            data: data[1]
        }
        ]
    },
    options: {
        legend: { display: false },
        title: {
        display: true,
        text: 'Predicted world population (millions) in 2050'
        }
    }
});

// return chart to the sheet 
return canvas;
```


# Packages

Packages in JavaScript are supported using ESM. You can use a third-party JS CDN to load third-party packages. Some possible CDNs include:&#x20;

* We recommend using esm.run from <https://www.jsdelivr.com/esm>
* <https://www.unpkg.com>
* <https://esm.sh>

Below are examples on how to correctly use esm.run to import packages in JavaScript.&#x20;

## Examples

Below are some common examples of libraries, imported using esm.run. Many more libraries are available for use in Quadratic and you can use the JS CDN of your choice. Below is how to use esm.run, which we recommend as a top option.

### Charting&#x20;

Chart.js is the only charting library in JavaScript supported in Quadratic.&#x20;

```javascript
import Chart from 'https://esm.run/chart.js/auto';
```

### Analytics

D3.js is a common analytics library for JavaScript.

```javascript
import * as d3 from 'https://esm.run/d3';

let my_data = [1,2,3]
let sum = d3.sum(my_data)
return sum
```

Brain.js is a Machine Learning library that works in Quadratic

```javascript
import * as brain from 'https://esm.run/brain.js';

// provide optional config object (or undefined). Defaults shown.
const config = {
  binaryThresh: 0.5,
  hiddenLayers: [3], // array of ints for the sizes of the hidden layers in the network
  activation: 'sigmoid', // supported activation types: ['sigmoid', 'relu', 'leaky-relu', 'tanh'],
  leakyReluAlpha: 0.01, // supported for activation type 'leaky-relu'
};

// create a simple feed-forward neural network with backpropagation
const net = new brain.NeuralNetwork(config);

await net.train([
  { input: [0, 0], output: [0] },
  { input: [0, 1], output: [1] },
  { input: [1, 0], output: [1] },
  { input: [1, 1], output: [0] },
]);

const output = net.run([1, 0]); // [0.987]

return output[0]
```


# Getting started

Work with classic spreadsheet logic - math, references, and point and click manipulation for quick data analysis.

Get started with Formulas the same way as any other spreadsheet - click `=` on a cell and get started right away. Formulas are in-line by default.&#x20;

<figure><img src="/files/5aiSgg0ATJMM22vYAHf3" alt="" width="563"><figcaption><p>In-line formulas in Quadratic</p></figcaption></figure>

You can also optionally use multi-line Formulas for those Formulas that need to be expanded to become readable.&#x20;

To open the multi-line editor either use / and select it in the cell type selection menu or use the multi-line editor button from the in-line editor as showed below.&#x20;

<figure><img src="/files/Q37VTod71ejeOyx64V4E" alt=""><figcaption></figcaption></figure>

The multi-line editor becomes useful when Formulas become more difficult to read than the space afforded by the in-line editor. Example:

```sql
IF( Z0 > 10, 
    IF( Z1 > 10, 
        IF (Z2 > 10, 
            AVERAGE(Z0:Z2), 
            "Invalid Data",
        ),
        "Invalid Data", 
    ),
    "Invalid Data", 
)
```

Cells are by default referenced relatively in Quadratic. Use $ notation to do absolute references, similar to what you'd be familiar with in traditional spreadsheets. Learn more on the [Reference cells](/formulas/reference-cells) page.

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td></td><td>Jump to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f449">👉</span> <strong>Reference cells</strong></td><td></td><td><a href="/pages/CnTacYBNc0vHwooM9uXx">/pages/CnTacYBNc0vHwooM9uXx</a></td></tr><tr><td></td><td>Jump to: <br><span data-gb-custom-inline data-tag="emoji" data-code="1f4dc">📜</span> <strong>Formulas cheat sheet</strong></td><td></td><td><a href="/pages/7SUufl45rmIO1Cvy93sI">/pages/7SUufl45rmIO1Cvy93sI</a></td></tr><tr><td></td><td></td><td></td><td></td></tr></tbody></table>


# AI assistant

Generate Formulas with AI.

Formula generation is one AI question away.&#x20;

To generate Formulas with AI, simply ask your question in natural language to the AI assistant located in the multi-line Formula editor.&#x20;

To enter the multi-line editor, click the multi-line Formula editor button.

<figure><img src="/files/xX8PJY6YGoUSfP7bIeHN" alt=""><figcaption></figcaption></figure>

Once you're in the multi-line Formula editor open the AI assistant and start asking your questions to generate Formulas.

<figure><img src="/files/RVVbXnSMocGOcoyQBj9c" alt=""><figcaption></figcaption></figure>


# Reference cells

Reference data in other cells from your formula.

## 1. Reference an individual cell

To reference an individual cell, use standard spreadsheet notation. The only difference is that Quadratic allows negative axes; for negative notation, append `n` to the cell reference. Cells are, by default, relatively referenced. Use `$` notation to use absolute references.&#x20;

**Examples in the table below:**

<table><thead><tr><th width="219">Formula Notation</th><th>(x, y) coordinate plane equivalent </th><th data-hidden>(x, y) coordinate plane equivalent</th><th data-hidden></th></tr></thead><tbody><tr><td><code>A0</code></td><td>(0,0)</td><td>(0</td><td></td></tr><tr><td><code>A1</code></td><td>(0,1)</td><td></td><td></td></tr><tr><td><code>B1</code></td><td>(1,1)</td><td></td><td></td></tr><tr><td><code>An1</code></td><td>(0,-1)</td><td></td><td></td></tr><tr><td><code>nA1</code></td><td>(-1,1)</td><td></td><td></td></tr><tr><td><code>nAn1</code></td><td>(-1,-1)</td><td></td><td></td></tr></tbody></table>

## 2. Relative cell reference&#x20;

Individual cells and ranges are, by default, referenced relatively. E.g. copy-pasting `A1` to the following two rows will produce `A2`, and `A3` respectively.

To reference a range of cells relatively, use the traditional spreadsheet notation that separates two distinct cells using a semicolon as a delimiter, e.g. `A1:D3`

Cells in this notation are referenced relatively, so you can drag out a cell to replicate that formula relatively across your selection.&#x20;

<figure><img src="/files/oIuS9RY9rV7MgXVkpxVC" alt=""><figcaption></figcaption></figure>

## 3. Absolute cell references

To perform absolute cell references, use standard spreadsheet notation with `$`, for example  `$A$1:D3` - `A1` will be copied absolutely and `D3` will be copied relatively if you drag to replicate.

## 3. Reference across sheets

To reference the value from another sheet, use the sheet name in quotations with an `!`.

### Single cell

To reference cell F12 in a sheet named "Sheet 1" from a sheet named "Sheet 2" use:&#x20;

```formula
"Sheet 1"!F12
```

### Range of cells&#x20;

To reference cells F12 to F14 in Sheet 1 from Sheet 2, use:

```
"Sheet 1"!F12:F14
```


# Functions and operators

Using formulas in the spreadsheet.

## Operators

| Precedence | Symbol           | Description                         |
| ---------- | ---------------- | ----------------------------------- |
| 1          | `x%`             | Percent (divides by 100)            |
| 2          | `+x`             | positive                            |
|            | `-x`             | negative                            |
| 3          | `a:b`            | cell range                          |
| 4          | `a..b`           | numeric range                       |
| 5          | `a^b or a**b`    | Exponentiation                      |
| 6          | `a*b`            | Multiplication                      |
|            | `a/b`            | Division                            |
| 7          | `a+b`            | Addition                            |
|            | `a-b`            | Subtraction                         |
| 8          | `a&b`            | String concatenation                |
| 9          | `a=b` or `a==b`  | Equal comparison                    |
|            | `a<>b` or `a!=b` | Not equal comparison                |
|            | `a<b`            | Less than comparison                |
|            | `a>b`            | Greater than comparison             |
|            | `a<=b`           | Less than or equal to comparison    |
|            | `a>=b`           | Greater than or equal to comparison |

## Mathematics functions

| **Function**                                                                    | **Description**                                                                                                                                                                                                                                                                                                                                                        |
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SUM([numbers...])`                                                             | Adds all values. Returns `0` if given no values.                                                                                                                                                                                                                                                                                                                       |
| `SUMIF(eval_range, criteria, [sum_range])`                                      | Evaluates each value based on some criteria, and then adds the ones that meet those criteria. If `sum_range` is given, then values in `sum_range` are added instead wherever the corresponding value in `eval_range` meets the criteria. See [the documentation](https://docs.quadratichq.com/formulas/criteria) for more details about how criteria work in formulas. |
| `SUMIFS(sum_range, eval_range1, criteria1, [more_eval_ranges_and_criteria...])` | Adds values from `numbers_range` wherever the criteria are met at the corresponding value in each `eval_range`. See [the documentation](https://docs.quadratichq.com/formulas/criteria) for more details about how criteria work in formulas.                                                                                                                          |
| `PRODUCT([numbers...])`                                                         | Multiplies all values. Returns `1` if given no values.                                                                                                                                                                                                                                                                                                                 |
| `ABS(number)`                                                                   | Returns the absolute value of a number.                                                                                                                                                                                                                                                                                                                                |
| `SQRT(number)`                                                                  | Returns the square root of a number.                                                                                                                                                                                                                                                                                                                                   |
| `CEILING(number, increment)`                                                    | Rounds a number up to the next multiple of `increment`. If `number` and `increment` are both negative, rounds the number down away from zero. Returns an error if `number` is positive but `significance` is negative. Returns `0` if `increment` is `0`.                                                                                                              |
| `FLOOR(number, increment)`                                                      | Rounds a number down to the next multiple of `increment`. If `number` and `increment` are both negative, rounds the number up toward zero. Returns an error if `number` is positive but `significance` is negative, or if `increment` is `0` but `number` is nonzero. Returns `0` if `increment` is `0` *and* `number` is `0`.                                         |
| `INT(number)`                                                                   | Rounds a number down to the next integer. Always rounds toward negative infinity.                                                                                                                                                                                                                                                                                      |
| `POWER(base, exponent)`                                                         | Returns the result of raising `base` to the power of `exponent`.                                                                                                                                                                                                                                                                                                       |
| `PI()`                                                                          | Returns π, the circle constant.                                                                                                                                                                                                                                                                                                                                        |
| `TAU()`                                                                         | Returns τ, the circle constant equal to 2π.                                                                                                                                                                                                                                                                                                                            |

#### CEILING.MATH

`CEILING.MATH(number, [increment], [negative_mode])`

Examples:

* `CEILING.MATH(6.5)`
* `CEILING.MATH(6.5, 2)`
* `CEILING.MATH(-12, 5)`
* `CEILING.MATH(-12, 5, -1)`

Rounds a number up or away from zero to the next multiple of`increment`. If `increment` is omitted, it is assumed to be `1`.\
The sign of `increment` is ignored.

If `negative_mode` is positive or zero, then `number` is rounded\
up, toward positive infinity. If `negative_mode` is negative,\
then `number` is rounded away from zero. These are equivalent\
when `number` is positive, so in this case `negative_mode` has\
no effect.

If `increment` is zero, returns zero.

#### FLOOR.MATH

`FLOOR.MATH(number, [increment], [negative_mode])`

Examples:

* `FLOOR.MATH(6.5)`
* `FLOOR.MATH(6.5, 2)`
* `FLOOR.MATH(-12, 5)`
* `FLOOR.MATH(-12, 5, -1)`

Rounds a number down or toward zero to the next multiple of`increment`. If `increment` is omitted, it is assumed to be `1`.\
The sign of `increment` is ignored.

If `negative_mode` is positive or zero, then `number` is rounded\
down, toward negative infinity. If `negative_mode` is negative,\
then `number` is rounded toward zero. These are equivalent when`number` is positive, so in this case `negative_mode` has no\
effect.

If `increment` is zero, returns zero.

#### ROUND

`ROUND(number, [digits])`

Examples:

* `ROUND(6.553, 2)`

Rounds a number to the specified number of digits after the\
decimal point.

* If `digits` is 0 or omitted, then the number is rounded to the\
  nearest integer.
* If `digits > 0`, then the number is rounded to a digit after\
  the decimal point. For example, `ROUND(x, 2)` rounds `x` to\
  the nearest multiple of 0.01.
* If `digits < 0`, then the number is rounded to a digit before\
  the decimal point. For example, `ROUND(x, -2)` rounds `x` to\
  the nearest multiple of 100.

Ties are broken by rounding away from zero. For example,`ROUND(50, -2)` rounds to `100`.

#### ROUNDUP

`ROUNDUP(number, [digits])`

Examples:

* `ROUNDUP(6.553, 2)`

Rounds a number **away from zero** to the specified number of\
digits after the decimal point.

* If `digits` is 0 or omitted, then the number is rounded to an\
  integer.
* If `digits > 0`, then the number is rounded to a digit after\
  the decimal point. For example, `ROUNDUP(x, 2)` rounds `x` to\
  a multiple of 0.01.
* If `digits < 0`, then the number is rounded to a digit before\
  the decimal point. For example, `ROUNDUP(x, -2)` rounds `x` to\
  a multiple of 100.

#### ROUNDDOWN

`ROUNDDOWN(number, [digits])`

Examples:

* `ROUNDDOWN(6.553, 2)`

Rounds a number **toward zero** to the specified number of\
digits after the decimal point. This is exactly the same as`TRUNC()`.

* If `digits` is 0 or omitted, then the number is rounded to an\
  integer.
* If `digits > 0`, then the number is rounded to a digit after\
  the decimal point. For example, `ROUNDDOWN(x, 2)` rounds `x`\
  to a multiple of 0.01.
* If `digits < 0`, then the number is rounded to a digit before\
  the decimal point. For example, `ROUNDDOWN(x, -2)` rounds `x`\
  to a multiple of 100.

#### TRUNC

`TRUNC(number, [digits])`

Examples:

* `TRUNC(6.553, 2)`

Rounds a number **toward zero** to the specified number of\
digits after the decimal point. This is exactly the same as`ROUNDDOWN()`.

* If `digits` is 0 or omitted, then the number is rounded to an\
  integer.
* If `digits > 0`, then the number is rounded to a digit after\
  the decimal point. For example, `TRUNC(x, 2)` rounds `x` to a\
  multiple of 0.01.
* If `digits < 0`, then the number is rounded to a digit before\
  the decimal point. For example, `TRUNC(x, -2)` rounds `x` to a\
  multiple of 100.

#### MOD

`MOD(number, divisor)`

Examples:

* `MOD(3.9, 3)`
* `MOD(-2.1, 3)`

Returns the remainder after dividing `number` by `divisor`. The\
result always has the same sign as `divisor`.

Note that `INT(n / d) * d + MOD(n, d)` always equals `n` (up to\
floating-point precision).

#### EXP

`EXP(exponent)`

Examples:

* `EXP(1), EXP(2/3), EXP(C9)`

Returns the result of raising [Euler's number](https://en.wikipedia.org/wiki/E_\(mathematical_constant\)) *e* to the power\
of `exponent`.

#### LOG

`LOG(number, [base])`

Examples:

* `LOG(100)`
* `LOG(144, 12)`
* `LOG(144, 10)`

Returns the [logarithm](https://en.wikipedia.org/wiki/Logarithm) of `number` to the base `base`. If`base` is omitted, it is assumed to be 10, the base of the[common logarithm](https://en.wikipedia.org/wiki/Common_logarithm).

#### LOG10

`LOG10(number)`

Examples:

* `LOG10(100)`

Returns the [base-10 logarithm](https://en.wikipedia.org/wiki/Common_logarithm) of `number`.

#### LN

`LN(number)`

Examples:

* `LN(50)`

Returns the [natural logarithm](https://en.wikipedia.org/wiki/Natural_logarithm) of `number`.

### Trigonometric functions

| **Function**       | **Description**                                                                                                                                  |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `DEGREES(radians)` | Converts radians to degrees.                                                                                                                     |
| `RADIANS(degrees)` | Converts degrees to radians.                                                                                                                     |
| `SIN(radians)`     | Returns the [sine](https://en.wikipedia.org/wiki/Trigonometric_functions) of an angle in radians.                                                |
| `ASIN(number)`     | Returns the [inverse sine](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions) of a number, in radians, ranging from 0 to π.          |
| `COS(radians)`     | Returns the [cosine](https://en.wikipedia.org/wiki/Trigonometric_functions) of an angle in radians.                                              |
| `ACOS(number)`     | Returns the [inverse cosine](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions) of a number, in radians, ranging from 0 to π.        |
| `TAN(radians)`     | Returns the [tangent](https://en.wikipedia.org/wiki/Trigonometric_functions) of an angle in radians.                                             |
| `ATAN(number)`     | Returns the [inverse tangent](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions) of a number, in radians, ranging from -π/2 to π/2.  |
| `CSC(radians)`     | Returns the [cosecant](https://en.wikipedia.org/wiki/Trigonometric_functions) of an angle in radians.                                            |
| `ACSC(number)`     | Returns the [inverse cosecant](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions) of a number, in radians, ranging from -π/2 to π/2. |
| `SEC(radians)`     | Returns the [secant](https://en.wikipedia.org/wiki/Trigonometric_functions) of an angle in radians.                                              |
| `ASEC(number)`     | Returns the [inverse secant](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions) of a number, in radians, ranging from 0 to π.        |
| `COT(radians)`     | Returns the [cotangent](https://en.wikipedia.org/wiki/Trigonometric_functions) of an angle in radians.                                           |
| `ACOT(number)`     | Returns the [inverse cotangent](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions) of a number, in radians, ranging from 0 to π.     |
| `SINH(radians)`    | Returns the [hyperbolic sine](https://en.wikipedia.org/wiki/Hyperbolic_functions) of an angle in radians.                                        |
| `ASINH(number)`    | Returns the [inverse hyperbolic sine](https://en.wikipedia.org/wiki/Inverse_hyperbolic_functions) of a number, in radians.                       |
| `COSH(radians)`    | Returns the [hyperbolic cosine](https://en.wikipedia.org/wiki/Hyperbolic_functions) of an angle in radians.                                      |
| `ACOSH(number)`    | Returns the [inverse hyperbolic cosine](https://en.wikipedia.org/wiki/Inverse_hyperbolic_functions) of a number, in radians.                     |
| `TANH(radians)`    | Returns the [hyperbolic tangent](https://en.wikipedia.org/wiki/Hyperbolic_functions) of an angle in radians.                                     |
| `ATANH(number)`    | Returns the [inverse hyperbolic tangent](https://en.wikipedia.org/wiki/Inverse_hyperbolic_functions) of a number, in radians.                    |
| `CSCH(radians)`    | Returns the [hyperbolic cosecant](https://en.wikipedia.org/wiki/Hyperbolic_functions) of an angle in radians.                                    |
| `ACSCH(number)`    | Returns the [inverse hyperbolic cosecant](https://en.wikipedia.org/wiki/Inverse_hyperbolic_functions) of a number, in radians.                   |
| `SECH(radians)`    | Returns the [hyperbolic secant](https://en.wikipedia.org/wiki/Hyperbolic_functions) of an angle in radians.                                      |
| `ASECH(number)`    | Returns the [inverse hyperbolic secant](https://en.wikipedia.org/wiki/Inverse_hyperbolic_functions) of a number, in radians.                     |
| `COTH(radians)`    | Returns the [hyperbolic cotangent](https://en.wikipedia.org/wiki/Hyperbolic_functions) of an angle in radians.                                   |
| `ACOTH(number)`    | Returns the [inverse hyperbolic cotangent](https://en.wikipedia.org/wiki/Inverse_hyperbolic_functions) of a number, in radians.                  |

#### ATAN2

`ATAN2(x, y)`

Examples:

* `ATAN2(2, 1)`

Returns the counterclockwise angle, in radians, from the X axis\
to the point `(x, y)`. Note that the argument order is reversed\
compared to the [typical `atan2()`\
function](https://en.wikipedia.org/wiki/Atan2).

If both arguments are zero, returns zero.

### Statistics functions

| **Function**                                                           | **Description**                                                                                                                                                                                                                                                                                                                                                                                                           |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `AVERAGE([numbers...])`                                                | Returns the arithmetic mean of all values.                                                                                                                                                                                                                                                                                                                                                                                |
| `AVERAGEIF(eval_range, criteria, [numbers_range])`                     | Evaluates each value based on some criteria, and then computes the arithmetic mean of the ones that meet those criteria. If `range_to_average` is given, then values in `range_to_average` are averaged instead wherever the corresponding value in `range_to_evaluate` meets the criteria. See [the documentation](https://docs.quadratichq.com/formulas/criteria) for more details about how criteria work in formulas. |
| `COUNTIF(range, criteria)`                                             | Evaluates each value based on some criteria, and then counts how many values meet those criteria. See [the documentation](https://docs.quadratichq.com/formulas/criteria) for more details about how criteria work in formulas.                                                                                                                                                                                           |
| `COUNTIFS(eval_range1, criteria1, [more_eval_ranges_and_criteria...])` | Evaluates multiple values on they're respective criteria, and then counts how many sets of values met all their criteria. See [the documentation](https://docs.quadratichq.com/formulas/criteria) for more details about how criteria work in formulas.                                                                                                                                                                   |
| `MIN([numbers...])`                                                    | Returns the smallest value. Returns +∞ if given no values.                                                                                                                                                                                                                                                                                                                                                                |
| `MAX([numbers...])`                                                    | Returns the largest value. Returns -∞ if given no values.                                                                                                                                                                                                                                                                                                                                                                 |
| `VAR([numbers...])`                                                    | Returns the variance of all values (sample variance). Uses the formula: Σ(x - μ)²/(n-1) where μ is the mean and n is the count.                                                                                                                                                                                                                                                                                           |
| `STDEV([numbers...])`                                                  | Returns the standard deviation of all values (sample standard deviation). Uses the formula: √(Σ(x - μ)²/(n-1)) where μ is the mean and n is the count.                                                                                                                                                                                                                                                                    |

#### COUNT

`COUNT([numbers...])`

Examples:

* `COUNT(A1:C42, E17)`
* `SUM(A1:A10) / COUNT(A1:A10)`

Returns the number of numeric values.

* Blank cells are not counted.
* Cells containing an error are not counted.

#### COUNTA

`COUNTA([range...])`

Examples:

* `COUNTA(A1:A10)`

Returns the number of non-blank values.

* Cells with formula or code output of an empty string are\
  counted.
* Cells containing zero are counted.
* Cells with an error are counted.

#### COUNTBLANK

`COUNTBLANK([range...])`

Examples:

* `COUNTBLANK(A1:A10)`

Counts how many values in the range are empty.

* Cells with formula or code output of an empty string are\
  counted.
* Cells containing zero are not counted.
* Cells with an error are not counted.

### Logic functions

* `FALSE`, `false` (case-insensitive), and `0` are all considered falsey
* `TRUE`, `true` (case-insensitive), and all nonzero numbers are considered truthy
* Other values produce an error when used in a place expecting a boolean

When used as a number, `TRUE` is equivalent to `1` and `FALSE` is equivalent to `0`.

| **Function**               | **Description**                                                                                  |
| -------------------------- | ------------------------------------------------------------------------------------------------ |
| `TRUE()`                   | Returns `TRUE`.                                                                                  |
| `FALSE()`                  | Returns `FALSE`.                                                                                 |
| `NOT(boolean)`             | Returns `TRUE` if `a` is falsey and `FALSE` if `a` is truthy.                                    |
| `IF(condition, t, f)`      | Returns `t` if `condition` is truthy and `f` if `condition` is falsey.                           |
| `IFERROR(value, fallback)` | Returns `fallback` if there was an error computing `value`; otherwise returns `value`.           |
| `IFNA(value, fallback)`    | Returns `fallback` if there was a "no match" error computing `value`; otherwise returns `value`. |

#### AND

`AND([booleans...])`

Examples:

* `AND(A1:C1)`
* `AND(A1, B12)`

Returns `TRUE` if all values are truthy and `FALSE` if any value\
is falsey.

Returns `TRUE` if given no values.

#### OR

`OR([booleans...])`

Examples:

* `OR(A1:C1)`
* `OR(A1, B12)`

Returns `TRUE` if any value is truthy and `FALSE` if all values\
are falsey.

Returns `FALSE` if given no values.

#### XOR

`XOR([booleans...])`

Examples:

* `XOR(A1:C1)`
* `XOR(A1, B12)`

Returns `TRUE` if an odd number of values are truthy and `FALSE`\
if an even number of values are truthy.

Returns `FALSE` if given no values.

### String functions

| **Function**                | **Description**                                                                                                                                                                                                          |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `CONCATENATE([strings...])` | Same as `CONCAT`, but kept for compatibility.                                                                                                                                                                            |
| `LEN(s)`                    | Returns half the length of the string in [Unicode code-points](https://tonsky.me/blog/unicode/). This is often the same as the number of characters in a string, but not for certain diacritics, emojis, or other cases. |
| `LENB(s)`                   | Returns half the length of the string in bytes, using UTF-8 encoding.                                                                                                                                                    |
| `CODE(s)`                   | Same as `UNICODE`. Prefer `UNICODE`.                                                                                                                                                                                     |
| `CHAR(code_point)`          | Same as `UNICHAR`. Prefer `UNICHAR`.                                                                                                                                                                                     |
| `LOWER(s)`                  | Returns the lowercase equivalent of a string.                                                                                                                                                                            |
| `UPPER(s)`                  | Returns the uppercase equivalent of a string.                                                                                                                                                                            |
| `PROPER(s)`                 | Capitalizes letters that do not have another letter before them, and lowercases the rest.                                                                                                                                |
| `T(v)`                      | Returns a string value unmodified, or returns the empty string if passed a value other than a string.                                                                                                                    |
| `EXACT(s1, s2)`             | Returns whether two strings are exactly equal, using case-sensitive comparison (but ignoring formatting).                                                                                                                |

#### ARRAYTOTEXT

`ARRAYTOTEXT(array, [format])`

Examples:

* `ARRAYTOTEXT({"Apple", "banana"; 42, "Hello, world!"})`
* `ARRAYTOTEXT({"Apple", "banana"; 42, "Hello, world!"}, 1)`

Converts an array of values to a string.

If `format` is 0 or omitted, returns a human-readable\
representation such as `Apple, banana, 42, hello, world!`. If`format` is 1, returns a machine-readable representation in\
valid formula syntax such as `{"Apple", "banana", 42, "Hello, world!"}`. If `format` is any other value, returns an error.

#### CONCAT

`CONCAT([strings...])`

Examples:

* `CONCAT("Hello, ", C0, "!")`
* `"Hello, " & C0 & "!"`

[Concatenates](https://en.wikipedia.org/wiki/Concatenation) all\
values as strings.

`&` can also be used to concatenate text.

#### LEFT

`LEFT(s, [char_count])`

Examples:

* `LEFT("Hello, world!") = "H"`
* `LEFT("Hello, world!", 6) = "Hello,"`
* `LEFT("抱歉，我不懂普通话") = "抱"`
* `LEFT("抱歉，我不懂普通话", 6) = "抱歉，我不懂"`

Returns the first `char_count` characters from the beginning of\
the string `s`.

Returns an error if `char_count` is less than 0.

If `char_count` is omitted, it is assumed to be 1.

If `char_count` is greater than the number of characters in `s`,\
then the entire string is returned.

#### LEFTB

`LEFTB(s, [byte_count])`

Examples:

* `LEFTB("Hello, world!") = "H"`
* `LEFTB("Hello, world!", 6) = "Hello,"`
* `LEFTB("抱歉，我不懂普通话") = ""`
* `LEFTB("抱歉，我不懂普通话", 6) = "抱歉"`
* `LEFTB("抱歉，我不懂普通话", 8) = "抱歉"`

Returns the first `byte_count` bytes from the beginning of the\
string `s`, encoded using UTF-8.

Returns an error if `byte_count` is less than 0.

If `byte_count` is omitted, it is assumed to be 1. If`byte_count` is greater than the number of bytes in `s`, then\
the entire string is returned.

If the string would be split in the middle of a character, then`byte_count` is rounded down to the previous character boundary\
so the the returned string takes at most `byte_count` bytes.

#### RIGHT

`RIGHT(s, [char_count])`

Examples:

* `RIGHT("Hello, world!") = "!"`
* `RIGHT("Hello, world!", 6) = "world!"`
* `RIGHT("抱歉，我不懂普通话") = "话"`
* `RIGHT("抱歉，我不懂普通话", 6) = "我不懂普通话"`

Returns the last `char_count` characters from the end of the\
string `s`.

Returns an error if `char_count` is less than 0.

If `char_count` is omitted, it is assumed to be 1.

If `char_count` is greater than the number of characters in `s`,\
then the entire string is returned.

#### RIGHTB

`RIGHTB(s, [byte_count])`

Examples:

* `RIGHTB("Hello, world!") = "!"`
* `RIGHTB("Hello, world!", 6) = "world!"`
* `RIGHTB("抱歉，我不懂普通话") = ""`
* `RIGHTB("抱歉，我不懂普通话", 6) = "通话"`
* `RIGHTB("抱歉，我不懂普通话", 7) = "通话"`

Returns the last `byte_count` bytes from the end of the string`s`, encoded using UTF-8.

Returns an error if `byte_count` is less than 0.

If `byte_count` is omitted, it is assumed to be 1.

If `byte_count` is greater than the number of bytes in `s`, then\
the entire string is returned.

If the string would be split in the middle of a character, then`byte_count` is rounded down to the next character boundary so\
that the returned string takes at most `byte_count` bytes.

#### MID

`MID(s, start_char, char_count)`

Examples:

* `MID("Hello, world!", 4, 6) = "lo, wo"`
* `MID("Hello, world!", 1, 5) = "Hello"`
* `MID("抱歉，我不懂普通话", 4, 4) = "我不懂普"`

Returns the substring of a string `s` starting at the`start_char`th character and with a length of `char_count`.

Returns an error if `start_char` is less than 1 or if`char_count` is less than 0.

If `start_char` is past the end of the string, returns an empty\
string. If `start_char + char_count` is past the end of the\
string, returns the rest of the string starting at `start_char`.

#### MIDB

`MIDB(s, start_byte, byte_count)`

Examples:

* `MIDB("Hello, world!", 4, 6) = "lo, wo"`
* `MIDB("Hello, world!", 1, 5) = "Hello"`
* `MIDB("抱歉，我不懂普通话", 10, 12) = "我不懂普"`
* `MIDB("抱歉，我不懂普通话", 8, 16) = "我不懂普"`

Returns the substring of a string `s` starting at the`start_byte`th byte and with a length of `byte_count` bytes,\
encoded using UTF-8.

Returns an error if `start_byte` is less than 1 or if`byte_count` is less than 0.

If `start_byte` is past the end of the string, returns an empty\
string. If `start_byte + byte_count` is past the end of the\
string, returns the rest of the string starting at `start_byte`.

If the string would be split in the middle of a character, then`start_byte` is rounded up to the next character boundary and`byte_count` is rounded down to the previous character boundary\
so that the returned string takes at most `byte_count` bytes.

#### UNICODE

`UNICODE(s)`

Examples:

* `UNICODE("a")=97`
* `UNICODE("Alpha")=65`

Returns the first [Unicode](https://en.wikipedia.org/wiki/Unicode) code point in a string as a number.\
If the first character is part of standard (non-extended)[ASCII](https://en.wikipedia.org/wiki/ASCII), then this is the same as its ASCII number.

#### UNICHAR

`UNICHAR(code_point)`

Examples:

* `UNICHAR(97) = "a"`
* `UNICHAR(65) = "A"`

Returns a string containing the given [Unicode](https://en.wikipedia.org/wiki/Unicode) code unit. For\
numbers in the range 0-127, this converts from a number to its\
corresponding [ASCII](https://en.wikipedia.org/wiki/ASCII) character.

#### CLEAN

`CLEAN(s)`

Examples:

* `CLEAN(CHAR(9) & "(only the parenthetical will survive)" & CHAR(10))`

Removes nonprintable [ASCII](https://en.wikipedia.org/wiki/ASCII) characters 0-31 (0x00-0x1F) from a\
string. This removes tabs and newlines, but not spaces.

#### TRIM

`TRIM(s)`

Examples:

* `TRIM(" a b c ")="a b c"`

Removes spaces from the beginning and end of a string `s`, and\
replaces each run of consecutive space within the string with a\
single space.

[Other forms of whitespace](https://en.wikipedia.org/wiki/Whitespace_character), including tabs and\
newlines, are preserved.

#### NUMBERVALUE

`NUMBERVALUE(s, [decimal_sep], [group_sep])`

Examples:

* `NUMBERVALUE("4,000,096.25")`
* `NUMBERVALUE("4.000.096,25")`

Parses a number from a string `s`, using `decimal_sep` as the\
decimal separator and `group_sep` as the group separator.

If `decimal_sep` is omitted, it is assumed to be `.`. If`group_sep` is omitted, it is assumed to be `,`. Only the first\
character of each is considered. If the decimal separator and\
the group separator are the same or if either is an empty\
string, an error is returned.

The decimal separator must appear at most once in the string.\
The group separator must not appear at any point after a decimal\
separator. Whitespace may appear anywhere in the string.\
Whitespace and group separators are ignored and have no effect\
on the returned number.

### Array functions

| **Function** | **Description** |
| ------------ | --------------- |

#### FILTER

`FILTER(array, include, [if_empty])`

Examples:

* `FILTER(A1:C5, D1:D5, "No results")`
* `FILTER(A1:C5, {FALSE; TRUE; TRUE; FALSE; TRUE})`

Filters an array of values by a list of booleans.

`include` must contain either a single row or a single column. If`include` is a single column, then each value in it corresponds to a\
row from `array`; if `include` is a single row, then each value in\
it corresponds to a column from `array`. If the value in `include`\
is truthy, then the corresponding row/column\
from `array` is included in the output. If `include` contains a\
single value, then it corresponds to all of `array`.

If no rows/columns are included in the output, then `if_empty` is\
outputted instead. If no rows/columns are included in the output*and* `if_empty` is omitted, then an error is returned.

#### SORT

`SORT(array, [sort_index], [sort_order], [by_column])`

Examples:

* `SORT(A1:A100)`
* `SORT(A1:C50)`
* `SORT(A1:C50, 3)`
* `SORT(A1:C50, , -1)`
* `SORT(A1:C50, 2, -1)`
* `SORT(A1:F3,,, TRUE)`

Sorts an array of values.

`sort_index` specifies the entry within each row or column to\
sort by. For example, if `sort_index` is `3` when sorting by row\
then each row will be sorted based on its value in the third\
column. If `sort_index` is omitted, then the first entry is\
used.

`sort_order` specifies whether to sort in reverse order. If`sort_order` is `1` or omitted, then the array is sorted in\
ascending order. If it is `-1`, then the array is sorted in\
descending order.

If `by_column` is `true`, then the function operations on\
columns. If `by_column` is `false` or omitted, then the function\
operates on rows.

The sort is [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).

#### UNIQUE

`UNIQUE(array, [by_column], [exactly_once])`

Examples:

* `UNIQUE()`

Removes duplicates rows or columns from an array.

Rows or columns are returned in the order they initially appear;\
subsequent appearances are removed.

If `by_column` is `true`, then the function operations on\
columns. If `by_column` is `false` or omitted, then the function\
operates on rows.

If `exactly_once` is true, then rows and columns that appear\
multiple times are omitted; only rows or columns that appear\
exactly once are included in the output.

#### SUMPRODUCT

`SUMPRODUCT([arrays...])`

Examples:

* `SUMPRODUCT(C2:C5, D2:D5)`

Multiplies arrays componentwise, then returns the sum of all the\
elements. All arrays must have the same size.

For example, `SUMPRODUCT(C2:C5, D2:D5)` is equivalent to`SUM(C2:C5 * D2:D5)`.

### Date & time functions

Other spreadsheet software treats dates, times, and durations as pure numbers, which can cause mistakes and can't represent operations like "plus one month". Instead, Quadratic has separate types of data for each of the following:

* **Date**, such as `April 8, 2024`
* **Time**, such as `2:30 PM`
* **Date time**, such as `April 8, 2024 2:30 PM`
* **Duration**, such as `6 months 15 days 1h30m12s`

In formulas:

* Dates can be constructed using `DATE()` (e.g., `DATE(2024, 4, 8)`)
* Times can be constructed using `TIME()` (e.g., `TIME(14, 30, 0)`)
* Date times can be constructed by adding a date and a time (e.g., `DATE(2024, 4, 8) + TIME(14, 30, 0)`)
* Durations can be constructed using `DURATION.YMD()`, `DURATION.HMS()`, or by adding them both (e.g., `DURATION.YMD(0, 6, 15) + DURATION.HMS(1, 30, 12)`)

Dates, times, and date times can be entered into a cell using the formats above, or other formats (such as `YYYY-MM-DD`). Durations can be entered using long form (such as `1 hour, 30 minutes, 12 seconds`) or short form (such as `1h30m12s`). Durations support the following units:

* **Years**, written `y`, `yr`, `year`, `yrs`, or `years`
* **Months**, written `mo`, `mon`, `month`, or `months`
* **Weeks**, written `w`, `week`, or `weeks`
* **Days**, written `d`, `day`, or `days`
* **Hours**, written `h`, `hr`, `hour`, `hrs`, or `hours`
* **Minutes**, written `m`, `min`, `minute`, `mins`, or `minutes`
* **Seconds**, written `s`, `sec`, `second`, `secs`, or `seconds`
* **Milliseconds**, written `ms`, `millisec`, `millisecond`, or `milliseconds`
* **Microseconds**, written `us`, `µs`, `microsec`, `microsecond`, or `microseconds`
* **Nanoseconds**, written `ns`, `nanosec`, `nanosecond`, or `nanoseconds`
* **Picoseconds**, written `ps`, `picosec`, `picosecond`, or `picoseconds`
* **Femtoseconds**, written `fs`, `femtosec`, `femtosecond`, or `femtoseconds`
* **Attoseconds**, written `as`, `attosec`, `attosecond`, or `attoseconds`

Quadratic automatically converts between years and months (with 1 year = 12 months) and between any units less than one month, but does not convert months into days because months vary in length.

In formulas:

Durations can also be constructed by subtracting two dates, two times, or two datetimes. Durations can be added to or subtracted from all of these types as well, including other durations.

| **Function** | **Description** |
| ------------ | --------------- |

#### NOW

`NOW()`

Examples:

* `NOW()`

Returns the current local date and time.

This depends on the time configuration of the computer where the\
formula is run, which may depend on timezone.

#### TODAY

`TODAY()`

Examples:

* `TODAY()`

Returns the current local date.

This depends on the time configuration of the computer where the\
formula is run, which may depend on timezone.

#### DATE

`DATE(year, month, day)`

Examples:

* `DATE(2024, 04, 08)`
* `DATE(1995, 12, 25)`
* `DATE(1965, 3, 18)`

Returns a specific date from a `year`, `month`, and `day`.

`year`, `month`, and `day` must be numbers, and are rounded to\
the nearest integer.

If `day` is outside the range of days in the given month, then\
it overflows and offsets the `month`. For example, `DATE(2024, 1, 99)` returns `2024-04-08`.

If `month` is outside the range from `1` to `12` (inclusive),\
then it overflows and offsets the `year`. For example,`DATE(2024, 13, 1)` returns `2025-01-01` and `DATE(2024, 0, 1)`\
returns `2023-12-01`.

*Note that February 29, 1900 does not exist.*

To construct a date time, simply add a date and time. For\
example, `DATE(1965, 3, 18) + TIME(8, 34, 51)` was the second\
(in UTC) that Alexei Leonov began the first-ever spacewalk.

#### TIME

`TIME(hour, minute, second)`

Examples:

* `TIME(8, 34, 51)`
* `TIME(2, 56, 0)`
* `TIME(2,30,59.99)`

Returns a specific time from an `hour`, `minute`, and `second`.

`hour`, `minute`, and `second` must be numbers. `hour` and`minute` are rounded to the nearest integer, but `second` may\
have a fractional component.

If `second` is outside the range from `0` to `59` inclusive,\
then it is divided by 60. The remainder is used for `second` and\
the quotient is added to `minute`. For example, `TIME(9, 0, 844)` returns `9:14:04 AM`.

Similarly, if `minute` is outside the range from `0` to `59`\
inclusive, then it is divided by 60. The remainder is used for`minute` and the quotient is added to `hour`. For example,`TIME(16, 70, 45)` returns `5:10:45 PM` and `TIME(12, -1, -1)`\
returns `11:58:59 AM`.

If `hour` is outside the range from `0` to `23`, then it is\
divided by 24. The quotient is discarded and the remainder is\
used for `hour`. For example, `TIME(-8, 30, 0)` returns `4:30:00 PM`.

To construct a date time, simply add a date and time. For\
example, `DATE(1969, 7, 21) + TIME(2, 56, 0)` was the minute (in\
UTC) that Neil Armstrong became the first person to walk on the\
surface of the moon.

#### DURATION.YMD

`DURATION.YMD(years, months, days)`

Examples:

* `DURATION.YMD(0, 0, 60)`
* `DURATION.YMD(-5, 0, 0)`
* `DURATION.YMD(1, 6, 0)`

Returns a duration of `years`, `months`, and `days`.

`years`, `months`, and `days` must be numbers. `years` and`months` are rounded to the nearest integer, but `days` may have\
a fractional component.

Months and years are combined, but days are not. For example,`DURATION.YMD(5, -3, 50)` returns `4y 9mo 50d`.

To construct a duration longer than one day, simply construct\
another duration using `DURATION.YMD` and add it to this one.\
For example, `DURATION.YMD(1, 2, 3) + DURATION.HMS(4, 5, 6)`\
returns `1y 2mo 3d 4h 5m 6s`.

#### DURATION.HMS

`DURATION.HMS(hours, minutes, seconds)`

Examples:

* `DURATION.HMS(0, 2, 30)`
* `DURATION.HMS(6, 0, 0)`
* `DURATION.HMS(24, 0, 0)`

Returns a duration of `hours`, `minutes`, and `seconds`.

`hours`, `minutes`, and `seconds` must be numbers. `hours` and`minutes` are rounded to the nearest integer, but `seconds` may\
have a fractional component.

Seconds, minutes, and hours are combined. For example,`DURATION.YMD(1, 72, 72)` returns `2h 13m 12s`.

To construct a duration longer than one day, simply construct\
another duration using `DURATION.YMD` and add it to this one.\
For example, `DURATION.YMD(1, 2, 3) + DURATION.HMS(4, 5, 6)`\
returns `1y 2mo 3d 4h 5m 6s`.

#### YEAR

`YEAR(date)`

Examples:

* `YEAR(DATE(2024, 4, 8)) = 2024`
* `YEAR(TIME(30, 16, 45)) = 0`
* `YEAR(DURATION.HMS(6, 10, 15)) = 0`
* `YEAR(DURATION.YMD(1, 2, 3)) = 1`
* `YEAR(DURATION.YMD(-1, -2, 3)) = -2`
* `YEAR(DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 1`
* `YEAR(DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 1`
* `YEAR(-DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = -2`
* `YEAR(-DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = -2`

Returns the year portion of a date or duration.

* The year portion of a date or date time is typically between`1900` and `2100`.
* The year portion of a time is always `0`.
* The year portion of a duration is rounded down, and may be\
  negative. For example, the year portion of `1y 4mo` is `1` and\
  the year portion of `-1y -4mo` is `-2`.
* The year portion of a number of days is not well-defined, so\
  an error is returned.

#### MONTH

`MONTH(date)`

Examples:

* `MONTH(DATE(2024, 4, 8)) = 4`
* `MONTH(TIME(30, 16, 45)) = 0`
* `MONTH(DURATION.HMS(6, 10, 15)) = 0`
* `MONTH(DURATION.YMD(1, 2, 3)) = 2`
* `MONTH(DURATION.YMD(-1, -2, 3)) = 10`
* `MONTH(DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 1`
* `MONTH(DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 1`
* `MONTH(-DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 1`
* `MONTH(-DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 1`

Returns the month portion of a date or duration.

* The month portion of a date or date time is always between `1`\
  and `12` (inclusive).
* The month portion of a time is always `0`.
* The month portion of a duration is always between `0` and`11`, even if the duration is negative. For example, the month\
  portion of `1y 4mo` is `4` and the month portion of `-1y -4mo`\
  is `8`.
* The month portion of a number of days is not well-defined, so\
  an error is returned.

#### DAY

`DAY(date)`

Examples:

* `DAY(DATE(2024, 4, 8)) = 8`
* `DAY(TIME(30, 16, 45)) = 0`
* `DAY(DURATION.HMS(6, 10, 15)) = 0`
* `DAY(DURATION.YMD(1, 2, 3)) = 3`
* `DAY(DURATION.YMD(-1, -2, 3)) = 3`
* `DAY(DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 3`
* `DAY(DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 2`
* `DAY(-DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = -3`
* `DAY(-DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = -4`

Returns the day portion of a date or duration.

* The day portion of a date or date time is always between `1`\
  and `31` (inclusive).
* The day portion of a time is always `0`.
* The day portion of a duration is rounded down, and may be\
  negative.
* The day portion of a number of days is equal to its integer\
  part when rounded down.

#### HOUR

`HOUR(time)`

Examples:

* `HOUR(TIME(30, 16, 45)) = 6`
* `HOUR(TIME(30, 0, -1)) = 5`
* `HOUR(TIME(0, 0, 0)) = 0`
* `HOUR(TIME(0, 0, -1)) = 23`
* `HOUR(789.084) = 2`
* `HOUR(-789.084) = 57`
* `HOUR(DURATION.HMS(6, 10, 15)) = 6`
* `HOUR(DURATION.YMD(1, 2, 3)) = 0`
* `HOUR(DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 6`
* `HOUR(DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 17`
* `HOUR(-DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 6`
* `HOUR(-DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 17`

Returns the hour portion of a time or duration.

* The hour portion of a date is always zero.
* The hour portion of a time or date time is always between `0`\
  and `23` (inclusive).
* The hour portion of a duration is always between `0` and `23`\
  (inclusive), even if the duration is negative.
* The hour portion of a number of days is equal to its\
  fractional part, times `24`, rounded down. It is always\
  between `0` and `23` (inclusive), even if the original number\
  is negative.

#### MINUTE

`MINUTE(time)`

Examples:

* `MINUTE(TIME(30, 16, 45)) = 16`
* `MINUTE(TIME(30, 0, -1)) = 59`
* `MINUTE(TIME(0, 0, 0)) = 0`
* `MINUTE(TIME(0, 0, -1)) = 59`
* `MINUTE(789.001389) = 2`
* `MINUTE(-789.001389) = 57`
* `MINUTE(DURATION.HMS(6, 10, 15)) = 10`
* `MINUTE(DURATION.YMD(1, 2, 3)) = 0`
* `MINUTE(DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 10`
* `MINUTE(DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 49`
* `MINUTE(-DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 10`
* `MINUTE(-DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 49`

Returns the minute portion of a time or duration.

* The minute portion of a date is always zero.
* The minute portion of a time or date time is always between`0` and `59` (inclusive).
* The minute portion of a duration is always between `0` and`59` (inclusive), even if the duration is negative.
* The minute portion of a number of days is equal to its\
  fractional part, times `1440`, rounded down. It is always\
  between `0` and `59` (inclusive), even if the original number\
  is negative.

#### SECOND

`SECOND(time)`

Examples:

* `SECOND(TIME(30, 16, 45)) = 45`
* `SECOND(TIME(30, 0, -1)) = 59`
* `SECOND(TIME(0, 0, 0)) = 0`
* `SECOND(TIME(0, 0, -1)) = 59`
* `SECOND(0.5557291667) = 15`
* `SECOND(-0.5557291667) = 44`
* `SECOND(DURATION.HMS(6, 10, 15)) = 15`
* `SECOND(DURATION.YMD(1, 2, 3)) = 0`
* `SECOND(DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 15`
* `SECOND(DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 45`
* `SECOND(-DURATION.YMD(1, 2, 3) + DURATION.HMS(6, 10, 15)) = 15`
* `SECOND(-DURATION.YMD(1, 2, 3) - DURATION.HMS(6, 10, 15)) = 45`

Returns the second portion of a time or duration.

* The second portion of a date is always zero.
* The second portion of a time or date time is always between`0` and `59` (inclusive).
* The second portion of a duration is always between `0` and`59` (inclusive), even if the duration is negative.
* The second portion of a number of days is equal to its\
  fractional part, times `86400`, rounded down. It is always\
  between `0` and `59` (inclusive), even if the original number\
  is negative.

#### EDATE

`EDATE(day, months_offset)`

Examples:

* `EDATE(DATE(2024, 04, 08), 8)`

Adds a number of months to a date.

If the date goes past the end of the month, the last day in the\
month is returned.

#### EOMONTH

`EOMONTH(day, [months_offset])`

Examples:

* `EOMONTH(DATE(2024, 04, 08))`
* `EOMONTH(DATE(2024, 04, 08), 8)`

Returns the last day of the month that is `months_offset` months\
after `day`.

* If `months_offset` is zero, then the value returned is the\
  last day of the month containing `day`.
* If `months_offset` is positive, then the day returned is that\
  many months later.
* If `months_offset` is negative, then the day returned is that\
  many months earlier.

### Lookup functions

| **Function**               | **Description**                                    |
| -------------------------- | -------------------------------------------------- |
| `INDIRECT(cellref_string)` | Returns the value of the cell at a given location. |

#### VLOOKUP

`VLOOKUP(search_key, search_range, output_col, [is_sorted])`

Examples:

* `VLOOKUP(17, A1:C10, 3)`
* `VLOOKUP(17, A1:C10, 2, FALSE)`

Searches for a value in the first vertical column of a range and\
return the corresponding cell in another vertical column, or an\
error if no match is found.

If `is_sorted` is `TRUE`, this function uses a [binary search\
algorithm](https://en.wikipedia.org/wiki/Binary_search_algorithm),\
so the first column of `search_range` must be sorted, with\
smaller values at the top and larger values at the bottom;\
otherwise the result of this function will be meaningless. If`is_sorted` is omitted, it is assumed to be `false`.

If any of `search_key`, `output_col`, or `is_sorted` is an\
array, then they must be compatible sizes and a lookup will be\
performed for each corresponding set of elements.

#### HLOOKUP

`HLOOKUP(search_key, search_range, output_row, [is_sorted])`

Examples:

* `HLOOKUP(17, A1:Z3, 3)`
* `HLOOKUP(17, A1:Z3, 2, FALSE)`

Searches for a value in the first horizontal row of a range and\
return the corresponding cell in another horizontal row, or an\
error if no match is found.

If `is_sorted` is `TRUE`, this function uses a [binary search\
algorithm](https://en.wikipedia.org/wiki/Binary_search_algorithm),\
so the first row of `search_range` must be sorted, with smaller\
values at the left and larger values at the right; otherwise the\
result of this function will be meaningless. If `is_sorted` is\
omitted, it is assumed to be `false`.

If any of `search_key`, `output_col`, or `is_sorted` is an\
array, then they must be compatible sizes and a lookup will be\
performed for each corresponding set of elements.

#### XLOOKUP

`XLOOKUP(search_key, search_range, output_range, [fallback], [match_mode], [search_mode])`

Examples:

* `XLOOKUP("zebra", A1:Z1, A4:Z6)`
* `XLOOKUP({"zebra"; "aardvark"}, A1:Z1, A4:Z6)`
* `XLOOKUP(50, C4:C834, B4:C834, {-1, 0, "not found"}, -1, 2)`

Searches for a value in a linear range and returns a row or\
column from another range.

`search_range` must be either a single row or a single column.

**Match modes**

There are four match modes:

* 0 = exact match (default)
* -1 = next smaller
* 1 = next larger
* 2 = wildcard

See [the documentation](https://docs.quadratichq.com/formulas/wildcards) for more details about how wildcards work in formulas.

**Search modes**

There are four search modes:

* 1 = linear search (default)
* -1 = reverse linear search
* 2 = [binary\
  search](https://en.wikipedia.org/wiki/Binary_search_algorithm)
* -2 = reverse binary search

Linear search finds the first matching value, while reverse\
linear search finds the last matching value.

Binary search may be faster than linear search, but binary\
search requires that values are sorted, with smaller values at\
the top or left and larger values at the bottom or right.\
Reverse binary search requires that values are sorted in the\
opposite direction. If `search_range` is not sorted, then the\
result of this function will be meaningless.

Binary search is not compatible with the wildcard match mode.

**Result**

If `search_range` is a row, then it must have the same width as`output_range` so that each value in `search_range` corresponds\
to a column in `output_range`. In this case, the **search axis**\
is vertical.

If `search_range` is a column, then it must have the same height\
as `output_range` so that each value in `search_range`\
corresponds to a row in `output_range`. In this case, the**search axis** is horizontal.

If a match is not found, then `fallback` is returned instead. If\
there is no match and `fallback` is omitted, then returns an\
error.

If any of `search_key`, `fallback`, `match_mode`, or`search_mode` is an array, then they must be compatible sizes\
and a lookup will be performed for each corresponding set of\
elements. These arrays must also have compatible size with the\
non-search axis of `output_range`.

#### MATCH

`MATCH(search_key, search_range, [match_mode])`

Examples:

* `MATCH(12, {10, 20, 30})`
* `MATCH(19, {10, 20, 30}, -1)`
* `MATCH("A", {"a"; "b"; "c"}, 0)`

Searches for a value in a range and returns the index of the\
first match, starting from 1.

If `match_mode` is `1` (the default), then the index of the*greatest value less than* `search_key` will be returned. In\
this mode, `search_range` must be sorted in ascending order,\
with smaller values at the top or left and larger values at the\
bottom or right; otherwise the result of this function will be\
meaningless.

If `match_mode` is `-1`, then the index of the *smallest value*\
*greater than* `search_key` will be returned. In this mode,`search_range` must be sorted in ascending order, with larger\
values at the top or left and smaller values at the bottom or\
right; otherwise the result of this function will be\
meaningless.

If `match_mode` is `0`, then the index of the first value*equal* to `search_key` will be returned. In this mode,`search_range` may be in any order. `search_key` may also be a\
wildcard.

See [the documentation](https://docs.quadratichq.com/formulas/wildcards) for more details about how wildcards work in formulas.

#### INDEX

`INDEX(range, [row], [column], [range_num])`

Examples:

* `INDEX({1, 2, 3; 4, 5, 6}, 1, 3)`
* `INDEX(A1:A100, 42)`
* `INDEX(A6:Q6, 12)`
* `INDEX((A1:B6, C1:D6, D1:D100), 1, 5, C6)`
* `E1:INDEX((A1:B6, C1:D6, D1:D100), 1, 5, C6)`
* `INDEX((A1:B6, C1:D6, D1:D100), 1, 5, C6):E1`
* `INDEX(A3:Q3, A2):INDEX(A6:Q6, A2)`

Returns the element in `range` at a given `row` and `column`. If\
the array is a single row, then `row` may be omitted; otherwise\
it is required. If the array is a single column, then `column`\
may be omitted; otherwise it is required.

If `range` is a group of multiple range references, then the\
extra parameter `range_num` indicates which range to index from.

When `range` is a range references or a group of range\
references, `INDEX` may be used as part of a new range\
reference.

### Financial functions

Financial functions for calculating loan payments, interest rates, and other financial calculations.

| **Function** | **Description** |
| ------------ | --------------- |

#### PMT

`PMT(rate, nper, pv, [fv], [payment_type])`

Examples:

* `PMT(0.08/12, 12*5, 10000)`
* `PMT(0.06/12, 24, 5000, 0, 1)`

Calculates the payment for a loan based on constant payments and a constant interest rate.

* rate: The interest rate per period (e.g., 0.08/12 for 8% annual rate with monthly payments)
* nper: The total number of payments (e.g., 5\*12 for 5 years of monthly payments)
* pv: The present value (the loan amount)
* \[fv]: The future value (default 0)
* \[type]: When payments are due (0=end of period, 1=beginning of period, default 0)

Returns the negative of the payment amount (since it represents money you pay out).

### Not yet implemented

We are continually adding new formula functions; see the [list of functions that we are currently building](https://github.com/quadratichq/quadratic/issues/337). If there is a formula function that is not implemented and not on that list please [contact us](https://www.quadratichq.com/contact).&#x20;


# LET & LAMBDA

## LET & LAMBDA

Quadratic supports Excel's modern formula-composition functions, letting you name intermediate values and define custom functions inline.

### LET

`LET(name1, value1, ..., calculation)` defines named values inside a formula, then uses them in a final calculation. It improves readability and avoids repeating expressions.

```
=LET(revenue, SUM(Sales[Amount]), cost, SUM(Costs[Amount]), revenue - cost)
```

### LAMBDA

`LAMBDA(param1, ..., calculation)` defines a reusable custom function inline. Combine it with array functions — `MAP`, `REDUCE`, `BYROW`, `BYCOL`, `SCAN`, and `MAKEARRAY` — to apply custom logic across ranges:

```
=MAP(A1:A10, LAMBDA(x, x * 1.08))
```

```
=REDUCE(0, B1:B20, LAMBDA(acc, v, acc + MAX(v, 0)))
```

### ISOMITTED

Inside a `LAMBDA`, `ISOMITTED(argument)` returns `TRUE` if an optional argument was not supplied — useful for default values.

### STOCKHISTORY

Quadratic also supports the Excel-compatible `STOCKHISTORY` function for retrieving historical stock prices directly in the sheet:

```
=STOCKHISTORY("AAPL", "2025-01-01", "2025-12-31")
```

For richer financial data — fundamentals, statements, news, technical indicators — see Financial data in Python.

For the full function reference, see Functions and operators.


# Arrays

Write formulas that operate on many cells at once.

An array can be written using `{}`, with `,` between values within a row and `;` between rows. For example, `{1, 2, 3; 4, 5, 6}` is an array with two rows and three columns:

| 1 | 2 | 3 |
| - | - | - |
| 4 | 5 | 6 |

Arrays cannot be empty and every row must be the same length.

Numeric ranges (such as `1..10`) and cell ranges (such as `A1:A10`) also produce arrays. All operators and most functions can operate on arrays, following these rules:

1. Operators always operate element-wise. For example, `{1, 2, 3} + {10, 20, 30}` produces `{11, 22, 33}`.
2. Functions that take a fixed number of values operate element-wise. For example, `NOT({TRUE, TRUE, FALSE})` produces `{FALSE, FALSE, TRUE}`.
3. Functions that can take any number of values expand the array into individual values. For example, `SUM({1, 2, 3})` is the same as `SUM(1, 2, 3)`.

When arrays are used element-wise, they must be the same size. For example, `{1, 2} + {10, 20, 30}` produces an error.

When an array is used element-wise with a single value, the value is expanded into an array of the same size. For example, `{1, 2, 3} + 10` produces `{11, 12, 13}`.


# Criteria

Using Criteria in formulas to match certain cells.

Some functions, such as `SUMIF()`, take a **criteria** parameter that other values are compared to. A criteria value can be a literal value, such as `1`, `FALSE`, `"blue"`, etc. A literal value checks for equality (case-insensitive). However, starting a string with a comparison operator enables more complex criteria:

| **Symbol**               | **Description**                                                  |
| ------------------------ | ---------------------------------------------------------------- |
| `"=blue"` or `"==blue"`  | Equal comparison (supports [wildcards](/formulas/wildcards))     |
| `"<>blue"` or `"!=blue"` | Not-equal comparison (supports [wildcards](/formulas/wildcards)) |
| `"<blue"`                | Less-than comparison                                             |
| `">blue"`                | Greater-than comparison                                          |
| `"<=blue"`               | Less-than-or-equal comparison                                    |
| `">=blue"`               | Greater-than-or-equal comparison                                 |


# Wildcards

Using Wildcards in formulas to match certain cells.

Wildcard patterns can be used with several formula functions, including `MATCH` (with a `match_mode`of `0`) and `XLOOKUP`(with `match_mode` of `2`). They can also be used in [criteria](/formulas/criteria) with any equality-based comparison (`=`, `==`, `<>`, `!=`, or no operator)

In wildcards, the special symbols `?` and `*` can be used to match certain text patterns: `?` matches any single character and `*` matches any sequence of zero or more characters. For example, `DEFEN?E` matches the strings `"defence"` and `"defense"`, but not `"defenestrate"`. `*ATE` matches the strings `"ate"`, `"inflate"`, and `"late"`, but not `"wait"`. Multiple `?` and `*` are also allowed.

To match a literal `?` or `*`, prefix it with a tilde `~`: for example, `COUNTIF(A1:A10, "HELLO~?")` matches only the string `"Hello?"` (and uppercase/lowercase variants).

To match a literal tilde `~` in a string with `?` or `*`, replace it with a double tilde `~~`. For example, `COUNTIF(A1:A10, "HELLO ~~?")` matches the strings `"hello ~Q"`, `"hello ~R"`, etc. If the string does not contain any `?` or `*`, then tildes do not need to be escaped.


# Navigating

Navigate the Quadratic spreadsheet.

For advanced users, feel free to [skip to Shortcuts](/spreadsheet/shortcuts) for the condensed list of shortcuts for navigating the spreadsheet. Read the enclosed for a more in-depth feature-by-feature exploration of navigating Quadratic.&#x20;

## Cell type palette `/`

Pressing `/` on a cell will open the cell type palette. Once open, choose the type of cell you want to create.&#x20;

## Panning

Quadratic was built to be the most fluid navigating spreadsheet. Enabling this is the 60 FPS experience from WASM + WebGL. Try it out holding `Spacebar` and then panning you can experience the ease of navigation for yourself.&#x20;

<figure><img src="/files/SV1MB42asxvDbl4J4xwX" alt="" width="563"><figcaption></figcaption></figure>

## Presentation mode

You can use presentation mode to directly present your visualizations or data story to your audience with `Ctrl` + `.`  (Mac: `⌘ Command` + `.` )

## Command Palette

Simple search pop-up for all in-app actions. Open the command palette by pressing `Ctrl` + `p` (Mac: `⌘ Command` + `p`

<figure><img src="/files/JnoKjYXlJiNsryqmDeqR" alt="" width="375"><figcaption><p>Open palette with <code>Ctrl</code> + <code>p</code> (Mac: <code>⌘ Command</code> + <code>p</code>)</p></figcaption></figure>

## Seamless Zooming

Zoom in: `Ctrl` + `+`  (Mac: `⌘ Command` + `+` )\
Zoom out: `Ctrl` + `-`  (Mac: `⌘ Command` + `-` )

<figure><img src="/files/HmvM5NOikyv3C51J1PpQ" alt=""><figcaption></figcaption></figure>

## **Formatting**

`Right-click` a cell to open a menu of formatting options.

<figure><img src="/files/L4Tnyk7coD7RAsJ2LGBX" alt=""><figcaption></figcaption></figure>

## Add tabs

Add tabs to your sheet via the bottom left of the sheet.&#x20;

<figure><img src="/files/NDNkNK5g5E5h9OeUQOQS" alt=""><figcaption></figcaption></figure>

You can add colors, rearrange, rename, and access more settings by right clicking a tab.

<figure><img src="/files/v40CW3rvS6cQzyXJKE3F" alt="" width="195"><figcaption></figcaption></figure>

## More spreadsheet basics

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td></td><td><span data-gb-custom-inline data-tag="emoji" data-code="1f4cf">📏</span> Learn more about files and sharing -></td><td></td><td><a href="/pages/KWVkB1nVs4kr4igpgSpo">/pages/KWVkB1nVs4kr4igpgSpo</a></td></tr><tr><td></td><td><span data-gb-custom-inline data-tag="emoji" data-code="1f6e3">🛣️</span> Get a full list of navigation shortcuts -> </td><td></td><td><a href="/pages/EhLwpp0oUiMrowRotlDb">/pages/EhLwpp0oUiMrowRotlDb</a></td></tr><tr><td></td><td><span data-gb-custom-inline data-tag="emoji" data-code="1f522">🔢</span> Learn how to get data into the sheet -> </td><td></td><td><a href="/pages/SojPu6M5U3IXXPsR3QVh">/pages/SojPu6M5U3IXXPsR3QVh</a></td></tr><tr><td></td><td><span data-gb-custom-inline data-tag="emoji" data-code="1f4d7">📗</span> Learn how to present and share your sheets -></td><td></td><td><a href="/pages/4kwUhQaYrLu4JeSGE1tS">/pages/4kwUhQaYrLu4JeSGE1tS</a></td></tr></tbody></table>


# Formula bar

## Formula bar

The formula bar sits below the toolbar and shows the active cell's address and contents. It's the fastest way to read and edit formulas without opening the full code editor.

### What it does

* **Edit in place** — type values or formulas directly; autocomplete and function hints appear as you type.
* **Reference highlighting** — cell and range references in your formula are color-highlighted on the grid.
* **Expand** — long formulas can expand the bar to multiple lines.
* **Code cells** — selecting a Python, JavaScript, or SQL cell shows the language and an AI-generated summary of what the code does, with one-click actions to open the code editor or ask about it in the AI chat.
* **Rename tables and columns** — select a table name or column header and rename it right from the bar.

### Show or hide

Toggle the formula bar from the **View** menu or in Settings. When hidden, pressing `/` in a cell falls back to the inline editor.


# Freeze panes

## Freeze panes

Freeze rows, columns, or both so labels stay on screen as you move through large datasets.

### How to freeze

* **Drag the corner handles** at the top-left of the grid to freeze rows and/or columns.
* Use the **View** menu, or right-click a row or column header.
* Ask the AI: "freeze the first two rows."

Frozen panes are preserved when importing from Google Sheets and Excel, and can be set programmatically via the Developer API and MCP server.

### Unfreeze

Drag the handles back to the corner, or unfreeze from the **View** menu.


# Files

Files are a way to share or save your work for non-team users.

All Files are saved on the Quadratic cloud so you can access your files from any device. Files auto-save as you work if you are connected to the online cloud service.&#x20;

## Local saving&#x20;

When disconnected from the Quadratic Cloud, you can continue working as your changes are saved locally to your browser storage. Those offline changes will sync once you've reconnected to the Quadratic Cloud service.&#x20;

<figure><img src="/files/yVFekew7YEWBA4kxlKbu" alt=""><figcaption></figcaption></figure>

If you've made changes while offline, once you've reconnected to the Cloud service on that machine, you'll get a prompt asking if you want to sync your offline changes.&#x20;

You can either let the unsaved changes sync, or if you have progress you did on a separate machine that was connected, you may want to undo these changes as offline changes can overwrite newer online changes.&#x20;

<figure><img src="/files/rwtpkhV9WW4TvvQx6Y3Q" alt=""><figcaption></figcaption></figure>

## Download files

You can download and share local files from Quadratic. Simply navigate to the top left and use `File` --> `Download local copy`.&#x20;

<figure><img src="/files/2woiUdDvZXMpt2kvfqx0" alt="" width="375"><figcaption><p>Download local copy from the file menu.</p></figcaption></figure>

## Browser permissions

Some browsers have strict permissioning which may block certain browser actions - the only action identified thus far that will be blocked by lack of permissions is to `copy selection as PNG`.&#x20;

If you've denied the sheet access to perform permissioned actions the first time this feature will not work. You will need to grant the browser file system access to fix this error.&#x20;


# Shortcuts

Shortcuts for navigating your spreadsheets faster.

As you gain familiarity with the spreadsheet, start navigating faster with shortcuts. We break the cheat sheet down into a few sections of shortcuts.&#x20;

## Code and Formula Editor Shortcuts&#x20;

<table><thead><tr><th>Code and Formula Editor</th><th width="230.33333333333331">Windows / Linux</th><th>Mac</th></tr></thead><tbody><tr><td>Select Cell Type</td><td><code>=</code></td><td><code>=</code></td></tr><tr><td>Run or Re-Run</td><td><code>Ctrl</code> + <code>Enter</code></td><td><code>⌘ Command</code> + <code>Enter</code> </td></tr><tr><td>Close Editor</td><td><code>Esc</code></td><td><code>Esc</code></td></tr><tr><td>Run all cells in sheet</td><td><code>Shift</code> + <code>Windows</code> + <code>Enter</code></td><td><code>Shift</code> + <code>⌘ Command</code> + <code>Enter</code></td></tr><tr><td>Run all cells in file</td><td><code>Shift</code> + <code>Alt</code> + <code>Windows</code> + <code>Enter</code></td><td><code>Shift</code> + <code>Option</code> + <code>⌘ Command</code> + <code>Enter</code></td></tr></tbody></table>

## Files and Editing Shortcuts&#x20;

<table><thead><tr><th>Files and Editing</th><th width="214.33333333333331">Windows / Linux</th><th>Mac</th></tr></thead><tbody><tr><td>Open File</td><td><code>Ctrl</code> + <code>o</code></td><td><code>⌘ Command</code> + <code>o</code></td></tr><tr><td>Undo</td><td><code>Ctrl</code> + <code>z</code></td><td><code>⌘ Command</code> + <code>z</code></td></tr><tr><td>Redo</td><td><code>Ctrl</code> + <code>Shift</code> + <code>z</code></td><td><code>⌘ Command</code> + <code>⇧ Shift</code> + <code>z</code></td></tr><tr><td>Cut</td><td><code>Ctrl</code> + <code>x</code></td><td><code>⌘ Command</code> + <code>x</code></td></tr><tr><td>Copy</td><td><code>Ctrl</code> + <code>c</code></td><td><code>⌘ Command</code> + <code>c</code></td></tr><tr><td>Paste</td><td><code>Ctrl</code> + <code>p</code></td><td><code>⌘ Command</code> + <code>v</code></td></tr><tr><td>Copy as PNG</td><td><code>Ctrl</code> + <code>Shift</code> + <code>c</code></td><td><code>⌘ Command</code> + <code>⇧ Shift</code> + <code>c</code></td></tr></tbody></table>

## Cell Selection and Formatting&#x20;

<table><thead><tr><th width="255.33333333333331">Cell Selection &#x26; Formatting</th><th width="208">Windows / Linux</th><th>Mac</th></tr></thead><tbody><tr><td>Format Menu</td><td><code>Right-click</code></td><td><code>Right-click</code></td></tr><tr><td>Bold</td><td><code>Ctrl</code> + <code>b</code></td><td><code>⌘ Command</code> or <code>⌃ Control</code> + <code>b</code></td></tr><tr><td>Italic</td><td><code>Ctrl</code> + <code>i</code></td><td><code>⌘ Command</code> or <code>⌃ Control</code> + <code>i</code></td></tr><tr><td>Clear All Formatting </td><td><code>Ctrl</code> + <code>\</code></td><td><code>⌘ Cmd</code> + <code>\</code></td></tr><tr><td>Select Multiple Cells</td><td><code>Shift</code> + <code>Arrow keys</code></td><td><code>⇧ Shift</code> + <code>Arrow keys</code> </td></tr></tbody></table>

## Navigation and View

<table><thead><tr><th>Navigation &#x26; View</th><th width="225.33333333333331">Windows / Linux</th><th>Mac</th></tr></thead><tbody><tr><td>Go to Cell</td><td><code>Ctrl</code> + <code>g</code></td><td><code>⌘ Command</code> + <code>g</code></td></tr><tr><td>Command Palette </td><td><code>Ctrl</code> + <code>p</code></td><td><code>⌘ Command</code> + <code>p</code> </td></tr><tr><td>Presentation Mode</td><td><code>Ctrl</code> + <code>.</code> </td><td><code>⌘ Command</code> + <code>.</code></td></tr><tr><td>Drag Canvas</td><td><code>Spacebar</code> (hold)</td><td><code>Spacebar</code> (hold)</td></tr><tr><td>Zoom In</td><td><code>Ctrl</code> + <code>+</code> </td><td><code>⌘ Command</code> + <code>+</code></td></tr><tr><td>Zoom Out</td><td><code>Ctrl</code> + <code>-</code></td><td><code>⌘ Command</code> + <code>-</code></td></tr><tr><td>Zoom to Selection</td><td><code>Ctrl</code> + <code>8</code> </td><td><code>⌘ Command</code> + <code>8</code></td></tr><tr><td>Zoom to Fit</td><td><code>Ctrl</code> + <code>9</code> </td><td><code>⌘ Command</code> + <code>9</code></td></tr><tr><td>Zoom to 100%</td><td><code>Ctrl</code> + <code>0</code></td><td><code>⌘ Command</code> + <code>0</code> </td></tr></tbody></table>


# Insert/delete rows and columns

Insert and delete any number of rows and columns in your sheets.

You can insert and delete multiple rows or columns by right clicking a row, column, or selection and using the available options.

<figure><img src="/files/6eDpLUeHU3RyuVaEc9PX" alt=""><figcaption></figcaption></figure>


# Data validation

Use data validation to build no-code logic into your spreadsheets.

## Getting started&#x20;

To get started with **Data validation**, right click a cell and use the expand menu. Select **Data validation**.&#x20;

**Data validation** can also be found from the command palette (ctrl/cmd+p).&#x20;

{% hint style="info" %}
Results from data validations can be read by programming languages like Python, JavaScript, and Formulas in Quadratic. Combine code with no-code data validations to build unlimited logic and applications for your team.&#x20;
{% endhint %}

<figure><img src="/files/nj6AWwcRBupzjApECHVx" alt=""><figcaption><p>Right click --> "..." --> Data validation</p></figcaption></figure>

## Dropdowns

You can create dropdowns from statically entered list or by reading from existing data in the sheet. In the following example the data in our dropdown is from column 3.&#x20;

You can also search inside of dropdowns to filter for the criteria you're looking for.&#x20;

<figure><img src="/files/524RIUzVG2vtfxgRT7pI" alt=""><figcaption></figcaption></figure>

## Checkboxes

Checkboxes resolve to TRUE or FALSE and can be read by programming languages. Note the following checkbox which is read into Python; if TRUE the code runs to calculate top 5 by set criteria, if FALSE the code doesn't run. You can use these checkboxes for all sorts of logic.

<figure><img src="/files/fqUwseSSZTmrW2dvETkQ" alt="" width="375"><figcaption></figcaption></figure>

## Insert message

You can insert messages to cells using the message validation. In this example we describe where the data for this validation is coming from in a static message that displays each time the cell is clicked.&#x20;

<figure><img src="/files/QC9LHAxOXM7MdZrEc306" alt="" width="375"><figcaption></figcaption></figure>


# Present & share

Glean insights from your data, visually.

## Present & share

1. [Present your data directly:](#present-your-data) make your spreadsheet more viewable while not editing.
2. [Visualize your data:](#visualize-your-data) turn raw data into beautiful visualizations.
3. [Share your work:](#share-your-work) let others play with and edit your work as a team.
4. [Copy as PNG:](#copy-as-png) copy and paste data into any presentation, design tool, or anywhere else you'd want to share your PNG.
5. [Export your file:](#export-your-file) download your work as Excel, CSV, or a Quadratic file.
6. [Embed as an iFrame:](#embed-in-a-webpage-via-iframe) get your spreadsheets into any page on the web.

### 1. Present your data

`Ctrl` + `.` (Mac: `⌘ Command` + `.`) enters presentation mode, removing the grid lines to present your data and visualizations, seamlessly.

Notice how the grid and toolbars disappear in presentation mode, enabling a smooth presentation mode. We recommend using `Spacebar` + `Mouse` to smoothly pan as you present your workbooks.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FK77fHyBlfSdY56O0RBr7%2Fimage.png?alt=media&#x26;token=370e2669-534d-48b7-b54b-3a6f735d99f2" alt=""><figcaption></figcaption></figure>

### 2. Visualize your data

Create beautiful Plotly charts in Quadratic. View the visualize your data page to learn more.

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f4ca">📊</span> <strong>Learn how to:</strong><br>Create visualizations in Quadratic -></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr></tbody></table>

### 3. Share your work

By sharing, you allow other users to view your sheet, code, etc.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FDTuAs4dMNgdFn39L0jve%2FCleanShot%202024-01-22%20at%2014.48.13%402x.png?alt=media&#x26;token=686d14c5-98b0-4caa-8391-9f72dfa4777f" alt=""><figcaption></figcaption></figure>

Files shared publicly can be viewed by anyone with the link — viewers without edit access see a read-only sheet.

### 4. Copy as PNG

Select the cells you wish to copy and simply `Right-click` and select copy as PNG or use the shortcut `Ctrl` + `Shift` + `c` (Mac: `⌘ Command` + `Shift` + `c`). You can also download a selection as a `.png` file.

You can then paste your PNG into your presentations, articles, etc.

<mark style="background-color:yellow;">Note: the below sample data has used the copy-paste as PNG feature to copy/paste straight into our docs!</mark>

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FuoPkfS7BLrPamLgOpMkS%2Fimage.png?alt=media&#x26;token=e921ad50-06f3-497a-8655-a47a29052570" alt=""><figcaption></figcaption></figure>

### 5. Export your file

Export from **File > Download**:

| Format            | Scope                     |
| ----------------- | ------------------------- |
| Excel (.xlsx)     | Whole file                |
| CSV               | Current sheet             |
| Quadratic (.grid) | Whole file, native format |
| PNG               | Current selection         |

{% hint style="info" %}
File exports (Excel, CSV, .grid) are included in paid plans — see Plans & billing.
{% endhint %}

### 6. Embed in a webpage via iFrame

If you have public sharing permissions turned ON in your spreadsheet, you can freely embed your spreadsheet in any site that allows embedding. Use the URL to your spreadsheet in an `<iframe>` HTML tag, include ?embed at the end of the URL. E.g. [`https://app.quadratichq.com/file/02a5b8cb-90f2-474f-b6a8-76f1c15951e0?embed`](https://app.quadratichq.com/file/02a5b8cb-90f2-474f-b6a8-76f1c15951e0?embed)

**Example:**

```
<iframe src="https://app.quadratichq.com/file/02a5b8cb-90f2-474f-b6a8-76f1c15951e0?embed" title="your sheet description"></iframe>
```


# Scheduled tasks

Schedule tasks to run in your spreadsheet at set intervals.

## Scheduled tasks

Quadratic scheduled tasks run your sheet automatically on a schedule you set. Cloud runners execute your code without needing your browser open — connect a data source, schedule a refresh, and your sheet stays current.

#### Accessing scheduled tasks

Access scheduled tasks from the sidebar. The panel lists every task in the file with its schedule and run history. You can also filter your dashboard's file list to show only files with scheduled tasks.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2F4ezkeYCWNf31EvG6VTSp%2FCleanShot%202026-01-05%20at%2018.13.42%402x.png?alt=media&#x26;token=768e681a-bd21-4809-8612-e0e573769744" alt=""><figcaption></figcaption></figure>

#### Schedule your task

**Task types: Run file, Run sheet, Run selection**

**Run file:** executes everything in the entire spreadsheet.

**Run sheet:** only executes a single selected sheet.

**Run selection:** only executes a selection in a single sheet.

#### Intervals: hourly, daily, custom cron

Set hourly, daily or custom cron timeframes.

* **Daily** — pick the days of the week and a time.
* **Hourly** — runs at a chosen minute past each hour.
* **Custom cron** — full cron expressions for anything else.

Pick your timezone when creating the task — schedules are stored in UTC and displayed in the timezone you choose.

{% hint style="warning" %}
Tasks can run at most once per hour. Custom cron expressions more frequent than hourly are rejected.
{% endhint %}

Cron examples:

| Cron expression | Translation                        |
| --------------- | ---------------------------------- |
| 0 0 \* \* \*    | Every day at 12:00 AM              |
| 0 0 \* \* FRI   | At 12:00 AM, only on Friday        |
| 0 0 1 \* \*     | At 12:00 AM, on day 1 of the month |
| 0 \* \* \* \*   | Every hour                         |

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2Flk5yqoQlgPAiIuy22T6U%2FCleanShot%202026-01-05%20at%2018.18.05%402x.png?alt=media&#x26;token=adb1c781-8eb6-4f19-a62f-01fc559fe2dc" alt=""><figcaption></figcaption></figure>

#### Manage tasks with AI

Ask the AI to manage schedules for you: "run this sheet every weekday at 7am" creates the task. You can also ask it to list, edit, pause, or delete tasks and to summarize recent run history.


# Date-time formatting

Description of date-time formatting in Quadratic.

In Quadratic you can use either preset date-time options or use custom date-time options.&#x20;

## Date-time presets

Date-time presets include the standard formats you'd expect for both date and time.&#x20;

<figure><img src="/files/5xRWUakjjEK45GKkIX6K" alt="" width="353"><figcaption></figcaption></figure>

## Custom formatting&#x20;

A very wide range of custom formatting is supported in Quadratic. The table below reflects all available options for custom formatting.&#x20;

| Spec     | Example                          | Description                                                                                                              |
| -------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| %Y       | 2001                             | Gregorian year padded to 4 digits. Support from -262144 to 262143.                                                       |
| %C       | 20                               | Gregorian year divided by 100, zero-padded to 2 digits.                                                                  |
| %y       | 01                               | Gregorian year modulo 100, zero-padded to 2 digits.                                                                      |
| %m       | 07                               | Month number (01-12), zero-padded to 2 digits.                                                                           |
| %b       | Jul                              | Abbreviated month name. Always 3 letters.                                                                                |
| %B       | July                             | Full month name.                                                                                                         |
| %h       | Jul                              | Same as %b                                                                                                               |
| %d       | 08                               | Day number 01-31, zero padded to 2 digits.                                                                               |
| %e       | 8                                | Same as %d but padded.                                                                                                   |
| %a       | Sun                              | Abbreviated weekday name.                                                                                                |
| %A       | Sunday                           | Full weekday name.                                                                                                       |
| %w       | 0                                | Sunday = 0, Monday = 1, etc.                                                                                             |
| %u       | 7                                | Monday = 1, Tuesday = 2, etc.                                                                                            |
| %U       | 28                               | Week number 00-53, zero padded to 2 digits. Week starting with Sunday.                                                   |
| %W       | 27                               | Same as %U, but week 1 starts with Monday instead of Sunday.                                                             |
| %G       | 2001                             | Same as %Y but ISO 8601 year number.                                                                                     |
| %g       | 01                               | Same %y but year number in ISO 8601                                                                                      |
| %V       | 27                               | Same as %U but uses the week number in ISO 8601 week date (01-53).                                                       |
| %j       | 189                              | Day of the year 001-366, zero padded to 3 digits.                                                                        |
| %D       | 07/08/01                         | Month/day/year format. Same as %m/%d/%y.                                                                                 |
| %x       | 07/08/01                         | Same as %D except locale date representation.                                                                            |
| %F       | 2001-07-08                       | Year-month-day format in ISO 8601. Same as %Y-%m-%d.                                                                     |
| %v       | 8-Jul-2001                       | Day-month-year, same as %e-%b-%Y.                                                                                        |
| %H       | 00                               | Hour number 00-23, zero padded to 2 digits.                                                                              |
| %k       | 0                                | Same as %H but space padded. Same as %\_H.                                                                               |
| %I (aye) | 12                               | Hour number in 12 hour clocks 01-12, zero-padded to 2 digits.                                                            |
| %l (el)  | 12                               | Same as %I (aye), but space-padded, same as %\_I (aye).                                                                  |
| %P       | am                               | am or pm in 12-hour clock                                                                                                |
| %p       | AM                               | AM or PM in 12-hour clocks                                                                                               |
| %M       | 34                               | Minute number 00-59, zero padded to 2 digits.                                                                            |
| %S       | 60                               | Second number 00-60, zero-padded to 2 digits.                                                                            |
| %f       | 26490000                         | Number of nanoseconds since last whole second.                                                                           |
| %.f      | .026490                          | Decimal fraction of a second.                                                                                            |
| %.3f     | .026                             | Decimal fraction of a second with a fixed length of 3.                                                                   |
| %.6f     | .026490                          | Decimal fraction of a second with a fixed length of 6.                                                                   |
| %.9f     | .026490000                       | Decimal fraction of a second with a fixed length of 9.                                                                   |
| %3f      | 026                              | Decimal fraction of a second like `%.3f` but without the leading dot.                                                    |
| %6f      | 026490                           | Decimal fraction of a second like `%.6f` but without the leading dot.                                                    |
| %9f      | 026490000                        | Decimal fraction of a second like `%.9f` but without the leading dot.                                                    |
| %R       | 00:34                            | Hour-minute format. Same as `%H:%M`.                                                                                     |
| %T       | 00:34:60                         | Hour-minute-second format. Same as `%H:%M:%S`.                                                                           |
| %X       | 00:34:60                         | Locale’s time representation (e.g., 23:13:48).                                                                           |
| %r       | 12:34:60 AM                      | Locale’s 12 hour clock time. (e.g., 11:11:04 PM). Falls back to `%X` if the locale does not have a 12 hour clock format. |
| %Z       | ACST                             | Local time zone name. Skips all non-whitespace characters during parsing. Identical to `%:z` when formatting.            |
| %z       | +0930                            | Offset from the local time to UTC (with UTC being `+0000`).                                                              |
| %:z      | +09:30                           | Same as `%z` but with a colon.                                                                                           |
| %::z     | +09:30:00                        | Offset from the local time to UTC with seconds.                                                                          |
| %:::z    | +09                              | Offset from the local time to UTC without minutes.                                                                       |
| %#z      | +09                              | *Parsing only:* Same as `%z` but allows minutes to be missing or present.                                                |
| %c       | Sun Jul 8 00:34:60 2001          | Locale’s date and time (e.g., Thu Mar 3 23:05:25 2005).                                                                  |
| %+       | 2001-07-08T00:34:60.026490+09:30 | ISO 8601 / RFC 3339 date & time format.                                                                                  |
| %s       | 994518299                        | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC.                                                        |
| %t       |                                  | Literal tab (\t).                                                                                                        |
| %n       |                                  | Literal newline (\n).                                                                                                    |
| %%       |                                  | Literal percent sign.                                                                                                    |
| %-?      |                                  | Suppresses any padding including spaces and zeroes. (e.g. `%j` = `012`, `%-j` = `12`)                                    |
| %\_?     |                                  | Uses spaces as a padding. (e.g. %j = 012, %\_j = 12)                                                                     |
| %0?      |                                  | Uses zeroes as a padding. (e.g. %e = 9, %0e = 09)                                                                        |


# Desktop app

## Desktop app

The Quadratic desktop app wraps the full product in a native window with deep-link support, so `quadratic://` links open straight into your files.

### Download

Get the installer for your platform from the Quadratic website. The app updates itself automatically.

### Why use the desktop app

* Native window and dock/taskbar presence, separate from browser tabs
* Deep links into files
* Built-in cross-origin support — API requests from Python and JavaScript that would be blocked by browser CORS rules work in the desktop app

Everything else — files, teams, connections, AI — works exactly like the web app and stays in sync with it.


# Browser compatibility

## Browser compatibility

Quadratic has **first-tier** browser support for:

* Google Chrome\*
* Chromium browsers (Arc, Edge, Opera, etc.)

*\*All of our developers and automated tests use Chrome, giving it first-class support status.*

Non-Chromium browsers receive **second-tier** support and are less performant than first-tier browsers:

* Safari
* Firefox

### Mobile

Quadratic works on modern mobile browsers with a dedicated mobile layout: touch-friendly toolbars, the AI chat, and a mobile code editor. Very large files may be limited by device memory, particularly on iOS.

### Desktop app

Prefer a native app? Quadratic is available as a desktop app for macOS, Windows, and Linux.

To file a browser-specific issue, please [contact us](https://quadratichq.com/contact).

### Troubleshooting

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FH72uIkQ40WMPq29lWIec%2FCleanShot%202024-07-24%20at%2010.05.27%402x.png?alt=media&#x26;token=48028bc7-3517-40ca-a1ca-5c61eb30e00c" alt="" width="563"><figcaption></figcaption></figure>

#### Hardware acceleration

If you receive this error message in Chrome, a possible cause is that your browser's hardware acceleration is turned off, preventing WebGL from working.

**Solution:** You can enable hardware or graphics acceleration by visiting `chrome://settings/system` and toggling on hardware or graphics acceleration.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FpbtpGES20HMbIDYdOhlJ%2FCleanShot%202024-07-24%20at%2010.08.43%402x.png?alt=media&#x26;token=dd234c63-4920-4dc6-b7c0-7327bdb1c681" alt=""><figcaption><p>Toggle must be on for Quadratic to work in your browser</p></figcaption></figure>


# Embedding Quadratic spreadsheets

Embed Quadratic in other apps and websites.

You can embed a Quadratic spreadsheet in your own website or app so visitors can view or interact with it without leaving the page. The spreadsheet runs in an iframe and supports the same features as opening the file in Quadratic (formulas, Python, JavaScript, charts, etc.), with options to restrict editing or show a single sheet.

### Prerequisites

To create an embed link, you need **edit access** to the file. The embed link itself controls access — once created, anyone with the embed link can view the file regardless of the file's sharing settings. This is similar to "share by link" — the embed UUID acts as a secret access token.

To revoke embed access, delete the embed link from the Share dialog. The file's publicLinkAccess setting ("Anyone with the link") does not affect embed access.

### Getting the embed link

1. Open the spreadsheet in Quadratic.
2. Go to **File → Share** (or use the Share option in the top bar).
3. Scroll to the **Embed** section at the bottom and expand it.
4. Optionally adjust the embed options (read-only, sheet, preload—see below).
5. Copy the embed URL from the read-only box, or use **Copy link** to copy the URL and **Copy HTML** to copy a ready-made `<iframe>` snippet.

The base embed URL looks like:

```
https://app.quadratichq.com/embed?embedId=YOUR_EMBED_UUID
```

The `embedId` is a unique identifier for the embed link, separate from the file's UUID. This ensures the file's UUID is never exposed in the embed URL.

### Embed URL parameters

You can add query parameters to control how the embed behaves.

| Parameter          | Description                                                                                                                                                     |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `embedId`          | **Required** for embedding an existing file. The embed link's UUID (from the Share dialog). Omit when using `import`, `uploadMode`, or for a blank spreadsheet. |
| `import`           | URL of a file to import (CSV, Excel, Parquet, or Quadratic grid). The file is fetched and opened in the embed. See Embedding with imported files.               |
| `uploadMode`       | Set to `postMessage` to upload a file via postMessage. See Uploading files via postMessage.                                                                     |
| `readonly`         | If present, the embed loads in read-only mode. Viewers cannot edit cells or run code.                                                                           |
| `sheet`            | Name of a single sheet to display. When set, only that sheet is shown and the sheet bar is hidden. Omit to show all sheets.                                     |
| `preload`          | Comma-separated list of runtimes to preload for faster first run: `python`, `js`, or `python,js`.                                                               |
| `hideHeadings`     | If present, row and column headings (A, B, C… and 1, 2, 3…) are hidden.                                                                                         |
| `hideGridLines`    | If present, grid lines between cells are hidden.                                                                                                                |
| `noFormatting`     | If present, the formatting bar and cell position/goto input are hidden.                                                                                         |
| `noClampToContent` | If present, users can pan the viewport beyond the edges of sheet content. By default, panning is clamped to the content bounds.                                 |
| `noZoom`           | If present, zooming is disabled and zoom controls are hidden.                                                                                                   |

The `embedId`, `import`, and `uploadMode` parameters are mutually exclusive: use `embedId` to embed an existing Quadratic file, `import` with a URL to load a file from the web, `uploadMode` to upload a file via postMessage, or omit all three for a blank spreadsheet.

#### Examples

* View-only embed: `https://app.quadratichq.com/embed?embedId=abc-123&readonly`
* Single sheet, read-only: `https://app.quadratichq.com/embed?embedId=abc-123&readonly&sheet=Summary`
* Preload Python and JavaScript: `https://app.quadratichq.com/embed?embedId=abc-123&preload=python,js`
* Minimal UI (no headings, grid lines, or formatting bar): `https://app.quadratichq.com/embed?embedId=abc-123&hideHeadings&hideGridLines&noFormatting`
* Fixed zoom, clamped viewport: `https://app.quadratichq.com/embed?embedId=abc-123&noZoom`

### Embedding with imported files

You can embed Quadratic and have it load a spreadsheet from a URL instead of an existing Quadratic file. Use the `import` parameter with the full URL of the file. The file is fetched when the embed loads and opened in the viewer. The original file is never modified — if the user signs in to Quadratic, a duplicate is created in their account.

**Supported formats:**

* **CSV** (`.csv`)
* **Excel** (`.xlsx`, `.xls`)
* **Parquet** (`.parquet`)
* **Quadratic grid** (`.grid`)

The format is inferred from the URL path (the file extension). The URL must be publicly accessible so the embed can fetch it (same-origin or CORS permitting).

**Examples:**

* Import a CSV from your site: `https://app.quadratichq.com/embed?import=https://yoursite.com/data/sales.csv`
* Import an Excel file (read-only): `https://app.quadratichq.com/embed?import=https://yoursite.com/reports/q4.xlsx&readonly`

You can combine `import` with other embed parameters (`readonly`, `sheet`, `preload`, etc.) the same way as with `embedId`.

### Uploading files via postMessage

For larger files or files you don't want to host publicly, you can upload file data directly to the embed iframe using the browser's postMessage API. This is useful when the file is generated dynamically or stored locally.

Set `uploadMode=postMessage` in the embed URL:

```
https://app.quadratichq.com/embed?uploadMode=postMessage
```

Then, in your host page, listen for a `quadratic-embed-upload-ready` message from the iframe and respond by posting the file data:

```html
<iframe id="quadratic-embed" src="https://app.quadratichq.com/embed?uploadMode=postMessage" width="100%" height="600" style="border: none;"></iframe>
<script>
const iframe = document.getElementById('quadratic-embed');
const fileName = 'data.csv';
const fileData = new Uint8Array([/* your file bytes */]).buffer;

window.addEventListener('message', (event) => {
  if (event.source === iframe.contentWindow && event.data?.type === 'quadratic-embed-upload-ready') {
    iframe.contentWindow.postMessage({
      type: 'quadratic-embed-upload',
      uploadName: fileName,
      uploadData: fileData
    }, '*', [fileData]);
  }
});
</script>
```

The embed will import the file and display it. Supported formats are the same as for URL imports (CSV, Excel, Parquet, and Quadratic grid).

You can combine `uploadMode` with other embed parameters (`readonly`, `sheet`, `preload`, etc.) the same way as with `embedId`.

### Blank embed

Omit `embedId`, `import`, and `uploadMode` to load a blank spreadsheet:

```
https://app.quadratichq.com/embed
```

This creates an empty spreadsheet that users can interact with. All changes are local-only and lost when the page is refreshed.

### Adding the embed to your site

Use an iframe and set the `src` to your embed URL:

```html
<iframe
  src="https://app.quadratichq.com/embed?embedId=YOUR_EMBED_UUID"
  width="100%"
  height="600"
  style="border: none;">
</iframe>
```

Adjust `width` and `height` to fit your layout. The Share dialog's **Copy HTML** button gives you a snippet you can paste into your page.

### Embed settings page

You can configure embed options interactively using the embed settings page at:

```
https://app.quadratichq.com/embed/settings
```

This page lets you:

* Enter an embed ID, upload a file, or specify a file URL
* Configure size, appearance, and performance options
* Preview the embed in real-time
* Copy the generated iframe HTML code

### Embed options explained

* **Read-only** — When enabled, the embedded spreadsheet is view-only. No edits or code execution.
* **Only show sheet** — If you enter a sheet name, only that sheet is shown and the sheet tabs are hidden. Leave blank to show all sheets.
* **Preload Python** / **Preload JavaScript** — Preloads the corresponding runtime so the first run of Python or JavaScript in the embed is faster. Helpful if you know visitors will run code.
* **Hide headings** — When enabled, row and column headings (A, B, C… and 1, 2, 3…) are hidden for a cleaner look.
* **Hide grid lines** — When enabled, the grid lines between cells are hidden for a cleaner presentation.
* **Hide formatting bar** — When enabled, the formatting bar and cell position/goto input are hidden, even in non-read-only mode.
* **Allow panning beyond content** — By default, the viewport is clamped to the sheet content bounds. Enable this to let users pan beyond the edges.
* **Disable zooming** — When enabled, zooming is locked and zoom controls (menu, pinch, wheel) are hidden.

### Viewing and editing in the embed

Anyone with the embed link can open the spreadsheet. They can change cells and run code in the browser regardless of whether the file is shared as **Can view** or **Can edit**, but **no one can ever modify your original file through an embed**. All changes happen in a temporary, in-browser session.

If a viewer signs in to Quadratic (or opens the file from the embed), a **duplicate** of the file is created in their account. They work on their own independent copy from that point on — your original file is never touched. This means:

* Your data is always safe.
* Viewers get the full Quadratic experience (formulas, Python, JavaScript, charts, etc.).
* Saving requires a Quadratic account and always produces a separate copy.

### Edit in Quadratic button

The embed displays an "Edit in Quadratic" button that allows users to export the current state of the embedded spreadsheet (including any local changes) to their Quadratic account. Clicking this button:

1. Exports the current grid data
2. Uploads it to Quadratic
3. Opens a new tab where the user can sign in and claim the file

The claimed file is a copy — the original embedded file is never modified.


# Manage your team

Work together with your team and organization.

## Manage your team

In Quadratic, you are placed in a **Team** by default, even if it's just you.

You can add and switch which **Team** you're working with from the **Teams** selection menu in the top left of your dashboard.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FE8MerHp59HXIyHAXFLKt%2FCleanShot%202024-07-17%20at%2016.20.56%402x.png?alt=media&#x26;token=2bf363e0-6fa9-4b36-bd01-e6707bf513ed" alt="" width="563"><figcaption></figcaption></figure>

### Add team members

You can view your team members from the Members tab on your team's dashboard. You can also add, remove, and edit their permissions from here.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FW5xUSujCjtRXVINrHQPG%2FCleanShot%202024-07-17%20at%2016.43.36%402x.png?alt=media&#x26;token=895b2596-753b-4de3-8847-ecbb5adb6b3e" alt=""><figcaption></figcaption></figure>

### Team permissions

By default, members of your team(s) will have access to all of your sheets within the team.

You set each member's team permissions. These permissions are global to your team and apply to all spreadsheets within that team.

* **Owner:** creator of the team, can add and remove members, edit all files, create and remove connections.
* **Editor:** can edit all files and access all connections but cannot add or remove members and cannot edit or remove connections.
* **Viewer:** can view all files but not edit files, cannot add and remove members, and cannot add, edit, or remove connections.

To edit permissions, open the Members tab and make your changes to each user's permissions.

### Manage your connections

Manage your connections from the Connections tab. Connections are a direct connection to databases, data warehouses, and SaaS tools.

Learn more about SQL connections and Agent connections.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2Fm6IMbIdupYYryhuYIU5W%2FCleanShot%202024-07-17%20at%2016.55.07%402x.png?alt=media&#x26;token=44c514ff-8ac1-4887-9a13-a10b7a52f6e6" alt="" width="563"><figcaption></figcaption></figure>

### Team secrets

Store API keys and credentials as named secrets from your team settings. Secrets are referenced as `{{SECRET_NAME}}` from agent connections and code cells, and their values are never exposed after creation.

### API tokens

Create tokens for the Developer API from your team settings. Tokens are team-scoped — treat them like passwords.

### Team AI memory

Shared AI context for your whole team lives in your team settings. Add business definitions and conventions once, and every teammate's AI chats use them. See AI memory & context.

### Plans & billing

Your team's plan, AI usage, and invoices are managed from the Billing settings. See Plans & billing.

### Rename your team

Navigate to the **Settings** tab and change your team name.

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FFeIEe9dHM1SZhKhedVLT%2FCleanShot%202024-07-17%20at%2016.46.42%402x.png?alt=media&#x26;token=fb55d659-8d94-43cf-9358-c34ac9c6c9d4" alt=""><figcaption></figcaption></figure>


# Plans & billing

## Plans & billing

Manage your plan, AI usage, and invoices from your team settings under **Billing**.

### Plans

|                                 | Personal (Free)        | Pro                           | Business                      |
| ------------------------------- | ---------------------- | ----------------------------- | ----------------------------- |
| Price                           | $0                     | $20/user/month                | $40/user/month                |
| AI usage                        | Monthly message limit  | $20 AI credits per user/month | $40 AI credits per user/month |
| AI models                       | Default model          | All models                    | All models                    |
| Files                           | Limited editable files | Unlimited                     | Unlimited                     |
| Connections                     | Limited                | Unlimited                     | Unlimited                     |
| File export (Excel, CSV, .grid) | —                      | Included                      | Included                      |
| AI on-demand overage            | —                      | —                             | Available                     |

### AI usage & billing

Only the AI work you initiate counts toward your usage: chat messages, code assistant conversations, PDF imports, and web searches. Background AI — like autocomplete suggestions, chat naming, and agent connection setup — is always free.

On paid plans, each user gets a monthly AI credit allowance. Business teams can enable on-demand usage to keep going past the allowance at cost.

### Upgrades & downgrades

Upgrades take effect immediately with prorated billing — you're invoiced right away for the remainder of the period. Downgrades are scheduled for the end of the current billing period.


# Private files

Details about private files in Quadratic Teams

Sometimes you want the files you're working on to be private before sharing with others. This is what the **Private** section of your team is for.

Files in the **Private** section are only viewable to you and specific other members you invite to the file. Files shared with you by others can be found in the **Shared with me** section.

<figure><img src="/files/HvI8uO3BdNrZweVPqUQI" alt=""><figcaption></figcaption></figure>

Once ready, you can move files from the **Private** section to the **Team** section for everyone to view and edit according to their permissions.&#x20;

<figure><img src="/files/OG16tvzrGBV94znY0p6p" alt="" width="563"><figcaption></figcaption></figure>


# Collaboration

Work together with your teammates.

## Editing collaboratively&#x20;

Members of your team can work together in real-time.&#x20;

<figure><img src="/files/Flsa82Wy5pVCGhsknBo8" alt=""><figcaption></figcaption></figure>

## User following

You can follow users by clicking their portrait in the top right of a sheet. This will then put you in follow mode which you can exit by pressing `escape`.


# Embedded sheets

Embed your sheets for others to view and edit across the web.

Quadratic is embeddable in websites and other apps. Files that are made public can be shared anywhere that supports embedding. Files with permissions can still be embedded but only users with permissions to view the sheet will be able to do so.&#x20;

## Embed in apps&#x20;

Some apps will allow you to embed Quadratic directly. Notion is one such example of application you can embed directly if you have public viewing enabled.&#x20;

<figure><img src="/files/7jcKINfLYi5e0AwhonBn" alt="" width="563"><figcaption></figcaption></figure>

## Embed in a web page via iFrame

If you have public sharing permissions turned ON in your spreadsheet, you can freely embed your spreadsheet in any site that allows embedding. Simply use the URL to your spreadsheet in an `<iframe>` HTML tag.

**Example:**\
`<iframe src="Quadratic_URL" title="your sheet description"></iframe>`


# SQL connections

[Visit the SQL section](/connections/sql-getting-started) of the docs to get started with SQL in Quadratic.&#x20;


# API requests

For any data sources you can't connect directly to with SQL, we recommend using API requests via Python or JavaScript. Alternatively, you can import Excel files, drag and drop CSVs, or [let us know if you need help](https://quadratichq.com/contact) with the specific data source you're trying to connect.&#x20;

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f40d">🐍</span> Make an API request from Python -></td><td></td><td></td><td><a href="/pages/gonxIBjOhzIg4gQLfvxq">/pages/gonxIBjOhzIg4gQLfvxq</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f7e8">🟨</span> Make an API request from JavaScript -></td><td></td><td></td><td><a href="/pages/v9OuRPCASJZxnZu2fsMr">/pages/v9OuRPCASJZxnZu2fsMr</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f7e9">🟩</span> Import Excel files -></td><td></td><td></td><td><a href="/pages/1RtflwVfZutTB9pRxEWZ">/pages/1RtflwVfZutTB9pRxEWZ</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f4c1">📁</span> Drag and drop .CSV files -></td><td></td><td></td><td><a href="/pages/VEoSfKAoFlsspnVMA5Mg">/pages/VEoSfKAoFlsspnVMA5Mg</a></td></tr><tr><td><span data-gb-custom-inline data-tag="emoji" data-code="1f5c4">🗄️</span> Drag and drop .Parquet files -></td><td></td><td></td><td><a href="/pages/qyg2fTsqHd04LwPB4IXF">/pages/qyg2fTsqHd04LwPB4IXF</a></td></tr></tbody></table>


# Drag and drop .CSV

Easily drag and drop your .CSV files into the spreadsheet.

Simply drag the file from your system directly into the spreadsheet. Supports up to millions of rows in a few seconds, tops.

{% embed url="<https://www.quadratichq.com/images/section-drag-n-drop.gif>" %}


# Drag and drop .Parquet

Easily drag and drop your .Parquet files into the spreadsheet.

## Drag and drop .Parquet

Simply drag the .Parquet file from your system directly into the spreadsheet. Supports up to a few million rows performantly.

{% embed url="<https://www.quadratichq.com/images/section-drag-n-drop.gif>" %}

#### Other file formats

CSV and Excel files can also be dragged straight into the spreadsheet — see Drag and drop .CSV and Import Excel files. To import Google Sheets, see Import Google Sheets.


# Import Excel files

Go from Excel files to Quadratic.

## Import Excel files

Import Excel files (.xlsx, .xls) in any of these ways:

* Drag and drop the file directly onto the spreadsheet
* Import from the dashboard's `Import file` menu
* Attach the file to the AI chat

<figure><img src="https://2438361843-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Ff1Y5UzPF2x1oIzVJbUK8%2Fuploads%2FAU7Id1Q7LBfywWCFp1Kp%2FCleanShot%202024-04-17%20at%2016.07.44%402x.png?alt=media&#x26;token=75c56d66-3a8d-45a9-a1d0-28322a9e9f99" alt="" width="563"><figcaption></figcaption></figure>

Formatting, formulas converted where supported, and frozen panes are preserved on import.

You can also export any Quadratic file back to Excel from **File > Download** (available on paid plans).


# Import Google Sheets

## Import Google Sheets

Import a Google Sheet directly — no CSV export step needed.

### How to import

1. Use the import option on your dashboard or in the file menu and choose **Google Sheets**.
2. Sign in with Google and pick the spreadsheet using the Google file picker.
3. The sheet imports with values, formatting, and frozen panes preserved.

You can also ask the AI to import a Google Sheet from chat.

### Permissions

Quadratic uses Google's per-file authorization scope: it can only read the files you explicitly choose in the picker, never your whole Drive.


# Getting started

Self-host Quadratic enterprise use-cases

Quadratic is self-hostable software. Many enterprise customers and privacy-oriented individual users have voiced a desire for self-hosted deployments to their hardware/corporate cloud environments for added data security and controls.&#x20;

Self-hosting support is offered for business and enterprise.&#x20;

You can get started with self-hosting [here](https://selfhost.quadratichq.com/).&#x20;

## Providers

* [AWS](/self-hosting/aws)
* [Docker](/self-hosting/docker) (best for personal users and small teams that don't use a cloud provider)

### Not officially supported

This list is not officially supported (yet). You can [contact us](https://quadratichq.com/contact), and we'll walk you through how to make these hosted solutions work. Please note that we are in the process of writing documentation and testing all the edge cases for these deployments so there may still be some issues.

* [Azure](/self-hosting/azure)
* [Google Cloud Platform (GCP)](/self-hosting/google-cloud-platform)
* [Any other mainstream cloud ](/self-hosting/other-hosting)&#x20;

Enterprises can [Contact us](https://quadratichq.com/contact) for help deploying with any of these other options.&#x20;

## Security&#x20;

By self-hosting, you are guaranteeing your data stays within your cloud under your company's data controls.&#x20;

Note that the Quadratic Cloud is also [SOC II and HIPAA compliant](https://trust.quadratichq.com/).&#x20;

The code deployed to your self-hosted environment is the same as the code [in our source-available GitHub repo](https://github.com/quadratichq/quadratic).&#x20;

## License key

You'll first need to gather a license key from <https://selfhost.quadratichq.com>. Quadratic self-hosting is free for individuals and priced per user for business and enterprise users.&#x20;

If you are a business or enterprise user of self-hosting, please [contact us](https://quadratichq.com/contact) to get set up with a business or enterprise license key. Individual users can generate a free license key from <https://selfhost.quadratichq.com>.


# Docker

Deploy Quadratic with Docker to almost any hardware.

Docker deployments with Quadratic are a few easy steps away. Instructions vary depending on operating system.&#x20;

## MacOS and Linux

**Basic requirements:**&#x20;

* License Key (available at <https://selfhost.quadratichq.com/>)
* The following open ports: 80, 443, 3001, 3002, 3003, 4433, 4455, and 8000
* [Docker](https://www.docker.com/get-started/)

```bash
curl -sSf https://raw.githubusercontent.com/quadratichq/quadratic-selfhost/main/init-local.sh -o init.sh && bash -i init.sh
```

This will start the process of downloading and running containers, also prompting for the license key that you can acquire for free at <https://selfhost.quadratichq.com/>. Quadratic self-hosting is free for up to three users on a single deployment.&#x20;

Need help? Enterprise users can  [Contact us](https://quadratichq.com/contact).

## Windows

Running on Windows requires using WSL 2 with Docker integration enabled.&#x20;

1. WSL 2 set up, here are installation instructions: <https://learn.microsoft.com/en-us/windows/wsl/install>
2. Add Docker to WSL: <https://docs.docker.com/desktop/features/wsl/>
3. Retrieve license key: <https://selfhost.quadratichq.com/>
4. Run command to download and run containers, this step will also prompt for license&#x20;

```bash
curl -sSf https://raw.githubusercontent.com/quadratichq/quadratic-selfhost/main/init-local.sh -o init.sh && bash -i init.sh
```

Need help? Enterprise users can  [Contact us](https://quadratichq.com/contact).

## Common errors

**Toomanyrequests**: this error will happen if you have not logged in to Docker when trying to download and run containers. Use `Docker login` in your terminal to fix.&#x20;

**Error getting credentials:** <https://forums.docker.com/t/error-failed-to-solve-error-getting-credentials-err-exit-status-1-out/136124>

**Docker + WSL virtualization errors:** ensure virtualization is enabled in your BIOS: <https://support.microsoft.com/en-us/windows/enable-virtualization-on-windows-c5578302-6e43-4b4b-a449-8ced115f58e1>

## Contact us

Enterprise users can [contact us](https://quadratichq.com/contact) if you need any more help setting up a deployment of Quadratic using Docker.&#x20;


# AWS

Deploy Quadratic to Amazon Web Services (AWS).

AWS build instructions are for anyone wanting to deploy Quadratic to [Amazon Web Services (AWS)](https://aws.amazon.com/).&#x20;

## Getting started

You'll first need to create a license key from <https://selfhost.quadratichq.com>. Quadratic self-hosting is free for individuals and priced per user for business and enterprise users.&#x20;

## CloudFormation

Using Cloud Formation templates is the easiest way to get started with self-hosting Quadratic on AWS.

### Setup

* You'll need a domain where you can configure a subdomain using DNS.
  * Example: `quadratic.yourbusiness.com`&#x20;
* Download the Cloud Formation template that best fits your use-case.
  * [Cloud Formation Templates](https://github.com/quadratichq/quadratic-selfhost/tree/main/deployment/aws/cloudformation)
  * If you don't know which you'd like to use, start with quadratic-selfhost-lite.yml.

### Deployment

1. Go into the AWS Console in the Region where you'd like to deploy Quadratic.
2. Select Cloud Formation and click on the "Create Stack" button.
3. Download a[ Quadratic Cloud Formation Template](https://github.com/quadratichq/quadratic-selfhost/tree/main/deployment/aws/cloudformation).&#x20;
4. Select "Use an existing template" and "Upload a template file", then upload the downloaded template and click on the "Next" button.
5. Name your stack anything you want.
6. Add your [license key](https://selfhost.quadratichq.com/) and the domain or subdomain you plan to host quadratic on for parameters.
   * Example `quadratic.yourbusiness.com`&#x20;
   * We will take care of provisioning SSL certificates on your behalf.
7. &#x20;Click on the "Next" button to continue.
8. On the "Configure stack options" page, no changes are necessary, confirm. Click on the "Next" button to continue.
9. Review options and click on the "Submit" button to launch the stack.
10. Wait for the stack to start.
11. Click on the "Outputs" tab and copy the instance IP address.  Create two DNS A records for your (sub)domain:
    * A  `quadratic.yourbusiness.com` - 192.168.1.1
    * A `*.quadratic.yourbusiness.com` - 192.168.1.1
12. Wait for the DNS to propagate and your stack to start.
13. You're done. Open your subdomain in a web browser (Chome recommended).

## Manual EC2

### Setup

Quadratic is best deployed via an EC2 instance at a minimum of 4 cores and 12 GB of RAM.

### Startup command

Quadratic can be installed via a single command:&#x20;

```
curl -sSf https://raw.githubusercontent.com/quadratichq/quadratic-selfhost/main/init-aws.sh -o init.sh && bash -i init.sh
```

This will download the initialization script, which will prompt for a license key in order to register Quadratic. Enter the license key generated from <https://selfhost.quadratichq.com>.

Additionally, the docker compose network will start. Please allow several minutes for the docker images to download.


# Azure

Deploy Quadratic to your company's Azure cloud.

We do not yet have documentation for Azure deployments, but you can [contact us](https://quadratichq.com/contact) for help setting up a deployment of Quadratic.&#x20;


# Google Cloud Platform

Deploy Quadratic with to your company's Google Cloud Platform (GCP) environment.

We do not yet have documentation for GCP deployments, but you can [contact us](https://quadratichq.com/contact) for help setting up a deployment of Quadratic.&#x20;


# Bring your own AI

How to add your AI keys to self-hosted Quadratic

## Enabling AI

If you want to enable AI in self-hosting you'll need to add your own AI provider API keys to the .env file located in the root directory.&#x20;

You can find the .env by navigating to the quadratic-selfhost folder and opening the .env file within the root directory. In the AI section you'll find a place for OpenAI and Anthropic keys. The easiest way to find and edit this file is through your terminal. Example: on Mac use `nano .env`  from the root directory, then scroll down until you find the AI environment variables; enter your AI key here.&#x20;

Once you've edited your .env you can stop and start the Docker containers from the quadratic-selfhost folder using the commands `./stop.sh` and `./start.sh`


# Other hosting

Explore other deployment surfaces for Quadratic.

Don't you see your deployment option on our list? Please [contact us](https://quadratichq.com/contact) and let us know what you'd like supported. It's likely, we can already support it, so we'd be happy to work with you on getting Quadratic set up in your environment.


# Overview

Quadratic for Education

The Quadratic Education plan is **100% free** for students, teachers, and researchers. The Education plan provides access to unlimited sheets, code, teammates, and connections in Quadratic.

The only limitation on the Education plan is AI. Education users are subject to the same prompt limitations as the free tier. You can subscribe to a pro plan for more prompts each month. &#x20;

{% hint style="success" %}
[Enroll here](https://app.quadratichq.com/education/enroll) - Quadratic is free for anyone who signs up with a valid education email address.
{% endhint %}

{% hint style="info" %}
If your email is not automatically approved, please [contact us.](https://www.quadratichq.com/contact)&#x20;
{% endhint %}

Quadratic is a modern spreadsheet powered by Python, SQL, and AI. Built to be the best tool for individuals and teams to do data science. Naturally, many educational institutions have reached out to get onboarded to the Quadratic Education Plan, as they want their students to access the best tools for learning analytics.&#x20;

## :tools: No setup required&#x20;

Remove the barriers of setting up environments, installing packages, git versioning, and more. Get straight to your analysis without wasting time on setup and configuration headaches.&#x20;

## :earth\_americas: Accessible via web&#x20;

Quadratic is built to run on all ranges of hardware, which gives every student a workspace that is performant regardless of their computer.&#x20;

## :palms\_up\_together: Built for collaboration&#x20;

Quadratic is built to be used collaboratively in real-time by dozens of users simultaneously. Engage, build, and learn together in an environment optimized for collaboration.&#x20;

{% hint style="success" %}
Get started by signing up using your school email address here - <https://app.quadratichq.com/education/enroll>
{% endhint %}


# Enrolling in the education plan

Get onboarded to the Quadratic education program.

## Enrolling

To enroll, visit the following URL <https://app.quadratichq.com/education/enroll>

When you log into Quadratic, you'll see the following pop-up, which shows your enrollment status.&#x20;

<figure><img src="/files/LKh4XIeop7HsDjHWoNkA" alt=""><figcaption></figcaption></figure>

If you are not successfully enrolled when you visit the education URL when signed in with your educational institution's email account, please [contact us](https://www.quadratichq.com/contact).&#x20;


# Teachers

Access Quadratic for the classroom

## :1234: Learning data science and analytics

Teachers and professors have primarily leveraged Quadratic as a way to teach their students complex concepts more intuitively.&#x20;

Quadratic combines a code editor with a spreadsheet, allowing students to visualize the results of their code in real-time, without worrying about setting up complex environments, installing libraries, versioning, or other complex issues. Students can learn how to do modern analytics in an environment that is optimized for doing so.&#x20;

{% hint style="info" %}
Teachers are leveraging Quadratic to better engage their students across all disciplines.&#x20;
{% endhint %}

Aside from learning how to code and doing modern analytics, students also get exposure to spreadsheets in a modern and fun way. Students groan at using more traditional spreadsheets; Quadratic is a spreadsheet that people love to use, one that just works, is performant, and is built like a modern application should be built.&#x20;

## :snake: Python

Quadratic has been built to be the best spreadsheet for developers, which means a focus on building a great developer experience. Quadratic's editor is built on Monaco (the underpinning of VS Code).&#x20;

Students don't need to setup new environments or manage complex Git operations. Packages are automatically installed and importable.&#x20;

Quadratic is one of the best interfaces for teaching students how to code.&#x20;

[Learn more about how Python works in Quadratic.](#python)[ ](/python/getting-started)

## :chart\_with\_upwards\_trend: SQL

Teach your students to query databases with our SQL connections and SQL language editor.&#x20;

Coming soon.&#x20;

## :robot: AI-assistant&#x20;

Use the AI assistant to help your students get exposure to the modern AI tools they'll see in their future careers.&#x20;

## :penguin: Group work&#x20;

Students can spin up collaborative spreadsheets in a few clicks and immediately collaborate with code and formulas. Easily collaborate without worrying about git, versioning, and other concerns. Popular packages are automatically installed and ready to import - don't worry about setting up environments for your student groups.&#x20;

[Learn more about creating teams.](/teams/manage-your-team)&#x20;

{% hint style="success" %}
Get started by signing up with a valid academic email address [here.](https://app.quadratichq.com/education/enroll)&#x20;
{% endhint %}


# Students

Use-cases for students

Quadratic is the perfect free solution for any analytics and spreadsheet use cases involving working with classmates. Use the spreadsheet optimized for developers to build modern projects and do classwork faster.&#x20;

## 🧑‍🏫 Classwork

Get your work done faster with the spreadsheet powered by Python, SQL, and AI.&#x20;

Write code individually, with the help of classmates, or with the AI-powered code assistant.&#x20;

Get your environment set up without any setup required. Instantly collaborate with classmates in a few clicks; see each other's changes in real-time and learn data science with a modern data tool leveraged by full-time data scientists, engineers, traders, business analysts, and more.&#x20;

## ⌨️ Hackathons

Quadratic is an easy way to start coding without having to install or configure environments, versions, or packages. Just jump in and start building. &#x20;

{% hint style="success" %}
Get started by signing up with a valid academic email address [here.](https://app.quadratichq.com/education/enroll)
{% endhint %}


# Researchers

Use Quadratic in the lab

## :microscope: Use Quadratic in academic labs&#x20;

Quadratic is the perfect solution for researchers needing the best analytics tool for lab-focused work.&#x20;

{% hint style="info" %}
Quadratic is 100% free for academic researchers - leverage the most powerful tool for modern analytics on any research budget.
{% endhint %}

Work collaboratively with your research team. Perform your lab analysis in a spreadsheet format you're used to, supercharged by the programming languages you're used to.&#x20;

{% hint style="success" %}
Get started by signing up with a valid academic email address [here](https://app.quadratichq.com/education/enroll).
{% endhint %}


# Education FAQ

Learn more in the Education FAQ.

### Is the education plan free?&#x20;

Yes, the Quadratic Education Plan is 100% free for teachers, students, and researchers with eligible emails.&#x20;

To enroll: <https://app.quadratichq.com/education/enroll>

You can [contact us](https://www.quadratichq.com/contact) to get added if your school email address isn't automatically recognized.&#x20;

### What do I get for joining the education plan?&#x20;

You receive the following:&#x20;

* Unlimited sheets&#x20;
* Unlimited code
* Unlimited connections&#x20;
* Unlimited teammates

At this time the Education plan does not offer extra AI prompts over the free tier.&#x20;

### Can I use a personal email for the education plan?&#x20;

Unfortunately, you must use the email attached to your educational institution.&#x20;

### What do I get for joining the Education Plan?&#x20;

Eligible sign-ups can create unlimited teams with unlimited users and have access to premium features as they are rolled out (typically $20/mo/user for paid non-education plans).&#x20;

### Can I migrate files from my personal account to my education account?&#x20;

You can [download your files](https://docs.quadratichq.com/spreadsheet/files) and upload them across accounts.&#x20;

### How do I know I'm on an education plan and won't get charged?&#x20;

We never ask education plan users for billing information. You will also find a badge on your dashboard signifying you're in the Education Plan.&#x20;

### Can I use AI in the Education Plan?&#x20;

Yes, Education accounts have the same usage limitations as the free plan.&#x20;

{% hint style="success" %}
If you have any other questions, feel free to [contact us](https://www.quadratichq.com/contact)!
{% endhint %}


# Brand assets

Assets for use in promotional materials

The Quadratic logo is available as an SVG which you can tweak (within reason) to fit the needs of any promotional material. [Contact us](https://www.quadratichq.com/contact) if you need additional assets.&#x20;

<figure><img src="/files/TNFpT7U0yIkuqbymFnBW" alt=""><figcaption></figcaption></figure>


