# Welcome

## CSV Getter Documentation

We make it easy to export data from your favourite no-code platforms. This documentation site will provide you all the knowledge you need to create export processes with CSV Getter. Whether you are backing up data, sharing it, or piping data from your favourite no-code platforms to analysis tools, this documentation should have you covered.

## New User?

Get started by creating an account [here](https://www.csvgetter.com/auth/signin).&#x20;


# What is CSV Getter?

## What is CSV Getter?

CSV Getter is a data export middleware that turns your Airtable bases, Notion databases, Google Sheets, and uploaded CSV files into live, shareable URLs. Every time someone (or something) hits your URL, they get the latest data — in CSV, JSON, XML, HTML, or Excel-compatible format.

***

### The Problem CSV Getter Solves

Platforms like Airtable and Notion are great for managing data, but getting that data *out* — into spreadsheets, dashboards, scripts, or other tools — typically requires manual exports, Zapier workflows, or custom API integrations.

CSV Getter eliminates that friction. You connect your data source once, and get a permanent URL that always returns current data.

***

### How It Works

```
┌──────────────┐       ┌──────────────┐       ┌──────────────┐
│  Data Source  │──────▶│  CSV Getter  │──────▶│  Your App /  │
│  (Airtable,  │       │   Backend    │       │  Spreadsheet │
│   Notion,    │       │              │       │  / Script    │
│   CSV, etc.) │       │  Fetches &   │       │              │
└──────────────┘       │  Transforms  │       └──────────────┘
                       └──────────────┘
```

1. **Connect** — Link your Airtable, Notion, or Google Sheets account (or upload a CSV).
2. **Configure** — Select a table/database, choose fields, pick an output format, and name your endpoint.
3. **Use** — Access your endpoint URL from anywhere. CSV Getter fetches the latest data from your source, transforms it to your chosen format, and returns it.

Data is fetched **in real time** — there's no caching or syncing delay. Every request hits the source.

***

### Supported Data Sources

| Source            | Connection Method              |
| ----------------- | ------------------------------ |
| **Airtable**      | OAuth or Personal Access Token |
| **Notion**        | Notion integration (internal)  |
| **Google Sheets** | Google OAuth                   |
| **CSV Upload**    | Direct file upload             |

***

### Supported Output Formats

CSV Getter supports 10+ output formats. You can set a default when creating the endpoint, or override it per-request using the `?type=` URL parameter:

| Format          | `type` value      | Description                                       |
| --------------- | ----------------- | ------------------------------------------------- |
| CSV             | *(default)*       | Standard comma-separated values                   |
| JSON (Records)  | `json_records`    | Array of objects                                  |
| JSON (Split)    | `json_split`      | Columns and data arrays                           |
| JSON (Index)    | `json_index`      | Keyed by row index                                |
| JSON (Columns)  | `json_columns`    | Keyed by column name                              |
| JSON (Values)   | `json_values`     | Array of arrays (no headers)                      |
| JSON (Table)    | `json_table`      | Schema + data                                     |
| XML             | `xml`             | Standard XML document                             |
| HTML Table      | `html_table`      | Static HTML table                                 |
| Dynamic Table   | `dynamic_table`   | Interactive HTML table with search and pagination |
| Excel Web Query | `excel_web_query` | Optimized for Excel's "From Web" feature          |

See The Type Parameter for details and sample output for each format.

***

### Key Concepts

#### Endpoints

An endpoint is a configured connection between a data source and a URL. Each endpoint has:

* A unique URL (e.g., `https://api.csvgetter.com/abc123def456`)
* A data source (Airtable table, Notion database, etc.)
* Selected fields/columns
* A default output format
* Optional authentication (Bearer token)

#### Credits

Each time your endpoint URL is accessed (a "hit"), it uses one credit. Preview/sample requests from the dashboard are free. Credits reset monthly with your subscription. See Credits for details.

#### URL Parameters

Every endpoint URL supports query parameters that modify the output dynamically. You can change the format, filter data with SQL, add timestamps, trigger email notifications, and more — all without changing the endpoint configuration. See URL Parameters.

#### Scheduled Jobs

Paid users can set up scheduled jobs that automatically hit their endpoint URLs on a daily, weekly, or monthly cadence. Jobs can export to Google Drive, update Google Sheets, or trigger other integrations. Notifications are sent on success or failure.

#### Authentication

Endpoints can optionally require a Bearer token. When enabled, requests must include an `Authorization: Bearer <token>` header. This lets you share your endpoint URL without giving unrestricted access.

***

### Who Uses CSV Getter?

* **Business analysts** who need Airtable data in Excel or Google Sheets
* **Developers** who want a quick JSON API from Airtable or Notion data
* **Operations teams** automating data exports with Zapier, Google Apps Script, or cron jobs
* **Agencies** sharing live client data via URLs
* **Anyone** who needs to get data out of Airtable or Notion without building custom integrations


# The Export URL

Every endpoint you create in CSV Getter produces a unique URL. This page explains the URL structure, how to use parameters, authentication, and shows complete real-world examples.

### URL Anatomy

```
https://api.csvgetter.com/<endpoint_id>?type=json_records&sql=SELECT * FROM csvgetter WHERE status='Active'
└──────────┬──────────────┘└─────┬─────┘└────────────────────────────┬────────────────────────────────────┘
       Base URL              Endpoint ID                      URL Parameters
```

#### Base URL

All endpoint URLs use the same base:

```
https://api.csvgetter.com
```

#### Endpoint ID

A unique alphanumeric identifier generated when you create the endpoint. Examples:

```
https://api.csvgetter.com/abc123def456
https://api.csvgetter.com/0b6VSN8fXQ8U
```

There is also an alternate path format that works identically:

```
https://api.csvgetter.com/files/abc123def456
```

#### URL Parameters

Query parameters are appended after `?` and separated by `&`. They modify the output without changing the endpoint configuration.

***

### How Parameters Chain

You can combine any number of parameters by joining them with `&`:

```
https://api.csvgetter.com/abc123?type=json_records&sql=SELECT name, email FROM csvgetter&filename_timestamp=true
```

**Order doesn't matter.** These are equivalent:

```
?type=json_records&sql=SELECT * FROM csvgetter
?sql=SELECT * FROM csvgetter&type=json_records
```

#### URL Encoding

When your parameters contain special characters (spaces, quotes, etc.), they must be URL-encoded. Most HTTP clients and browsers do this automatically, but if you're building URLs manually:

| Character | Encoded      |
| --------- | ------------ |
| Space     | `%20` or `+` |
| `=`       | `%3D`        |
| `&`       | `%26`        |
| `'`       | `%27`        |
| `*`       | `%2A`        |
| `>`       | `%3E`        |
| `<`       | `%3C`        |

**Example — SQL query with spaces and comparisons:**

Raw SQL:

```sql
SELECT * FROM csvgetter WHERE age > 25 ORDER BY name
```

URL-encoded:

```
?sql=SELECT%20*%20FROM%20csvgetter%20WHERE%20age%20%3E%2025%20ORDER%20BY%20name
```

> **Tip:** Use the URL Wizard in the CSV Getter dashboard to build URLs with parameters automatically.
>
> **Tip:** You can also use our [URL encoder](https://www.csvgetter.com/url-encoder-decoder)

***

### Authentication

Endpoints can optionally require a Bearer token. When auth is enabled:

**Request with auth:**

```bash
curl -H "Authorization: Bearer your-secret-token" \
  https://api.csvgetter.com/abc123def456
```

**Without the header (or wrong token), you'll get:**

```json
{
  "error": "Authorization header is required",
  "help": "https://docs.csvgetter.com"
}
```

The Bearer token is set when configuring your endpoint in the dashboard. You can change it at any time.

#### Auth Error Responses

| Scenario                | Status Code | Error Message                         |
| ----------------------- | ----------- | ------------------------------------- |
| No Authorization header | 401         | `Authorization header is required`    |
| Missing `Bearer` prefix | 401         | `Invalid Authorization header format` |
| Wrong token             | 401         | `Invalid Authorization header`        |

***

### Complete Real-World Example

#### Scenario

You have an Airtable base tracking job applicants. You want to:

1. Get only applicants with status "Interview"
2. Return only their name, email, and application date
3. Output as JSON
4. Get an email notification when the export runs

#### The URL

```
https://api.csvgetter.com/abc123def456?type=json_records&sql=SELECT name, email, application_date FROM csvgetter WHERE status='Interview' ORDER BY application_date DESC&email_me=true
```

#### Broken down:

| Part       | Value                                                                                                         | Purpose                                              |
| ---------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| Base URL   | `https://api.csvgetter.com/abc123def456`                                                                      | Your endpoint                                        |
| `type`     | `json_records`                                                                                                | Output as JSON array of objects                      |
| `sql`      | `SELECT name, email, application_date FROM csvgetter WHERE status='Interview' ORDER BY application_date DESC` | Filter to interviews, select 3 columns, sort by date |
| `email_me` | `true`                                                                                                        | Send you an email notification                       |

#### Response (200 OK)

```json
[
  {
    "name": "Jane Smith",
    "email": "jane@example.com",
    "application_date": "2025-03-15"
  },
  {
    "name": "John Doe",
    "email": "john@example.com",
    "application_date": "2025-03-10"
  }
]
```

***

### Previewing vs. Live Hits

When you use the **URL Wizard** or preview an endpoint in the dashboard, CSV Getter returns a **sample** (up to 10 rows) and does **not** deduct a credit.

When you hit the URL directly (browser, code, cURL, Excel, etc.), it returns the **full dataset** and deducts 1 credit.

***

### Error Responses

All error responses are JSON objects with an `error` field and an HTTP status code:

```json
{
  "error": "Not enough credits. Login at csvgetter.com to purchase.",
  "help": "https://docs.csvgetter.com"
}
```

See Troubleshooting for a full list of error codes and solutions.

***

### Next Steps

* **URL Parameters** — Full reference for all parameters.
* **The SQL Parameter** — Master data filtering and transformation.
* **The Type Parameter** — All output formats explained.
* **Combining URL Parameters** — Real workflow recipes.


# API Reference

CSV Getter provides a RESTful API for accessing your export endpoints and programmatically creating new ones.

### Authentication

CSV Getter uses two authentication mechanisms depending on the context:

#### 1. Endpoint Auth (Bearer Token)

For endpoints with authentication enabled, include the token you set in the dashboard:

```
Authorization: Bearer your-endpoint-token
```

This protects individual endpoint URLs from unauthorized access.

#### 2. API Key

For programmatic endpoint creation (via `POST /urls/create`), use your API key. Find it in your dashboard under account settings.

```
Authorization: Bearer your-api-key
```

***

### Endpoints

#### `GET /` — Health Check

Returns server status.

**Request:**

```bash
curl https://api.csvgetter.com/
```

**Response (200):**

```json
{
  "status": "up",
  "message": "Server is running",
  "version": "11.0.0"
}
```

***

#### `GET /<endpoint_id>` — Fetch Export Data

The primary endpoint. Returns your data in the configured (or overridden) format.

**Request:**

```bash
curl https://api.csvgetter.com/abc123def456
```

**With URL parameters:**

```bash
curl "https://api.csvgetter.com/abc123def456?type=json_records&sql=SELECT%20*%20FROM%20csvgetter%20LIMIT%2010"
```

**With Bearer auth (if enabled on the endpoint):**

```bash
curl -H "Authorization: Bearer my-secret-token" \
  https://api.csvgetter.com/abc123def456
```

**Response varies by format.** Default is CSV:

```
name,email,status
Jane Smith,jane@example.com,Active
John Doe,john@example.com,Pending
```

JSON example (`?type=json_records`):

```json
[
  {"name": "Jane Smith", "email": "jane@example.com", "status": "Active"},
  {"name": "John Doe", "email": "john@example.com", "status": "Pending"}
]
```

**Supported URL parameters:** See URL Parameters.

***

#### `GET /files/<endpoint_id>` — Fetch Export Data (Alternate Path)

Identical behavior to `GET /<endpoint_id>`. Provided for backward compatibility.

***

#### `GET /proxy/<proxy_id>` — Proxy Endpoint

Accesses an endpoint through a proxy configuration. Useful for advanced routing setups.

**Request:**

```bash
curl https://api.csvgetter.com/proxy/xyz789
```

**Error Responses:**

| Status | Message                         |
| ------ | ------------------------------- |
| 404    | `URL not found in database.`    |
| 401    | Auth-related errors (see below) |

***

### URL Parameters Reference

All `GET` export endpoints support these query parameters:

| Parameter               | Type   | Description                                                        |
| ----------------------- | ------ | ------------------------------------------------------------------ |
| `type`                  | string | Output format override (`json_records`, `xml`, `html_table`, etc.) |
| `sql`                   | string | SQL query to filter/transform data                                 |
| `nest_json`             | string | Wrap JSON output in a named key                                    |
| `filename_timestamp`    | string | Add timestamp to filename (`true`/`end`/`start`)                   |
| `extract_json_property` | string | Extract a specific property from linked records                    |
| `all_fields`            | string | Include all fields (`true`) — requires custom parameter setup      |
| `show_wait_page`        | string | Show a loading page before redirecting (`true`)                    |
| `save_to_gdrive`        | string | Save output to Google Drive (`true`)                               |
| `save_to_gsheets`       | string | Save output to Google Sheets (`true`)                              |

***

### Response Formats

#### CSV (Default)

```
Content-Type: text/csv
Content-Disposition: attachment; filename=My_Export.csv
```

#### JSON (all json\_\* types)

```
Content-Type: text/json
```

#### XML

```
Content-Type: application/xml
```

#### HTML (html\_table, dynamic\_table, excel\_web\_query)

```
Content-Type: text/html
```

***

### Error Codes

All errors return a JSON body with an `error` or `message` field:

| Status | Error                                                     | Cause                                                       |
| ------ | --------------------------------------------------------- | ----------------------------------------------------------- |
| 400    | `Could not validate sql.`                                 | Invalid SQL query syntax                                    |
| 400    | `Custom parameter 'X' is not enabled for this object.`    | Using a custom parameter not enabled for this endpoint      |
| 401    | `Authorization header is required`                        | Auth-enabled endpoint, no header provided                   |
| 401    | `Invalid Authorization header format`                     | Header doesn't start with `Bearer`                          |
| 401    | `Invalid Authorization header`                            | Wrong Bearer token                                          |
| 401    | `AUTH_HEADER_MISSING`                                     | Missing auth header (proxy route)                           |
| 401    | `INVALID_AUTH_HEADER`                                     | Wrong auth header (proxy route)                             |
| 403    | `Not enough credits. Login at csvgetter.com to purchase.` | Credit balance is zero                                      |
| 403    | `User ID not found`                                       | User account issue                                          |
| 403    | `USER_BLOCKED`                                            | Account has been blocked                                    |
| 404    | `Object not found`                                        | Endpoint ID doesn't exist                                   |
| 404    | `URL not found in database.`                              | Proxy ID doesn't exist                                      |
| 409    | `...Airtable data structure may have changed...`          | Airtable field structure changed since endpoint was created |
| 423    | `Failed to authenticate with export platform.`            | Airtable/Notion token expired or revoked                    |
| 429    | *(varies)*                                                | Airtable's own API rate limit exceeded                      |
| 500    | `Dataframe could not be retrieved`                        | Server error fetching data                                  |

***

### Rate Guidance

CSV Getter does not impose its own rate limits, but:

* **Airtable** limits API requests to 5 requests per second per base. If your endpoint hits this, you'll get a 429 error.
* **Notion** has its own rate limits.
* Each successful, non-sample export uses 1 credit.

For high-frequency use cases, consider caching the response on your end or using scheduled jobs instead.

***

### Code Examples

#### JavaScript (Node.js / Browser)

```javascript
const response = await fetch("https://api.csvgetter.com/abc123?type=json_records", {
  headers: {
    "Authorization": "Bearer my-secret-token"  // only if auth is enabled
  }
});
const data = await response.json();
console.log(data);
```

#### Python

```python
import requests

response = requests.get(
    "https://api.csvgetter.com/abc123",
    params={"type": "json_records", "sql": "SELECT * FROM csvgetter WHERE status='Active'"},
    headers={"Authorization": "Bearer my-secret-token"}  # only if auth is enabled
)
data = response.json()
print(data)
```

#### cURL

```bash
curl -H "Authorization: Bearer my-secret-token" \
  "https://api.csvgetter.com/abc123?type=json_records&sql=SELECT%20*%20FROM%20csvgetter%20LIMIT%205"
```

***

### Next Steps

* **URL Parameters** — Detailed reference for every parameter.
* **The SQL Parameter** — Full SQL query guide with examples.
* **Troubleshooting** — Solutions for every error code.


# Getting Started

This guide walks you through creating your first export endpoint — from sign-up to using your live data URL in Excel, Google Sheets, or code.

### Step 1: Create an Account

1. Go to [csvgetter.com](https://csvgetter.com) and click **Sign Up**.
2. Sign in with your Google account or email.
3. You'll land on your **Dashboard** — this is where you manage all your export endpoints.

> **Free trial:** You get 5 free endpoint hits to try everything out. No credit card required.

***

### Step 2: Connect a Data Source

Click **Create Endpoint** in the top navigation bar. You'll see the available data sources:

| Source         | Description                                                                       |
| -------------- | --------------------------------------------------------------------------------- |
| **Airtable**   | Connect via OAuth. CSV Getter reads your bases, tables, and views.                |
| **Notion**     | Connect via Notion integration. CSV Getter reads your databases.                  |
| **CSV Upload** | Upload a CSV file directly. Useful for static datasets you want to serve via URL. |

Click on your data source and follow the authorization flow. Once connected, you'll be redirected to the endpoint configuration screen.

***

### Step 3: Configure Your Endpoint

#### For Airtable:

1. Select a **Base** from the dropdown.
2. Select a **Table**.
3. (Optional) Select a **View** to filter or sort the data.
4. Choose which **Fields** (columns) to include.
5. Choose an **Output Format** — CSV or JSON.
6. Give your endpoint a **Name**.
7. Click **Create**.

#### For Notion:

1. Select a **Database** from the list.
2. Choose which **Properties** (columns) to include.
3. Choose an output format.
4. Name your endpoint and click **Create**.

#### For CSV Upload:

1. Upload your CSV file.
2. Name your endpoint and click **Create**.

***

### Step 4: Get Your Endpoint URL

After creating your endpoint, you'll see it listed on the **Export Endpoints** page (`/urls`). Each endpoint has a unique URL like:

```
https://api.csvgetter.com/abc123def456
```

This URL is **live** — every time you access it, you get the latest data from your source.

***

### Step 5: Use Your Endpoint

#### In a Web Browser

Simply paste the URL into your browser's address bar. The data will download as a CSV file (or display as JSON, depending on your settings).

#### In Microsoft Excel

1. Open Excel and go to **Data** > **From Web**.
2. Paste your endpoint URL.
3. Click **OK** — your data loads into a spreadsheet.
4. To refresh the data later, click **Refresh All** in the Data tab.

#### In Google Sheets

Use the `IMPORTDATA` function in any cell:

```
=IMPORTDATA("https://api.csvgetter.com/abc123def456")
```

The data will populate automatically and can be refreshed by re-entering the formula.

#### In Code (JavaScript)

```javascript
const response = await fetch("https://api.csvgetter.com/abc123def456");
const data = await response.text();
console.log(data);
```

#### In Code (Python)

```python
import requests
import pandas as pd
from io import StringIO

response = requests.get("https://api.csvgetter.com/abc123def456")
df = pd.read_csv(StringIO(response.text))
print(df)
```

#### With cURL

```bash
curl https://api.csvgetter.com/abc123def456
```

***

### Step 6: Customize with URL Parameters

Your endpoint URL supports parameters that change the output on the fly — no need to edit your endpoint settings. Just append them to the URL:

```
https://api.csvgetter.com/abc123def456?type=json_records
```

Common parameters:

| Parameter            | Example                                       | What it does                       |
| -------------------- | --------------------------------------------- | ---------------------------------- |
| `type`               | `?type=json_records`                          | Change output format to JSON       |
| `sql`                | `?sql=SELECT * FROM csvgetter WHERE age > 25` | Filter data with SQL               |
| `filename_timestamp` | `?filename_timestamp=true`                    | Add timestamp to download filename |

For the full list, see URL Parameters.

Use the **URL Wizard** in the endpoint editor to interactively build and test these parameters.

***

### What's Next?

* **The Export URL** — Understand URL anatomy and chaining parameters.
* **The SQL Parameter** — Filter, sort, and aggregate your data with SQL queries.
* **The Type Parameter** — All 10+ output formats explained.
* **URL Parameters** — Full reference for every parameter.
* **Credits** — How the credit system works.


# Troubleshooting

This page maps every error you might encounter when using CSV Getter to its cause and solution.

### Authentication Errors (401)

#### `Authorization header is required`

**Cause:** Your endpoint has Bearer auth enabled, but the request didn't include an `Authorization` header.

**Fix:** Add the header to your request:

```bash
curl -H "Authorization: Bearer your-token-here" https://api.csvgetter.com/abc123
```

In JavaScript:

```javascript
fetch("https://api.csvgetter.com/abc123", {
  headers: { "Authorization": "Bearer your-token-here" }
});
```

#### `Invalid Authorization header format`

**Cause:** The `Authorization` header doesn't start with `Bearer` (note the space after "Bearer").

**Fix:** Make sure the header is formatted as `Bearer <token>` with a space between "Bearer" and your token.

Wrong: `Authorization: your-token` Wrong: `Authorization: Beareryour-token` Right: `Authorization: Bearer your-token`

#### `Invalid Authorization header`

**Cause:** The Bearer token you provided doesn't match the one configured for this endpoint.

**Fix:** Log in to [csvgetter.com](https://csvgetter.com), navigate to the endpoint settings, and check the auth token. Make sure you're using the exact token shown there.

#### `AUTH_HEADER_MISSING`

**Cause:** Same as above, but returned on the proxy route (`/proxy/<id>`).

**Fix:** Same — add the correct `Authorization: Bearer <token>` header.

#### `INVALID_AUTH_HEADER`

**Cause:** Wrong token on the proxy route.

**Fix:** Check the token in your endpoint settings.

***

### Credit & Access Errors (403)

#### `Not enough credits. Login at csvgetter.com to purchase.`

**Cause:** Your credit balance is zero. Each non-preview endpoint hit uses 1 credit.

**Fix:**

1. Log in to [csvgetter.com](https://csvgetter.com).
2. Go to your Plan page.
3. Upgrade your plan or wait for your monthly credit reset.

#### `User ID not found`

**Cause:** The endpoint is associated with a user account that cannot be found.

**Fix:** Contact support at <info@csvgetter.com>.

#### `USER_BLOCKED`

**Cause:** Your account has been blocked by an administrator.

**Fix:** Contact support at <info@csvgetter.com>.

#### `Your free trial has expired`

**Cause:** You've used all 10 free trial hits without purchasing a plan.

**Fix:** Log in to [csvgetter.com](https://csvgetter.com) and select a paid plan.

#### `Please login to csvgetter.com to reactivate your URLs`

**Cause:** Your account was created but not fully initialized (you haven't logged into the dashboard yet).

**Fix:** Log in to [csvgetter.com](https://csvgetter.com) to complete account setup.

***

### Not Found Errors (404)

#### `Object not found`

**Cause:** The endpoint ID in the URL doesn't exist in the database. This happens if:

* The endpoint was deleted.
* The URL has a typo in the endpoint ID.
* You're using the wrong environment (staging vs production).

**Fix:** Check the URL against the endpoint listed in your dashboard at [csvgetter.com/urls](https://csvgetter.com/urls).

#### `URL not found in database.`

**Cause:** The proxy ID doesn't exist (on the `/proxy/<id>` route).

**Fix:** Verify the proxy URL is correct.

***

### SQL Errors (400)

#### `Could not validate sql.`

**Cause:** Your SQL query has a syntax error or references a column/table that doesn't exist.

**Response format:**

```json
{
  "error": "Could not validate sql.",
  "data": "no such column: nonexistent_column",
  "help": "https://docs.csvgetter.com/sql-parameter"
}
```

**Common sub-errors and fixes:**

| `data` field             | Cause                     | Fix                                                                                         |
| ------------------------ | ------------------------- | ------------------------------------------------------------------------------------------- |
| `no such column: X`      | Column name doesn't match | Check exact column names in your endpoint config. Column names are case-sensitive.          |
| `no such table: X`       | Wrong table name          | Always use `FROM csvgetter` — that's the only table.                                        |
| `near "X": syntax error` | SQL syntax error          | Check your SQL syntax. Common issues: missing quotes around strings, unmatched parentheses. |
| `unrecognized token`     | Special characters        | URL-encode your SQL query. Use `%20` for spaces, `%27` for single quotes, etc.              |

**Tips:**

* Column names with spaces must be wrapped in double quotes: `SELECT "First Name" FROM csvgetter`
* String values use single quotes: `WHERE status = 'Active'`
* The table is always `csvgetter`
* Use the URL Wizard to test SQL before deploying

#### `Custom parameter 'X' is not enabled for this object.`

**Cause:** You used a parameter (like `all_fields`) that requires custom configuration for this specific endpoint.

**Fix:** Contact support or check if the parameter is enabled in your endpoint settings.

***

### Data Source Errors (409, 423, 429)

#### `We received an error to indicate that your Airtable data structure may have changed.`

**Status:** 409

**Cause:** The Airtable table's field structure has changed since the endpoint was created. Fields may have been renamed, deleted, or had their types changed.

**Fix:**

1. Log in to [csvgetter.com](https://csvgetter.com).
2. Go to your endpoint settings.
3. Re-select the table and fields to match the current Airtable structure.
4. Save the endpoint.

#### `Failed to authenticate with export platform.`

**Status:** 423

**Cause:** The OAuth token for Airtable or Notion has expired, been revoked, or the integration was disconnected.

**Fix:**

1. Log in to [csvgetter.com](https://csvgetter.com).
2. Go to your Connections/Data page.
3. Reconnect the affected platform (Airtable or Notion).

#### Platform API Rate Limit (`PUBLIC_API_BILLING_LIMIT_EXCEEDED`)

**Status:** 429

**Cause:** Airtable's own API rate limit has been exceeded (5 requests per second per base).

**Fix:**

* Wait a few seconds and try again.
* If you're making automated requests, add a delay between calls.
* For high-frequency use cases, consider caching the response on your end.

***

### Server Errors (500)

#### `Dataframe could not be retrieved`

**Cause:** CSV Getter was unable to fetch or process the data from your source. This could be due to:

* A temporary issue with the data source (Airtable/Notion API down).
* A very large dataset causing a timeout.
* A misconfigured endpoint.

**Fix:**

1. Wait a minute and try again (might be a temporary issue).
2. Check if the data source is accessible directly (e.g., open Airtable and verify the base is accessible).
3. If persistent, contact support with your endpoint ID.

#### `You have been disconnected from Airtable. Please reconnect.`

**Cause:** The Airtable API key or OAuth token is invalid and couldn't be refreshed.

**Fix:** Log in to [csvgetter.com](https://csvgetter.com) and reconnect your Airtable account.

***

### Scheduled Job Failures

If a scheduled job fails, you'll receive an email with the error details:

**Subject:** `Scheduled Export Failed: <job name>`

The email includes the reason for the failure, which will match one of the errors described above.

**Common fixes for job failures:**

* Re-authenticate with the data source platform.
* Check that you have enough credits.
* Verify the endpoint still exists and hasn't been deleted.
* Check that the data source structure hasn't changed.

***

### Still Stuck?

* Email: <info@csvgetter.com>
* Book a call: [calendly.com/amjtech/csv-getter-test](https://calendly.com/amjtech/csv-getter-test)


# Credits

Credits are the usage currency in CSV Getter. Each time your export endpoint is accessed (a "hit"), it uses one credit.

### How Credits Work

#### 1 Hit = 1 Credit

Every time your endpoint URL is accessed and returns data, **1 credit is deducted** — regardless of:

* The size of the dataset (10 rows or 10,000 rows)
* The output format (CSV, JSON, XML, etc.)
* Whether URL parameters are used
* Which data source is connected

#### Previews Are Free

When you use the **URL Wizard** or **Preview** feature in the CSV Getter dashboard, the request returns a sample (up to 10 rows) and **does not use a credit**. You can preview as many times as you want.

#### Credits Reset Monthly

Your credit balance resets to your plan's full allocation on each billing cycle. Unused credits do not roll over.

***

### Plans and Credit Allocations

| Plan           | Monthly Credits | Monthly Price | Annual Price |
| -------------- | --------------- | ------------- | ------------ |
| **Free Trial** | 10 (one-time)   | Free          | Free         |
| **Lite**       | 20\*            | $5/mo         | $50/yr       |
| **Standard**   | 100\*           | $10/mo        | $100/yr      |
| **Pro**        | 500\*           | $20/mo        | $200/yr      |
| **Custom**     | Negotiable      | Custom        | Custom       |

\*With the option to add more.

***

### What Happens at Zero Credits

When your credit balance reaches zero:

1. **Endpoint requests return a 403 error:**

   ```json
   {
     "error": "Not enough credits. Login at csvgetter.com to purchase.",
     "help": "https://docs.csvgetter.com"
   }
   ```
2. **You receive an email notification** alerting you that your credits are exhausted (sent once per billing cycle).
3. **Scheduled jobs will fail** and send failure notification emails.
4. **Dashboard previews still work** — you can still preview your data and test SQL queries for free.

***

### Checking Your Remaining Credits

1. Log in to [csvgetter.com](https://csvgetter.com).
2. Your remaining credits are displayed on the **Dashboard** (home page) next to your email address.

***

### Free Trial

Every new account gets **10 free endpoint hits** to try the product. No credit card required.

* Trial hits work identically to paid credits.
* You can use all features during the trial: SQL queries, all output formats, email notifications, etc.
* After 10 hits, you'll need to select a paid plan to continue.
* Trial users receive email reminders at certain usage milestones (10, 50, 100, 150, 200, 250, 300 requests).

***

### Estimating Credit Usage

| Use Case                                         | Estimated Monthly Credits |
| ------------------------------------------------ | ------------------------- |
| Manual Excel refresh, 2x/day                     | \~60                      |
| Google Sheets `IMPORTDATA`, refreshes every hour | \~720                     |
| Zapier webhook, 1x/day                           | \~30                      |
| Google Apps Script, every 6 hours                | \~120                     |
| Scheduled job (daily)                            | \~30                      |
| Scheduled job (weekly)                           | \~4                       |
| API integration, 10 hits/day                     | \~300                     |

**Tips to reduce credit usage:**

* Cache responses on your end if your data doesn't change frequently.
* Use the `sql` parameter with `LIMIT` to reduce payload size (this doesn't save credits, but speeds up requests).
* Use scheduled jobs instead of frequent polling — jobs run at defined intervals rather than on every request.
* Use dashboard previews (free) to test and iterate before deploying automated exports.

***

### Custom Plans

If you need more than 500 credits per month, or need custom features:

* **Book a call:** [calendly.com/amjtech/csv-getter-test](https://calendly.com/amjtech/csv-getter-test)
* **Email:** <info@csvgetter.com>

***

### FAQ

**Q: Does changing the output format use extra credits?** A: No. One hit = one credit, regardless of format.

**Q: Do failed requests use credits?** A: No. Credits are only deducted when data is successfully fetched and returned. Authentication failures, SQL errors, and other errors do not use credits.

**Q: Can I buy additional credits without upgrading my plan?** A: Contact <info@csvgetter.com> for add-on credit options.

**Q: Do dashboard previews count as hits?** A: No. Previews and the URL Wizard use a sample mode that does not deduct credits.

**Q: What happens to my endpoints if my subscription expires?** A: Your endpoints remain configured but requests will return a 403 error until you resubscribe.


# Zapier Integration

You can use CSV Getter with Zapier to automatically fetch your data on a schedule, when a trigger fires, or as part of a multi-step workflow.

### Overview

CSV Getter endpoints are standard URLs that return data. In Zapier, you use the **Webhooks by Zapier** action to fetch data from your endpoint URL. This lets you:

* Pull Airtable/Notion data into any Zapier-supported app
* Run exports on a schedule (hourly, daily, weekly)
* Trigger exports when something happens in another app
* Chain CSV Getter output into downstream actions (email, Slack, Google Sheets, etc.)

***

### Step-by-Step: Fetch CSV Getter Data in Zapier

#### Step 1: Create a Zap

1. Log in to [zapier.com](https://zapier.com) and click **Create Zap**.
2. Choose your **Trigger**:
   * **Schedule by Zapier** — Run on a schedule (every hour, day, week, etc.)
   * **Any other trigger** — Run when something happens in another app

#### Step 2: Add a Webhooks Action

1. Click **+** to add an action step.
2. Search for **Webhooks by Zapier**.
3. Choose **GET** as the action event.
4. Click **Continue**.

#### Step 3: Configure the Request

**URL:** Paste your CSV Getter endpoint URL:

```
https://api.csvgetter.com/abc123def456
```

**To add URL parameters**, append them directly to the URL:

```
https://api.csvgetter.com/abc123def456?type=json_records&sql=SELECT * FROM csvgetter WHERE status='Active'
```

**Headers (if auth is enabled on your endpoint):**

| Key             | Value                      |
| --------------- | -------------------------- |
| `Authorization` | `Bearer your-secret-token` |

Leave all other fields at their defaults:

* **Payload Type:** None
* **Data:** (leave empty)

#### Step 4: Test the Action

1. Click **Test step**.
2. You should see your data returned in the response body.
3. If using JSON format (`?type=json_records`), Zapier will automatically parse the fields.

> **Tip:** Use `?type=json_records` for Zapier integrations. JSON is much easier for Zapier to parse and use in downstream steps than CSV.

#### Step 5: Use the Data in Downstream Actions

Once the Webhooks step returns data, you can use it in subsequent Zapier actions:

* **Gmail / Email** — Send the data as an email body or attachment
* **Google Sheets** — Write rows to a spreadsheet
* **Slack** — Post a summary message
* **Any app** — Map the JSON fields to the app's input fields

***

### Example: Daily Export to Google Sheets via Zapier

#### Trigger: Schedule by Zapier

* **Frequency:** Every day
* **Time:** 9:00 AM

#### Action 1: Webhooks by Zapier (GET)

* **URL:** `https://api.csvgetter.com/abc123?type=json_records`

#### Action 2: Google Sheets — Create Spreadsheet Row

* Map fields from the webhook response to columns in your Google Sheet.

***

### Example: Zapier Code Step (Advanced)

If you need more control, use a **Code by Zapier** step (JavaScript):

```javascript
const response = await fetch("https://api.csvgetter.com/abc123?type=json_records", {
  headers: {
    "Authorization": "Bearer your-token-here"  // only if auth is enabled
  }
});

const data = await response.json();

// Return the first record as output fields
// Or process the data as needed
return {
  total_records: data.length,
  first_name: data[0]?.name || "No data",
  first_email: data[0]?.email || "No data",
  all_data: JSON.stringify(data)
};
```

Or in Python:

```python
import requests

response = requests.get(
    "https://api.csvgetter.com/abc123?type=json_records",
    headers={"Authorization": "Bearer your-token-here"}  # only if auth is enabled
)

data = response.json()

return {
    "total_records": len(data),
    "first_name": data[0].get("name", "No data") if data else "No data",
    "all_data": str(data)
}
```

***

### Error Handling in Zapier

If your CSV Getter endpoint returns an error (non-200 status code), the Zapier step will fail. Common causes:

| Error            | Zapier Behavior | Fix                                    |
| ---------------- | --------------- | -------------------------------------- |
| 401 (Auth error) | Step fails      | Check Bearer token in headers          |
| 403 (No credits) | Step fails      | Purchase more credits at csvgetter.com |
| 400 (Bad SQL)    | Step fails      | Fix the SQL query in your URL          |
| 404 (Not found)  | Step fails      | Verify endpoint ID in the URL          |

**To handle errors gracefully:**

1. In your Zap, click on the Webhooks step.
2. Enable **Continue on error** (in advanced settings).
3. Add a downstream action that checks the status code and sends an alert if it's not 200.

***

### Tips

* **Use JSON format** (`?type=json_records`) — Zapier parses JSON fields automatically, making them available as individual fields in downstream steps.
* **Use `nest_json`** if your downstream app expects data under a specific key: `?type=json_records&nest_json=results`
* **Credit usage:** Each Zapier webhook call uses 1 credit. If your Zap runs hourly, that's \~720 credits/month.
* **SQL filtering:** Use the `sql` parameter to reduce the data to only what you need, keeping payloads small and Zapier steps fast.
* **Rate limits:** If running multiple Zaps against the same Airtable base, space them out to avoid Airtable's 5 req/sec limit.


# Google Apps Script Integration

Use Google Apps Script to automatically import CSV Getter data into Google Sheets on a schedule. This guide covers the complete setup: script, error handling, email alerts, and time-driven triggers.

### Quick Start

#### Step 1: Open the Script Editor

1. Open your Google Sheet.
2. Go to **Extensions > Apps Script**.
3. Delete any existing code in the editor.

#### Step 2: Paste the Script

```javascript
/**
 * Fetches data from CSV Getter and writes it to the active sheet.
 * Replace ENDPOINT_URL with your actual CSV Getter endpoint URL.
 */

// ========== CONFIGURATION ==========
var ENDPOINT_URL = "https://api.csvgetter.com/YOUR_ENDPOINT_ID";
var SHEET_NAME = "Sheet1";                    // Target sheet tab name
var BEARER_TOKEN = "";                        // Leave empty if auth is not enabled
var NOTIFY_EMAIL = "";                        // Email for failure alerts (leave empty to skip)
// ====================================

function fetchCSVGetterData() {
  try {
    var options = {};

    // Add auth header if Bearer token is set
    if (BEARER_TOKEN) {
      options = {
        headers: {
          "Authorization": "Bearer " + BEARER_TOKEN
        }
      };
    }

    // Fetch data from CSV Getter
    var response = UrlFetchApp.fetch(ENDPOINT_URL, options);
    var statusCode = response.getResponseCode();

    if (statusCode !== 200) {
      var errorMsg = "CSV Getter returned status " + statusCode + ": " + response.getContentText();
      Logger.log(errorMsg);
      sendFailureAlert(errorMsg);
      return;
    }

    var csvContent = response.getContentText();

    if (!csvContent || csvContent.trim() === "") {
      var emptyMsg = "CSV Getter returned empty data.";
      Logger.log(emptyMsg);
      sendFailureAlert(emptyMsg);
      return;
    }

    // Parse CSV into rows
    var rows = Utilities.parseCsv(csvContent);

    // Get the target sheet
    var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    var sheet = spreadsheet.getSheetByName(SHEET_NAME);

    if (!sheet) {
      sheet = spreadsheet.insertSheet(SHEET_NAME);
    }

    // Clear existing data and write new data
    sheet.clearContents();
    sheet.getRange(1, 1, rows.length, rows[0].length).setValues(rows);

    Logger.log("Successfully imported " + rows.length + " rows (including header) at " + new Date().toISOString());

  } catch (error) {
    var errMsg = "Error fetching CSV Getter data: " + error.message;
    Logger.log(errMsg);
    sendFailureAlert(errMsg);
  }
}

/**
 * Sends an email alert when the import fails.
 */
function sendFailureAlert(errorMessage) {
  if (!NOTIFY_EMAIL) return;

  try {
    var subject = "CSV Getter Import Failed - " + SpreadsheetApp.getActiveSpreadsheet().getName();
    var body = "The scheduled CSV Getter import failed.\n\n" +
               "Spreadsheet: " + SpreadsheetApp.getActiveSpreadsheet().getUrl() + "\n" +
               "Sheet: " + SHEET_NAME + "\n" +
               "Endpoint: " + ENDPOINT_URL + "\n" +
               "Time: " + new Date().toISOString() + "\n\n" +
               "Error: " + errorMessage + "\n\n" +
               "Please check your endpoint at https://csvgetter.com";

    MailApp.sendEmail(NOTIFY_EMAIL, subject, body);
    Logger.log("Failure alert sent to " + NOTIFY_EMAIL);
  } catch (e) {
    Logger.log("Could not send failure alert email: " + e.message);
  }
}
```

#### Step 3: Configure

1. Replace `YOUR_ENDPOINT_ID` with your actual endpoint ID.
2. Set `SHEET_NAME` to the tab name where data should be written.
3. If your endpoint has auth enabled, set `BEARER_TOKEN` to your token.
4. (Optional) Set `NOTIFY_EMAIL` to receive alerts on failure.

#### Step 4: Test

1. Click the **Run** button (play icon) at the top.
2. The first time, Google will ask you to authorize the script. Click **Review Permissions** > **Allow**.
3. Check your Google Sheet — the data should appear.
4. Check **Execution log** (View > Execution log) for success/error messages.

***

### Setting Up a Time-Driven Trigger

To run the import automatically on a schedule:

#### Option A: Via the Apps Script UI

1. In the Apps Script editor, click the **Triggers** icon (clock icon) in the left sidebar.
2. Click **+ Add Trigger**.
3. Configure:
   * **Function:** `fetchCSVGetterData`
   * **Deployment:** Head
   * **Event source:** Time-driven
   * **Type of time-based trigger:** Choose one:
     * **Minutes timer** — Every 5, 10, 15, or 30 minutes
     * **Hour timer** — Every 1, 2, 4, 6, 8, or 12 hours
     * **Day timer** — Once per day (pick the time window)
     * **Week timer** — Once per week (pick day and time)
4. Click **Save**.

#### Option B: Via Code

Add this function to your script and run it once to create the trigger:

```javascript
/**
 * Creates a time-driven trigger. Run this function ONCE to set up the schedule.
 */
function createDailyTrigger() {
  // Delete any existing triggers for this function to avoid duplicates
  var triggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < triggers.length; i++) {
    if (triggers[i].getHandlerFunction() === "fetchCSVGetterData") {
      ScriptApp.deleteTrigger(triggers[i]);
    }
  }

  // Create a new daily trigger at 8:00-9:00 AM
  ScriptApp.newTrigger("fetchCSVGetterData")
    .timeBased()
    .everyDays(1)
    .atHour(8)
    .create();

  Logger.log("Daily trigger created.");
}
```

Other schedule options:

```javascript
// Every hour
ScriptApp.newTrigger("fetchCSVGetterData")
  .timeBased()
  .everyHours(1)
  .create();

// Every 30 minutes
ScriptApp.newTrigger("fetchCSVGetterData")
  .timeBased()
  .everyMinutes(30)
  .create();

// Every Monday at 9 AM
ScriptApp.newTrigger("fetchCSVGetterData")
  .timeBased()
  .onWeekDay(ScriptApp.WeekDay.MONDAY)
  .atHour(9)
  .create();
```

***

### Importing JSON Instead of CSV

If you prefer JSON (e.g., for more control over parsing):

```javascript
function fetchCSVGetterDataAsJSON() {
  try {
    var url = ENDPOINT_URL + "?type=json_records";
    var options = {};

    if (BEARER_TOKEN) {
      options = {
        headers: { "Authorization": "Bearer " + BEARER_TOKEN }
      };
    }

    var response = UrlFetchApp.fetch(url, options);

    if (response.getResponseCode() !== 200) {
      sendFailureAlert("Status " + response.getResponseCode() + ": " + response.getContentText());
      return;
    }

    var data = JSON.parse(response.getContentText());

    if (!data || data.length === 0) {
      sendFailureAlert("No data returned.");
      return;
    }

    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
    if (!sheet) {
      sheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet(SHEET_NAME);
    }

    // Write headers from first object's keys
    var headers = Object.keys(data[0]);
    sheet.clearContents();
    sheet.getRange(1, 1, 1, headers.length).setValues([headers]);

    // Write data rows
    var rows = data.map(function(record) {
      return headers.map(function(header) {
        var value = record[header];
        return value !== null && value !== undefined ? value : "";
      });
    });

    if (rows.length > 0) {
      sheet.getRange(2, 1, rows.length, headers.length).setValues(rows);
    }

    Logger.log("Imported " + rows.length + " records.");

  } catch (error) {
    sendFailureAlert(error.message);
  }
}
```

***

### Using SQL Filters

Add SQL to the URL to only import filtered data:

```javascript
var ENDPOINT_URL = "https://api.csvgetter.com/YOUR_ENDPOINT_ID?sql=" +
  encodeURIComponent("SELECT name, email, status FROM csvgetter WHERE status = 'Active' ORDER BY name");
```

***

### Credit Usage

Each time the script runs and hits the endpoint, it uses 1 credit. Plan your trigger frequency accordingly:

| Frequency     | Credits/Month |
| ------------- | ------------- |
| Every 30 min  | \~1,440       |
| Every hour    | \~720         |
| Every 6 hours | \~120         |
| Daily         | \~30          |
| Weekly        | \~4           |

***

### Troubleshooting

| Issue                             | Cause                                             | Fix                                         |
| --------------------------------- | ------------------------------------------------- | ------------------------------------------- |
| `Exception: Request failed`       | Endpoint URL is wrong or server is down           | Verify the URL works in a browser first     |
| `401 Unauthorized`                | Bearer token is missing or wrong                  | Check `BEARER_TOKEN` value                  |
| `403 Forbidden`                   | Out of credits                                    | Purchase credits at csvgetter.com           |
| `Exceeded maximum execution time` | Dataset too large for Apps Script timeout (6 min) | Add `sql=...LIMIT 5000` to reduce data size |
| Script runs but sheet is empty    | Wrong `SHEET_NAME`                                | Check the tab name matches exactly          |
| Trigger not firing                | Trigger wasn't saved or was deleted               | Re-create the trigger in the Triggers panel |


# Connect Notion Database

Make sure the database is selected when connecting Notion.

<figure><img src="/files/28Zdu4n8ZOdNmrtlbW96" alt="" width="375"><figcaption></figcaption></figure>

Open the database to get the correct database Link.

<figure><img src="/files/bS9YYh46gCmy4cquD9yY" alt="" width="375"><figcaption></figcaption></figure>


# URL Parameters

Every CSV Getter export endpoint supports query parameters that modify the output dynamically. Append them to your endpoint URL.

```
https://api.csvgetter.com/abc123?param1=value1&param2=value2
```

***

### Quick Reference

| Parameter               | Values                                    | Description                                     |
| ----------------------- | ----------------------------------------- | ----------------------------------------------- |
| `type`                  | `json_records`, `xml`, `html_table`, etc. | Override output format                          |
| `sql`                   | SQL query string                          | Filter and transform data with SQL              |
| `nest_json`             | Key name string                           | Wrap JSON output in a named key                 |
| `filename_timestamp`    | `true`, `end`, `start`                    | Add timestamp to download filename              |
| `extract_json_property` | Property name string                      | Extract a specific property from linked records |
| `all_fields`            | `true`                                    | Include all fields from the source              |
| `show_wait_page`        | `true`                                    | Show a loading page before delivering data      |
| `save_to_gdrive`        | `true`                                    | Save export to Google Drive                     |
| `save_to_gsheets`       | `true`                                    | Save export to Google Sheets                    |

***

### Detailed Reference

#### `type`

Override the output format for this request.

**Values:** `json_records`, `json_split`, `json_index`, `json_columns`, `json_values`, `json_table`, `xml`, `html_table`, `dynamic_table`, `excel_web_query`

**Example:**

```
?type=json_records
```

Returns your data as a JSON array of objects instead of CSV.

```
?type=xml
```

Returns your data as an XML document.

See The Type Parameter for sample output for every format.

***

#### `sql`

Run a SQL query against your data to filter, sort, aggregate, or transform it.

**Value:** A SQL `SELECT` statement. The table name is always `csvgetter`.

**Example — filter rows:**

```
?sql=SELECT * FROM csvgetter WHERE status = 'Active'
```

**Example — select specific columns and sort:**

```
?sql=SELECT name, email FROM csvgetter ORDER BY name ASC
```

**Example — aggregate:**

```
?sql=SELECT department, COUNT(*) as count FROM csvgetter GROUP BY department
```

**Example — limit results:**

```
?sql=SELECT * FROM csvgetter LIMIT 10
```

Remember to URL-encode the query if building URLs manually. Spaces become `%20`, `=` becomes `%3D`, etc.

See The SQL Parameter for 19+ examples.

***

#### `nest_json`

Wrap JSON output in a top-level key. Only applies when the output is a JSON format.

**Value:** The key name to wrap the data in.

**Example:**

```
?type=json_records&nest_json=items
```

**Without `nest_json`:**

```json
[{"name": "Alice"}, {"name": "Bob"}]
```

**With `nest_json=items`:**

```json
{"items": [{"name": "Alice"}, {"name": "Bob"}]}
```

Useful when your consuming application expects data nested under a specific key.

***

#### `filename_timestamp`

Add a UTC timestamp to the downloaded filename.

**Values:**

* `true` or `end` — Append timestamp to the end: `My_Export_2025-03-15T143022.csv`
* `start` — Prepend timestamp to the beginning: `2025-03-15T143022_My_Export.csv`

**Example:**

```
?filename_timestamp=true
```

The timestamp format is `YYYY-MM-DDTHHMMSS` in UTC.

***

#### `extract_json_property`

When your Airtable data contains linked records (which come as JSON arrays), extract a specific property from each linked record.

**Value:** The property name to extract.

**Example:**

```
?extract_json_property=name
```

Without this parameter, a linked record field might show:

```
[{"id": "rec123", "name": "Alice"}, {"id": "rec456", "name": "Bob"}]
```

With `?extract_json_property=name`, it becomes:

```
Alice, Bob
```

***

#### `all_fields`

Include all fields from the source table, overriding the field selection you configured in the endpoint.

**Value:** `true`

**Example:**

```
?all_fields=true
```

> **Note:** This parameter must be enabled for your endpoint. It requires a custom parameter configuration. Contact support or check your endpoint settings if you receive a 400 error.

***

#### `show_wait_page`

Display a loading page in the browser while the data is being fetched, then redirect to the data.

**Value:** `true`

**Example:**

```
?show_wait_page=true
```

This is useful for large datasets where the export takes several seconds. Instead of the browser appearing to hang, users see a "Please wait" page.

***

#### `save_to_gdrive`

Save the export output as a file to your connected Google Drive.

**Value:** `true`

**Example:**

```
?save_to_gdrive=true
```

Requires a connected Google account. The file is saved to your Google Drive root folder.

Combine with `filename_timestamp` to avoid overwriting previous exports:

```
?save_to_gdrive=true&filename_timestamp=true
```

***

#### `save_to_gsheets`

Save the export output directly to a Google Sheets spreadsheet.

**Value:** `true`

**Example:**

```
?save_to_gsheets=true&spreadsheet=SPREADSHEET_ID&sheet=SHEET_NAME
```

Requires a connected Google account and the spreadsheet/sheet parameters.

***

### Combining Parameters

All parameters can be combined freely:

```
?type=json_records&sql=SELECT name, email FROM csvgetter WHERE status='Active'&nest_json=data
```


# Combining URL Parameters

URL parameters can be chained together to build powerful data workflows — all from a single URL. This page shows real-world recipes for common scenarios.

### How Parameters Chain

Append parameters to your endpoint URL with `?` for the first and `&` for each additional:

```
https://api.csvgetter.com/abc123?param1=value1&param2=value2&param3=value3
```

Parameters are processed in this order:

1. **Authentication** — Bearer token checked (if enabled)
2. **Data fetch** — Full dataset retrieved from source
3. **`all_fields`** — Override field selection (if set)
4. **`extract_json_property`** — Flatten linked records (if set)
5. **`sql`** — SQL query applied to filter/transform data
6. **`type`** — Output format applied
7. **`nest_json`** — JSON output wrapped in key (if set)
8. **`filename_timestamp`** — Filename modified (if set)
9. **Side effects** — `email_me`, `save_to_gdrive`, `save_to_gsheets`, etc.

***

### Recipe 1: Filter with SQL + Output as JSON + Email Notification

**Scenario:** You want active customers as JSON and get an email each time it runs.

```
https://api.csvgetter.com/abc123?sql=SELECT name, email, plan FROM csvgetter WHERE status='Active' ORDER BY name&type=json_records&email_me=true&email_tag=active-customers
```

| Parameter   | Value                                                                         | Purpose                                  |
| ----------- | ----------------------------------------------------------------------------- | ---------------------------------------- |
| `sql`       | `SELECT name, email, plan FROM csvgetter WHERE status='Active' ORDER BY name` | Filter to active, select 3 columns, sort |
| `type`      | `json_records`                                                                | JSON array of objects                    |
| `email_me`  | `true`                                                                        | Email notification                       |
| `email_tag` | `active-customers`                                                            | Label the notification                   |

***

### Recipe 2: Export to Google Drive with Timestamped Filename

**Scenario:** Daily backup of your Airtable data to Google Drive, with each file uniquely named.

```
https://api.csvgetter.com/abc123?save_to_gdrive=true&filename_timestamp=true
```

| Parameter            | Value  | Purpose                                 |
| -------------------- | ------ | --------------------------------------- |
| `save_to_gdrive`     | `true` | Save to connected Google Drive          |
| `filename_timestamp` | `true` | Append `_2025-03-15T143022` to filename |

Result: A file like `Customers_2025-03-15T143022.csv` appears in your Google Drive.

**With timestamp at the start:**

```
?save_to_gdrive=true&filename_timestamp=start
```

Result: `2025-03-15T143022_Customers.csv`

***

### Recipe 3: SQL Aggregation as Nested JSON for an API

**Scenario:** You're building a dashboard that expects data nested under a `data` key, showing department headcounts.

```
https://api.csvgetter.com/abc123?sql=SELECT department, COUNT(*) as headcount, AVG(salary) as avg_salary FROM csvgetter GROUP BY department ORDER BY headcount DESC&type=json_records&nest_json=data
```

| Parameter   | Value                                         | Purpose                          |
| ----------- | --------------------------------------------- | -------------------------------- |
| `sql`       | `SELECT department, COUNT(*) as headcount...` | Aggregate by department          |
| `type`      | `json_records`                                | JSON output                      |
| `nest_json` | `data`                                        | Wrap result in `{"data": [...]}` |

**Response:**

```json
{
  "data": [
    {"department": "Engineering", "headcount": 42, "avg_salary": 95000},
    {"department": "Marketing", "headcount": 18, "avg_salary": 72000}
  ]
}
```

***

### Recipe 4: Interactive Table for Stakeholders

**Scenario:** Share a browsable, searchable view of your data with non-technical team members.

```
https://api.csvgetter.com/abc123?type=dynamic_table&sql=SELECT name, role, department, start_date FROM csvgetter ORDER BY start_date DESC
```

| Parameter | Value                                          | Purpose                                   |
| --------- | ---------------------------------------------- | ----------------------------------------- |
| `type`    | `dynamic_table`                                | Interactive HTML with search + pagination |
| `sql`     | `SELECT name, role, department, start_date...` | Select relevant columns, sort by date     |

Share this URL directly — recipients see a searchable table in their browser.

***

### Recipe 5: Excel Import with Loading Page

**Scenario:** Large dataset that takes a few seconds to load. You want a "please wait" page rather than a browser timeout.

```
https://api.csvgetter.com/abc123?type=excel_web_query&show_wait_page=true
```

| Parameter        | Value             | Purpose                              |
| ---------------- | ----------------- | ------------------------------------ |
| `type`           | `excel_web_query` | Format for Excel's "From Web"        |
| `show_wait_page` | `true`            | Show loading page while data fetches |

***

### Recipe 6: Top 10 Records as XML

**Scenario:** Your integration requires XML and you only need the most recent 10 entries.

```
https://api.csvgetter.com/abc123?sql=SELECT * FROM csvgetter ORDER BY created_at DESC LIMIT 10&type=xml
```

| Parameter | Value                                  | Purpose           |
| --------- | -------------------------------------- | ----------------- |
| `sql`     | `...ORDER BY created_at DESC LIMIT 10` | Latest 10 records |
| `type`    | `xml`                                  | XML output        |

***

### Recipe 7: Filtered Data to Google Sheets

**Scenario:** Update a specific Google Sheet with only high-priority items.

```
https://api.csvgetter.com/abc123?sql=SELECT * FROM csvgetter WHERE priority='High'&save_to_gsheets=true&spreadsheet=SPREADSHEET_ID&sheet=Sheet1
```

| Parameter         | Value                      | Purpose                 |
| ----------------- | -------------------------- | ----------------------- |
| `sql`             | `...WHERE priority='High'` | Filter to high priority |
| `save_to_gsheets` | `true`                     | Write to Google Sheets  |
| `spreadsheet`     | Your spreadsheet ID        | Target spreadsheet      |
| `sheet`           | `Sheet1`                   | Target sheet/tab        |

***

### Recipe 8: Flattened Linked Records as JSON

**Scenario:** Your Airtable has linked record fields that return as JSON arrays. You want to extract just the names.

```
https://api.csvgetter.com/abc123?extract_json_property=name&type=json_records
```

| Parameter               | Value          | Purpose                                   |
| ----------------------- | -------------- | ----------------------------------------- |
| `extract_json_property` | `name`         | Extract `name` from linked record objects |
| `type`                  | `json_records` | JSON output                               |

**Before:**

```json
[{"assignee": "[{\"id\":\"rec1\",\"name\":\"Alice\"},{\"id\":\"rec2\",\"name\":\"Bob\"}]"}]
```

**After:**

```json
[{"assignee": "Alice, Bob"}]
```

***

### Recipe 9: Upload Training Data to OpenAI

**Scenario:** Send a filtered dataset to OpenAI for use with the Assistants API.

```
https://api.csvgetter.com/abc123?sql=SELECT question, answer FROM csvgetter WHERE verified='true'&save_to_openai=true&save_to_openai_filename=qa_training_data.csv
```

| Parameter                 | Value                      | Purpose                  |
| ------------------------- | -------------------------- | ------------------------ |
| `sql`                     | `...WHERE verified='true'` | Only verified Q\&A pairs |
| `save_to_openai`          | `true`                     | Upload to OpenAI         |
| `save_to_openai_filename` | `qa_training_data.csv`     | Custom filename          |

***

### Recipe 10: Authenticated Export with Email + Drive Backup

**Scenario:** Secure endpoint that also saves to Drive and notifies you.

```bash
curl -H "Authorization: Bearer my-secret-token" \
  "https://api.csvgetter.com/abc123?save_to_gdrive=true&filename_timestamp=end&email_me=true&email_tag=secure-backup"
```

| Parameter            | Value                    | Purpose                    |
| -------------------- | ------------------------ | -------------------------- |
| Auth header          | `Bearer my-secret-token` | Authenticate the request   |
| `save_to_gdrive`     | `true`                   | Backup to Drive            |
| `filename_timestamp` | `end`                    | Unique filename            |
| `email_me`           | `true`                   | Notification               |
| `email_tag`          | `secure-backup`          | Label for the notification |


# The Type Parameter

Create custom functionality with the 'type' parameter.

The `type` parameter lets you change the output format of your export endpoint on the fly. Append it to your URL:

```
https://api.csvgetter.com/abc123?type=json_records
```

If you don't specify a `type`, the endpoint uses its default format (CSV or JSON, depending on how you configured it).

***

### Format Comparison

| `type` Value      | Format          | MIME Type         | Best For                                           |
| ----------------- | --------------- | ----------------- | -------------------------------------------------- |
| *(none/default)*  | CSV             | `text/csv`        | Spreadsheets, data import, universal compatibility |
| `json_records`    | JSON (Records)  | `text/json`       | APIs, JavaScript apps, most JSON use cases         |
| `json_split`      | JSON (Split)    | `text/json`       | Data science tools, columnar processing            |
| `json_index`      | JSON (Index)    | `text/json`       | Row-keyed lookups                                  |
| `json_columns`    | JSON (Columns)  | `text/json`       | Column-oriented analysis                           |
| `json_values`     | JSON (Values)   | `text/json`       | Lightweight data transfer (no headers in body)     |
| `json_table`      | JSON (Table)    | `text/json`       | Schema-aware consumers, typed data                 |
| `xml`             | XML             | `application/xml` | Enterprise integrations, SOAP services             |
| `html_table`      | HTML Table      | `text/html`       | Embedding in web pages, simple viewing             |
| `dynamic_table`   | Dynamic Table   | `text/html`       | Interactive viewing with search and pagination     |
| `excel_web_query` | Excel Web Query | `text/html`       | Excel "Data from Web" feature                      |

***

### Sample Output for Each Format

Using this sample data:

| name  | email               | age |
| ----- | ------------------- | --- |
| Alice | <alice@example.com> | 30  |
| Bob   | <bob@example.com>   | 25  |

#### CSV (Default)

```
name,email,age
Alice,alice@example.com,30
Bob,bob@example.com,25
```

**When to use:** Universal format. Works with Excel, Google Sheets, pandas, any tool that reads CSV.

***

#### `json_records`

```json
[
  {"name": "Alice", "email": "alice@example.com", "age": 30},
  {"name": "Bob", "email": "bob@example.com", "age": 25}
]
```

**When to use:** The most common JSON format. Each row is an object. Ideal for JavaScript `fetch()`, REST APIs, and most programming use cases.

***

#### `json_split`

```json
{
  "columns": ["name", "email", "age"],
  "index": [1, 2],
  "data": [
    ["Alice", "alice@example.com", 30],
    ["Bob", "bob@example.com", 25]
  ]
}
```

**When to use:** Separates column names from data. Useful when you need to process headers independently, or for data science tools like pandas (`pd.read_json(url, orient='split')`).

***

#### `json_index`

```json
{
  "1": {"name": "Alice", "email": "alice@example.com", "age": 30},
  "2": {"name": "Bob", "email": "bob@example.com", "age": 25}
}
```

**When to use:** When you need to look up rows by their index. Each row is keyed by its row number.

***

#### `json_columns`

```json
{
  "name": {"1": "Alice", "2": "Bob"},
  "email": {"1": "alice@example.com", "2": "bob@example.com"},
  "age": {"1": 30, "2": 25}
}
```

**When to use:** When you want to access all values for a specific column. Good for column-by-column processing.

***

#### `json_values`

```json
[
  ["Alice", "alice@example.com", 30],
  ["Bob", "bob@example.com", 25]
]
```

**When to use:** Smallest JSON payload — no column names, no indices. Use when you already know the column order and want minimal data transfer.

***

#### `json_table`

```json
{
  "schema": {
    "fields": [
      {"name": "index", "type": "integer"},
      {"name": "name", "type": "string"},
      {"name": "email", "type": "string"},
      {"name": "age", "type": "integer"}
    ],
    "primaryKey": ["index"],
    "pandas_version": "1.4.0"
  },
  "data": [
    {"index": 1, "name": "Alice", "email": "alice@example.com", "age": 30},
    {"index": 2, "name": "Bob", "email": "bob@example.com", "age": 25}
  ]
}
```

**When to use:** Includes a schema with data types. Good for consumers that need to know column types (integer vs string) without guessing. CSV Getter automatically detects numeric columns.

***

#### `xml`

```xml
<?xml version='1.0' encoding='utf-8'?>
<data>
  <row>
    <name>Alice</name>
    <email>alice@example.com</email>
    <age>30</age>
  </row>
  <row>
    <name>Bob</name>
    <email>bob@example.com</email>
    <age>25</age>
  </row>
</data>
```

**When to use:** Enterprise integrations, SOAP services, or any system that requires XML input.

> **Note:** Column names with special characters (spaces, punctuation) are automatically converted to underscores in XML output.

***

#### `html_table`

Renders a static HTML page with the data in a `<table>` element.

**When to use:** Quick data viewing in a browser, embedding in web pages via iframe, or simple reporting.

***

#### `dynamic_table`

Renders an interactive HTML page with:

* Searchable columns
* Sortable headers
* Pagination

**When to use:** Sharing data with non-technical users who want to browse and search interactively.

***

#### `excel_web_query`

Renders an HTML table optimized for Excel's "Data from Web" import feature.

**When to use:** Specifically for pulling data into Excel using **Data > From Web**. The HTML is structured so Excel recognizes the table correctly.

***

### Using `nest_json` with JSON Formats

The `nest_json` parameter wraps any JSON output in a named top-level key:

```
?type=json_records&nest_json=results
```

**Without `nest_json`:**

```json
[
  {"name": "Alice", "age": 30}
]
```

**With `nest_json=results`:**

```json
{
  "results": [
    {"name": "Alice", "age": 30}
  ]
}
```

This works with all `json_*` formats and is useful when your consuming application expects a specific JSON structure.

***

### Choosing the Right Format

| Use Case                       | Recommended Format           |
| ------------------------------ | ---------------------------- |
| Import into Excel              | CSV or `excel_web_query`     |
| Import into Google Sheets      | CSV (via `IMPORTDATA`)       |
| JavaScript / web app           | `json_records`               |
| Python / pandas                | CSV or `json_split`          |
| Enterprise / legacy system     | `xml`                        |
| Share with non-technical users | `dynamic_table`              |
| Embed in a web page            | `html_table`                 |
| Minimal payload size           | `json_values`                |
| Need schema/type info          | `json_table`                 |
| API that expects nested JSON   | `json_records` + `nest_json` |


# The SQL Parameter

The sql parameter lets you filter, sort, aggregate, and transform your data using SQL queries — directly in the URL. The query runs against your data after it's fetched from the source, using SQLite

### How It Works

1. CSV Getter fetches your full dataset from Airtable/Notion/CSV.
2. The data is loaded into an in-memory SQLite database as a table called **`csvgetter`**.
3. Your SQL query runs against that table.
4. The filtered/transformed result is returned.

> **Important:** The table name is always `csvgetter`. Use this in your `FROM` clause.

***

### Basic Syntax

```
?sql=SELECT * FROM csvgetter
```

URL-encoded:

```
?sql=SELECT%20*%20FROM%20csvgetter
```

> **Tip:** The URL Wizard in the dashboard builds and encodes URLs for you automatically.

***

### Examples

#### 1. Select All Data

```sql
SELECT * FROM csvgetter
```

```
?sql=SELECT%20*%20FROM%20csvgetter
```

#### 2. Select Specific Columns

```sql
SELECT name, email, status FROM csvgetter
```

```
?sql=SELECT%20name%2C%20email%2C%20status%20FROM%20csvgetter
```

#### 3. Filter with WHERE

```sql
SELECT * FROM csvgetter WHERE status = 'Active'
```

```
?sql=SELECT%20*%20FROM%20csvgetter%20WHERE%20status%20%3D%20%27Active%27
```

#### 4. Multiple Conditions (AND / OR)

```sql
SELECT * FROM csvgetter WHERE status = 'Active' AND department = 'Engineering'
```

```sql
SELECT * FROM csvgetter WHERE status = 'Active' OR status = 'Pending'
```

#### 5. Comparison Operators

```sql
SELECT * FROM csvgetter WHERE age > 25
```

```sql
SELECT * FROM csvgetter WHERE salary >= 50000 AND salary <= 100000
```

```sql
SELECT * FROM csvgetter WHERE hire_date > '2024-01-01'
```

#### 6. LIKE (Pattern Matching)

```sql
SELECT * FROM csvgetter WHERE name LIKE 'John%'
```

```sql
SELECT * FROM csvgetter WHERE email LIKE '%@gmail.com'
```

```sql
SELECT * FROM csvgetter WHERE city LIKE '%York%'
```

#### 7. IN (Multiple Values)

```sql
SELECT * FROM csvgetter WHERE department IN ('Engineering', 'Marketing', 'Sales')
```

#### 8. ORDER BY (Sorting)

```sql
SELECT * FROM csvgetter ORDER BY name ASC
```

```sql
SELECT * FROM csvgetter ORDER BY created_date DESC
```

```sql
SELECT * FROM csvgetter ORDER BY department ASC, salary DESC
```

#### 9. LIMIT (Row Count)

```sql
SELECT * FROM csvgetter LIMIT 10
```

```sql
SELECT * FROM csvgetter ORDER BY created_date DESC LIMIT 5
```

#### 10. LIMIT with OFFSET (Pagination)

```sql
SELECT * FROM csvgetter LIMIT 10 OFFSET 20
```

This skips the first 20 rows and returns the next 10.

#### 11. COUNT (Aggregation)

```sql
SELECT COUNT(*) as total FROM csvgetter
```

```sql
SELECT status, COUNT(*) as count FROM csvgetter GROUP BY status
```

**Sample output (as JSON):**

```json
[
  {"status": "Active", "count": 42},
  {"status": "Pending", "count": 15},
  {"status": "Closed", "count": 8}
]
```

#### 12. SUM, AVG, MIN, MAX

```sql
SELECT SUM(amount) as total_amount FROM csvgetter
```

```sql
SELECT department, AVG(salary) as avg_salary FROM csvgetter GROUP BY department
```

```sql
SELECT MIN(price) as cheapest, MAX(price) as most_expensive FROM csvgetter
```

#### 13. GROUP BY

```sql
SELECT department, COUNT(*) as headcount, AVG(salary) as avg_salary
FROM csvgetter
GROUP BY department
```

#### 14. GROUP BY with HAVING

```sql
SELECT department, COUNT(*) as headcount
FROM csvgetter
GROUP BY department
HAVING COUNT(*) > 5
```

#### 15. DISTINCT

```sql
SELECT DISTINCT department FROM csvgetter
```

```sql
SELECT DISTINCT city, state FROM csvgetter ORDER BY city
```

#### 16. NULL Handling

```sql
SELECT * FROM csvgetter WHERE phone IS NOT NULL
```

```sql
SELECT * FROM csvgetter WHERE notes IS NULL
```

#### 17. String Functions

```sql
SELECT UPPER(name) as name_upper, LOWER(email) as email_lower FROM csvgetter
```

```sql
SELECT * FROM csvgetter WHERE LENGTH(description) > 100
```

#### 18. CASE Expressions

```sql
SELECT name,
  CASE
    WHEN salary > 100000 THEN 'Senior'
    WHEN salary > 50000 THEN 'Mid'
    ELSE 'Junior'
  END as level
FROM csvgetter
```

#### 19. Column Aliases

```sql
SELECT name AS full_name, email AS contact_email FROM csvgetter
```

***

### Column Names with Spaces

If your column names contain spaces, wrap them in double quotes:

```sql
SELECT "First Name", "Last Name", "Email Address" FROM csvgetter
```

```sql
SELECT * FROM csvgetter WHERE "Job Title" LIKE '%Manager%'
```

***

### Combining SQL with Other Parameters

The `sql` parameter works alongside other URL parameters:

```
?sql=SELECT name, email FROM csvgetter WHERE status='Active'&type=json_records&email_me=true
```

Processing order:

1. Data is fetched from the source
2. `sql` query is applied
3. `type` formatting is applied
4. Side effects (`email_me`, `save_to_gdrive`, etc.) execute

***

### Error Handling

If your SQL is invalid, you'll get a **400** response:

```json
{
  "error": "Could not validate sql.",
  "data": "no such column: nonexistent_column",
  "help": "https://docs.csvgetter.com/sql-parameter"
}
```

Common errors:

| Error                    | Cause                          | Fix                                           |
| ------------------------ | ------------------------------ | --------------------------------------------- |
| `no such column: X`      | Column name doesn't exist      | Check your field names in the endpoint config |
| `no such table: X`       | Wrong table name               | Always use `FROM csvgetter`                   |
| `near "X": syntax error` | SQL syntax error               | Check your SQL syntax                         |
| `unrecognized token`     | Special characters not handled | Use URL encoding                              |

***

### Supported SQL Features (SQLite)

Since the SQL engine uses SQLite, you have access to:

* `SELECT`, `FROM`, `WHERE`, `ORDER BY`, `GROUP BY`, `HAVING`, `LIMIT`, `OFFSET`
* Aggregate functions: `COUNT`, `SUM`, `AVG`, `MIN`, `MAX`
* String functions: `UPPER`, `LOWER`, `LENGTH`, `SUBSTR`, `TRIM`, `REPLACE`
* `LIKE`, `IN`, `BETWEEN`, `IS NULL`, `IS NOT NULL`
* `CASE ... WHEN ... THEN ... ELSE ... END`
* `DISTINCT`
* `AND`, `OR`, `NOT`
* Comparison: `=`, `!=`, `<`, `>`, `<=`, `>=`
* Math: `+`, `-`, `*`, `/`, `%`, `ABS`, `ROUND`

**Not supported:**

* `JOIN` (there's only one table)
* `INSERT`, `UPDATE`, `DELETE` (read-only)
* `CREATE TABLE`, `ALTER TABLE`, `DROP` (read-only)
* Subqueries (limited support — depends on complexity)


