# Dataset to Postgres, Supabase & MySQL (Database Push) (`nerolabs/dataset-to-database`) Actor

Pushes any Apify dataset, file or Google Sheet by URL, or JSON array into a Postgres, Supabase, Neon or MySQL table: creates the table with typed columns, adds missing ones, inserts, upserts by key or replaces; dry run previews the SQL. Agent-ready: pay per event (x402, MCP), per row.

- **URL**: https://apify.com/nerolabs/dataset-to-database.md
- **Developed by:** [Adam Pearce](https://apify.com/nerolabs) (community)
- **Categories:** Developer tools, Integrations, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.40 / 1,000 row writtens

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

**Push any Apify dataset into your own Postgres, Supabase, Neon or MySQL table, without writing a sync script.** Point it at a dataset (any scraper run's output), a CSV, Excel or JSON file or Google Sheet by URL, or a JSON array you paste in, give it a connection string and a table name, and it creates the table with sensible column types, adds columns as your data grows, and inserts, upserts on a key, or replaces the rows, in transactional batches you can see and audit. A dry run shows you the exact SQL first.

### Why use Dataset to Database?

Scraped data is only useful once it is where your application, dashboard or team can query it. Apify's own integrations cover Google Sheets and a few SaaS tools; your own database is where a script usually gets written, then forgotten, then breaks when a field is added.

- **Feed an app**: a nightly scrape lands in the Supabase or Neon table your product reads from, one row per key, updated in place.
- **Build a warehouse table**: append every run's rows to a Postgres table and point your BI tool at it.
- **Keep a lookup table current**: replace mode rebuilds a reference table from the latest file, safely (an empty scrape never wipes it).
- **Skip the schema work**: the table and its column types are inferred from the data (numbers, booleans, timestamps, JSON), and new fields become new columns automatically.
- **Chain it after any Actor**: add an integration on your scraper that starts this Actor with the run's dataset ID, or put it on a schedule, and the table stays fresh on its own.

No scraping involved at all, it only moves data you already have into a database you already own.

### How to use it

1. Get a connection string from your database: Supabase (Project Settings > Database > Connection string, the pooler works), Neon (the pooled string), RDS, Railway, Render, PlanetScale or any Postgres 12+ / MySQL 8+ that accepts connections from the internet.
2. Pick your rows: an existing Apify dataset in **Dataset to push**, a link in **File URL** (CSV, TSV, Excel, JSON, JSON Lines, or a Google Sheet), or rows in **Data (inline)**.
3. Set **Table name** (for example `leads` or `public.leads`) and a **Write mode**: append, upsert (with **Key field(s)** such as `email` or `sku`), or replace.
4. Leave **Dry run** on for the first run: it connects, checks the table, and reports the exact CREATE TABLE, ALTER TABLE and INSERT statements it would execute, without writing or charging anything.
5. Turn **Dry run** off and run it. The output is a one-row summary: rows written, table created, columns added, warnings.

### What it does with your data

- **Table creation**: one column per field, typed from the data. Postgres: BIGINT for whole numbers, DOUBLE PRECISION for decimals, BOOLEAN, TIMESTAMPTZ for ISO date-times, DATE, JSONB for objects and arrays, TEXT for everything else. MySQL: BIGINT, DOUBLE, TINYINT(1), DATETIME, DATE, JSON, TEXT (key columns VARCHAR(255) so they can be indexed). A column whose values are mixed (a number in one row, text in another) becomes TEXT rather than a guess.
- **Column names**: `snake_case` by default (`Company Name` becomes `company_name`, `firstName` becomes `first_name`), or the original names, quoted. Nested objects are flattened (`address.city` becomes `address_city`); arrays stay as JSON.
- **Existing tables**: the Actor matches your fields to the table's columns (case-insensitive), adds missing columns with ALTER TABLE, and writes only those columns. Turn **Add missing columns** off to write only what the table already has; dropped fields are listed in the warnings.
- **Upsert**: `INSERT ... ON CONFLICT (keys) DO UPDATE` on Postgres, `ON DUPLICATE KEY UPDATE` on MySQL. A new table gets a UNIQUE constraint on the key columns; an existing table without one gets a unique index created for it (if that fails because the table already holds duplicates, the run stops with a clear message). Rows whose key is empty are skipped and counted.
- **Replace**: deletes every row, then inserts, except when the input has 0 rows, so a failed scrape cannot empty your table.
- **Batches and billing**: rows go in batches of up to 500 per INSERT (fewer for very wide tables). Each batch is one transaction: insert, charge, commit. A batch your budget cannot cover is rolled back, so nothing is ever written unpaid or charged unwritten. If a batch fails on the database side, earlier committed batches stay and the run stops with the row range and the database's own message.
- **Optional sync columns**: `_synced_at` (UTC) and `_apify_run_id` on every written row.

### Input

- **Dataset to push / File URL / Data (inline)**: where the rows come from (dataset first, then file, then inline, if several are set).
- **Database type**: Postgres (Supabase, Neon, RDS, Railway, self-hosted) or MySQL / MariaDB (MySQL 8, MariaDB 10.5+, PlanetScale, RDS).
- **Connection string**: stored encrypted by Apify, never written to the log or the output. Percent-encode special characters in the password (`@` becomes `%40`).
- **SSL**: Auto (encrypt unless localhost, accept the provider's certificate) suits Supabase, Neon, RDS and PlanetScale. Verify full also checks the certificate. Disable is for private networks.
- **Table name**, **Write mode**, **Key field(s)**, **Create table if missing**, **Add missing columns**, **Column naming**, **Flatten nested objects**, **Add sync columns**, **Rows per batch**, **Dry run**, **Maximum input rows**, **Webhook URL**.

A typical scheduled sync:

```json
{
  "datasetId": "<the scraper run's dataset, or {{resource.defaultDatasetId}} from an integration>",
  "databaseType": "postgres",
  "connectionString": "postgres://postgres.abcd:<password>@aws-0-eu-west-1.pooler.supabase.com:6543/postgres",
  "tableName": "public.listings",
  "writeMode": "upsert",
  "keyFields": ["url"],
  "addSyncColumns": true
}
```

### Output

One summary row per run in the dataset (and as `PUSH_SUMMARY` in the key-value store):

```json
{
  "table": "\"public\".\"listings\"",
  "databaseType": "postgres",
  "mode": "upsert",
  "dryRun": false,
  "inputRecordCount": 1240,
  "rowsWritten": 1240,
  "rowsNotWritten": 0,
  "batchesCompleted": 3,
  "tableCreated": false,
  "columnsAdded": ["seller_rating"],
  "uniqueIndexCreated": false,
  "columns": [{ "name": "url", "type": "text", "sourceField": "url", "isKey": true }],
  "durationMs": 2140,
  "warnings": []
}
```

A dry run adds a `plan` object with the exact `createTableSql`, `alterTableSql`, `uniqueIndexSql` and a sample `insertSqlSample`, plus which columns already exist and which would be added.

### Webhook destination

Set **Webhook URL** and the run summary (never the rows, those are in your database now) is POSTed there as JSON the moment the push finishes, so a scheduled sync can tell Slack, Zapier, Make, n8n or your own API that the table is up to date. A failed or unreachable webhook never breaks the run, it's reported as a warning in the output and costs nothing. Charged only on a confirmed delivery (see Pricing).

### Pricing

Pay-per-event, anchored the same way as the rest of the Nero Labs dataset toolkit:

- **$0.002 per row written** (inserted or updated, inside a committed batch). Rows in a rolled-back batch, rows skipped for an empty key, and dry runs are never charged.
- **$0.02 per confirmed webhook delivery** (only when your endpoint responds 2xx; a failed delivery costs nothing).
- A small per-GB run-start fee (the platform default).
- Apify Store discounts apply from day one: 10% off for Bronze, 20% for Silver and 30% for Gold accounts.

Syncing a 1,000-row scrape into Supabase costs **$2.00**; a nightly 300-row upsert is **$0.60 a night**, about $18 a month. Connecting, creating the table, adding columns and downloading a file by URL are free.

### Tips

- Run with **Dry run** on first. It shows the inferred column types before anything is created, which is the moment to fix a field that should be a number but arrives as text (Dataset Filter & Transform can cast it on the way in).
- For a scheduled scraper, use **upsert** with the field that identifies a listing, product or contact across runs (`url`, `sku`, `email`). Append is for logs and history tables.
- Supabase: use the pooler connection string (port 6543 or 5432, both work) and leave SSL on Auto. Neon: the pooled string, SSL Auto. Amazon RDS: make sure the security group allows inbound connections from the internet, since Apify runs from changing IPs.
- Your database's own timestamp columns are filled from ISO 8601 strings (`2026-09-06T01:00:00Z`); other date formats arrive as text. Normalise them first with Dataset Filter & Transform's `dateFormat` if you want a real TIMESTAMPTZ column.

### Works with the rest of the Nero Labs dataset toolkit

- [Dataset Cleaner & Exporter](https://apify.com/nerolabs/dataset-cleaner-exporter): dedupe (exact, normalized or fuzzy), flatten nested JSON, clean emails, phones and URLs, then export CSV or Excel.
- [Dataset Filter & Transform](https://apify.com/nerolabs/dataset-filter-transform): keep the rows you want and reshape the fields (dates, replace, split, hash, 25 ops), sort, dedupe, limit.
- [Dataset Join & Merge](https://apify.com/nerolabs/dataset-join-merge): VLOOKUP-style joins and unions across two datasets, files or Google Sheets on a key field.
- [Dataset Aggregate, Group By & Pivot](https://apify.com/nerolabs/dataset-aggregate-pivot): counts, sums, averages and pivot tables per group.
- [Dataset Diff & Change Detector](https://apify.com/nerolabs/dataset-diff-detector): what was added, removed or changed since last time.
- [Dataset AI Enrich](https://apify.com/nerolabs/dataset-ai-enrich): add LLM-generated columns (classify, extract, summarise) to every row, no API key needed.
- [Dataset Charts & Report](https://apify.com/nerolabs/dataset-charts-report): chart images (PNG, SVG) and a PDF or HTML report from any data.
- **Dataset to Postgres, Supabase & MySQL** (this one): write the rows straight into a database table, creating it if needed.
- [Dataset to REST API](https://apify.com/nerolabs/dataset-to-rest-api): send every row to any API as its own request, with templating and auth presets.
- [Actor Pipeline Runner](https://apify.com/nerolabs/actor-pipeline-runner): chain several of these together in one run, each step fed the previous step's dataset.

A common pipeline: a scraper, then Cleaner, then Filter & Transform, then Join to enrich from a sheet, then Aggregate for the weekly summary, with Diff watching what changed and Charts & Report turning the numbers into the Monday PDF. Pipeline Runner runs that whole chain in one call.

### FAQ

**Which databases work?** Any Postgres 12 or newer (Supabase, Neon, Amazon RDS, Railway, Render, Heroku, a self-hosted server) and MySQL 8 / MariaDB 10.5 or newer (PlanetScale, RDS, self-hosted), as long as the database accepts connections from the internet. Apify runs from changing cloud IPs, so allow-list `0.0.0.0/0` or use your provider's pooler. SQLite, BigQuery, Snowflake and MongoDB are not supported.

**Is my connection string safe?** It is stored encrypted by Apify as a secret input field, decrypted only inside the run, and never written to the log, the dataset or the key-value store. This Actor runs with limited permissions and can read only the dataset you point it at.

**What happens if the table already exists with different column types?** The Actor writes into the columns it finds. If a value cannot be stored in an existing column's type, that batch is rolled back and the run stops with the database's own error and the row range, so nothing half-written is left behind. Use a dry run to see the inferred types first.

**Does upsert need a primary key?** It needs a unique index or constraint on the key columns. A table this Actor creates has one; for an existing table without one, the Actor creates a unique index (and tells you if it cannot, usually because of duplicate rows).

**Can I run this on a schedule after another Actor?** Yes, that is the intended pattern: an integration on the scraper that starts this Actor with `{{resource.defaultDatasetId}}` as the dataset, or a schedule pointed at a named dataset that the earlier steps append to.

**Will it invent or guess data?** No. Types are inferred only when every value agrees; anything mixed becomes TEXT. Unreadable cells become NULL, never a made-up value.

If this saved you a sync script or a manual import, a review on the Store page helps a lot. Found a bug or want a feature? Use the Issues tab, replies come from a real person, usually within hours.

# Actor input Schema

## `datasetId` (type: `string`):

Pick an existing Apify dataset (for example the output of any scraper run). Use this OR 'File URL' OR 'Data (inline)' below. Declaring it this way is what lets this Actor run with limited permissions: it may read the dataset you point at, and nothing else on your account.

## `fileUrl` (type: `string`):

Instead of a dataset, download the rows from a public link: a CSV or TSV file, an Excel .xlsx file (first sheet, header row), a JSON array or JSON Lines file, or a Google Sheet (paste the normal sheet link, sharing set to 'Anyone with the link can view'). The format is detected automatically. Up to 100 MB per run. The download is never charged; only rows written are.

## `data` (type: `array`):

A JSON array of records to push, for ad-hoc data instead of a dataset ID or file URL.

## `fileFormat` (type: `string`):

Only needed if automatic detection gets the file URL's format wrong.

## `databaseType` (type: `string`):

Postgres covers Supabase, Neon, Amazon RDS for Postgres, Railway, Render and any self-hosted Postgres 12 or newer. MySQL covers MySQL 8, MariaDB 10.5 or newer, PlanetScale and Amazon RDS for MySQL.

## `connectionString` (type: `string`):

postgres://user:password@host:5432/database or mysql://user:password@host:3306/database. Supabase: Project Settings > Database > Connection string (the session or transaction pooler both work). Neon: the pooled connection string. Stored encrypted by Apify and never written to the log or the output. If the password contains special characters, percent-encode them (an @ becomes %40). Leave empty together with 'Dry run' to preview the table plan without connecting.

## `sslMode` (type: `string`):

'Auto' encrypts the connection to any host that is not localhost and accepts the provider's certificate chain, which is what Supabase, Neon, RDS and PlanetScale need. 'Verify full' also checks the certificate against public roots. 'Disable' sends plain TCP (local or private networks only).

## `tableName` (type: `string`):

The table to write to, for example 'leads' or 'public.leads' (schema.table). Created on the first run when 'Create table if missing' is on, with column types inferred from the data.

## `writeMode` (type: `string`):

'Append' inserts every row. 'Upsert' inserts new rows and updates existing ones matched on the key field(s), so a scheduled scraper keeps one row per key (a unique index on the key columns is created if the table has none). 'Replace' deletes every existing row and then inserts, except when the input has 0 rows, so an empty scrape can never wipe your table.

## `keyFields` (type: `array`):

Required for upsert: the field name(s) that identify a row across runs, for example 'email', 'sku', 'url', or several together. Rows with an empty key are skipped and reported. Nested fields are written with '\_' (address.city becomes address\_city).

## `createTableIfMissing` (type: `boolean`):

Create the table on the first run, with one column per field and a type inferred from the data: BIGINT for whole numbers, DOUBLE PRECISION for decimals, BOOLEAN, TIMESTAMPTZ for ISO date-times, DATE, JSONB for objects and arrays, TEXT for everything else (MySQL: BIGINT, DOUBLE, TINYINT(1), DATETIME, DATE, JSON, TEXT). Mixed values fall back to TEXT, never a guess.

## `addMissingColumns` (type: `boolean`):

When the table already exists, add a column for any field it does not have yet (ALTER TABLE ADD COLUMN). Turn off to write only the columns the table already has; extra fields are then dropped and listed in the run's warnings.

## `columnNaming` (type: `string`):

'snake\_case' turns 'Company Name' into company\_name and 'firstName' into first\_name, which is what SQL users expect. 'Original' keeps the field names as they are, quoted.

## `flattenNested` (type: `boolean`):

address: {city: 'Lisbon'} becomes a column address\_city. Arrays and objects that are not flattened are stored as JSON. Turn off to store every nested object as one JSON column.

## `addSyncColumns` (type: `boolean`):

Stamp every written row with the run's UTC time and the Apify run ID, handy for 'what did the last sync touch' queries.

## `batchSize` (type: `integer`):

Rows per INSERT statement and per transaction. Each batch is written, charged and committed together, so a batch the budget cannot cover is rolled back. Reduced automatically when a table has many columns.

## `dryRun` (type: `boolean`):

On: nothing is written and no row is charged; the output holds the inferred column types and the SQL that a real run would execute. Works without a connection string too (the table is then assumed to be missing). Turn off to write.

## `maxItems` (type: `integer`):

Stop loading after this many rows (a cost guard for large inputs). There is a hard safety ceiling of 200,000 rows per run regardless.

## `webhookUrl` (type: `string`):

Optional. If set, the run summary (table, rows written, columns added, warnings; never the rows themselves) is POSTed here as JSON the moment the push finishes, so a scheduled sync can tell Slack, Zapier, Make, n8n or your own API that the table is up to date. Only charged when the endpoint confirms receipt (HTTP 2xx); a failed delivery is reported as a warning and costs nothing.

## Actor input object example

```json
{
  "data": [
    {
      "email": "ana@example.com",
      "name": "Ana Silva",
      "company": "Silva Ltd",
      "plan": "Pro",
      "mrr": 49,
      "active": true,
      "signedUpAt": "2026-08-01T09:30:00Z",
      "address": {
        "city": "Lisbon",
        "country": "PT"
      },
      "tags": [
        "b2b",
        "eu"
      ]
    },
    {
      "email": "ben@example.com",
      "name": "Ben Okafor",
      "company": "Okafor & Co",
      "plan": "Starter",
      "mrr": 19,
      "active": true,
      "signedUpAt": "2026-08-14T15:05:00Z",
      "address": {
        "city": "Lagos",
        "country": "NG"
      },
      "tags": [
        "b2b"
      ]
    },
    {
      "email": "cara@example.com",
      "name": "Cara Lind",
      "company": "Lind Studio",
      "plan": "Free",
      "mrr": 0,
      "active": false,
      "signedUpAt": "2026-09-02T11:00:00Z",
      "address": {
        "city": "Malmö",
        "country": "SE"
      },
      "tags": []
    }
  ],
  "fileFormat": "auto",
  "databaseType": "postgres",
  "sslMode": "auto",
  "tableName": "apify_leads",
  "writeMode": "upsert",
  "keyFields": [
    "email"
  ],
  "createTableIfMissing": true,
  "addMissingColumns": true,
  "columnNaming": "snake_case",
  "flattenNested": true,
  "addSyncColumns": false,
  "batchSize": 500,
  "dryRun": true
}
```

# Actor output Schema

## `pushSummary` (type: `string`):

Table, mode, rows written, columns created or added, and any warnings from this run (plus the planned SQL in a dry run).

## `summaryRow` (type: `string`):

The same summary as a dataset item, for integrations that read datasets.

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "data": [
        {
            "email": "ana@example.com",
            "name": "Ana Silva",
            "company": "Silva Ltd",
            "plan": "Pro",
            "mrr": 49,
            "active": true,
            "signedUpAt": "2026-08-01T09:30:00Z",
            "address": {
                "city": "Lisbon",
                "country": "PT"
            },
            "tags": [
                "b2b",
                "eu"
            ]
        },
        {
            "email": "ben@example.com",
            "name": "Ben Okafor",
            "company": "Okafor & Co",
            "plan": "Starter",
            "mrr": 19,
            "active": true,
            "signedUpAt": "2026-08-14T15:05:00Z",
            "address": {
                "city": "Lagos",
                "country": "NG"
            },
            "tags": [
                "b2b"
            ]
        },
        {
            "email": "cara@example.com",
            "name": "Cara Lind",
            "company": "Lind Studio",
            "plan": "Free",
            "mrr": 0,
            "active": false,
            "signedUpAt": "2026-09-02T11:00:00Z",
            "address": {
                "city": "Malmö",
                "country": "SE"
            },
            "tags": []
        }
    ],
    "tableName": "apify_leads",
    "writeMode": "upsert",
    "keyFields": [
        "email"
    ],
    "dryRun": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("nerolabs/dataset-to-database").call(input);

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

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

```

## Python example

```python
from apify_client import ApifyClient

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

# Prepare the Actor input
run_input = {
    "data": [
        {
            "email": "ana@example.com",
            "name": "Ana Silva",
            "company": "Silva Ltd",
            "plan": "Pro",
            "mrr": 49,
            "active": True,
            "signedUpAt": "2026-08-01T09:30:00Z",
            "address": {
                "city": "Lisbon",
                "country": "PT",
            },
            "tags": [
                "b2b",
                "eu",
            ],
        },
        {
            "email": "ben@example.com",
            "name": "Ben Okafor",
            "company": "Okafor & Co",
            "plan": "Starter",
            "mrr": 19,
            "active": True,
            "signedUpAt": "2026-08-14T15:05:00Z",
            "address": {
                "city": "Lagos",
                "country": "NG",
            },
            "tags": ["b2b"],
        },
        {
            "email": "cara@example.com",
            "name": "Cara Lind",
            "company": "Lind Studio",
            "plan": "Free",
            "mrr": 0,
            "active": False,
            "signedUpAt": "2026-09-02T11:00:00Z",
            "address": {
                "city": "Malmö",
                "country": "SE",
            },
            "tags": [],
        },
    ],
    "tableName": "apify_leads",
    "writeMode": "upsert",
    "keyFields": ["email"],
    "dryRun": True,
}

# Run the Actor and wait for it to finish
run = client.actor("nerolabs/dataset-to-database").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "data": [
    {
      "email": "ana@example.com",
      "name": "Ana Silva",
      "company": "Silva Ltd",
      "plan": "Pro",
      "mrr": 49,
      "active": true,
      "signedUpAt": "2026-08-01T09:30:00Z",
      "address": {
        "city": "Lisbon",
        "country": "PT"
      },
      "tags": [
        "b2b",
        "eu"
      ]
    },
    {
      "email": "ben@example.com",
      "name": "Ben Okafor",
      "company": "Okafor & Co",
      "plan": "Starter",
      "mrr": 19,
      "active": true,
      "signedUpAt": "2026-08-14T15:05:00Z",
      "address": {
        "city": "Lagos",
        "country": "NG"
      },
      "tags": [
        "b2b"
      ]
    },
    {
      "email": "cara@example.com",
      "name": "Cara Lind",
      "company": "Lind Studio",
      "plan": "Free",
      "mrr": 0,
      "active": false,
      "signedUpAt": "2026-09-02T11:00:00Z",
      "address": {
        "city": "Malmö",
        "country": "SE"
      },
      "tags": []
    }
  ],
  "tableName": "apify_leads",
  "writeMode": "upsert",
  "keyFields": [
    "email"
  ],
  "dryRun": true
}' |
apify call nerolabs/dataset-to-database --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nerolabs/dataset-to-database"
        }
    }
}

```

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

## OpenAPI specification

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