Dataset to Postgres, Supabase & MySQL (Database Push) avatar

Dataset to Postgres, Supabase & MySQL (Database Push)

Pricing

from $1.40 / 1,000 row writtens

Go to Apify Store
Dataset to Postgres, Supabase & MySQL (Database Push)

Dataset to Postgres, Supabase & MySQL (Database Push)

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.

Pricing

from $1.40 / 1,000 row writtens

Rating

0.0

(0)

Developer

Adam Pearce

Adam Pearce

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

an hour ago

Last modified

Share

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:

{
"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):

{
"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: dedupe (exact, normalized or fuzzy), flatten nested JSON, clean emails, phones and URLs, then export CSV or Excel.
  • Dataset Filter & Transform: keep the rows you want and reshape the fields (dates, replace, split, hash, 25 ops), sort, dedupe, limit.
  • Dataset Join & Merge: VLOOKUP-style joins and unions across two datasets, files or Google Sheets on a key field.
  • Dataset Aggregate, Group By & Pivot: counts, sums, averages and pivot tables per group.
  • Dataset Diff & Change Detector: what was added, removed or changed since last time.
  • Dataset AI Enrich: add LLM-generated columns (classify, extract, summarise) to every row, no API key needed.
  • 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: send every row to any API as its own request, with templating and auth presets.
  • 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.