Join Datasets: VLOOKUP & Merge Two Datasets or CSV Files avatar

Join Datasets: VLOOKUP & Merge Two Datasets or CSV Files

Pricing

from $0.70 / 1,000 joined rows

Go to Apify Store
Join Datasets: VLOOKUP & Merge Two Datasets or CSV Files

Join Datasets: VLOOKUP & Merge Two Datasets or CSV Files

Join two Apify datasets or CSV, JSON or Excel files by a key, like VLOOKUP or a SQL join: add the fields of the matching lookup row to each row. Match on text, exact values or website domains; left, inner or anti join. Enrich scraper results with other actors' data. Pay per row.

Pricing

from $0.70 / 1,000 joined rows

Rating

0.0

(0)

Developer

Michael Costa

Michael Costa

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

15 hours ago

Last modified

Share

What does Join Datasets do?

Join Datasets joins two Apify datasets or CSV, JSON or Excel files by a key, like VLOOKUP or a SQL join: each row of your main data gets the fields of its matching row in the lookup data. Match on text, exact values or website domains; keep every row (left join), only matches (inner) or only non-matches (anti).

It's the step that puts two scrapers' results side by side: Google Maps leads plus each website's tech stack, products plus their stock from another source, companies plus the contacts you already have in your CRM export.

Input fields · API · Use it from Claude, ChatGPT or Cursor (MCP)

Jump to: Fields · Price · How to use · Input · Output · Chain it · AI agents (MCP) · Limits · FAQ

Try it in one click: the input comes pre-filled with the US Treasury's official exchange rates on 2025-12-31, joined by currency with the same table a year earlier: each of the 174 rates gets last year's rate next to it (173 matched, 1 currency was new). 174 rows, about $0.17 (174 × $0.001, plus $0.00005 for the run start). Then pick your own datasets or files.

What data does Join Datasets return?

Each main row with the lookup fields added. From the example:

FieldExampleNotes
(your main fields)"country": "Afghanistan", "exchange_rate": "65.96"The main row as it is.
rateAYearEarlier70.35A lookup field, renamed (exchange_rate -> rateAYearEarlier).
rateDateAYearEarlier2024-12-31Another lookup field, renamed.
joinMatchedtrueWhether the row found a lookup row (turn off with Add a matched true/false field).

Every row has the same columns: when there's no match, the added fields are null. A lookup field whose name the main row already has is added as lookup_<name>, so your main data is never overwritten. The run's RUN_STATS record counts the rows read, matched and not matched.

How much does it cost to join two datasets?

You pay per row written: $1.00 per 1,000 rows, plus $0.00005 each time a run starts. Matched or not, it's the same price, and the lookup rows you join with aren't charged.

It's cheaper on paid Apify plans: $0.90 per 1,000 rows on Starter, $0.80 on Scale and $0.70 on Business. The prices on this page are the Free-plan price, so on a paid plan you pay less than the examples show.

  • The example: 174 rows × $0.001 = about $0.17, plus the start fee.
  • For example: 2,000 Google Maps leads joined with their websites' tech stacks: 2,000 × $0.001 = $2.00; only the 300 leads not in your CRM yet (anti join): $0.30.
  • Caps: Max rows per run in the input, and Maximum cost per run in the run options. The run stops cleanly at whichever comes first.

Never charged: main rows an inner or anti join leaves out, lookup rows, and rows too large to write.

How to join two datasets or files

  1. Open Join Datasets and click Try for free (or Start if you're signed in).
  2. Pick the rows to enrich in Main data: Apify dataset (or paste a link in Or: main data file URL).
  3. Pick the rows with the extra fields in Lookup data (a dataset or a file link).
  4. In Join on, name the field that links them: url, or website = domain when the two call it differently. For websites, set Match values as to Website domain.
  5. Optional: choose Which rows to keep and the Lookup fields to add, then click Start and open the Output tab (export as JSON, CSV or Excel).

Example: exchange rates now and a year earlier

The pre-filled input (two public CSV files, joined by currency, two lookup fields renamed):

{"fileUrl": "https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v1/accounting/od/rates_of_exchange?format=csv&filter=record_date:eq:2025-12-31&page[size]=300",
"lookupFileUrl": "https://api.fiscaldata.treasury.gov/services/api/fiscal_service/v1/accounting/od/rates_of_exchange?format=csv&filter=record_date:eq:2024-12-31&page[size]=300",
"joinOn": ["country_currency_desc"],
"lookupFields": ["exchange_rate -> rateAYearEarlier", "record_date -> rateDateAYearEarlier"]}

Real output from a local run on 2026-10-03 (the Treasury's bookkeeping columns left out):

[
{"country": "Afghanistan", "currency": "Afghani", "exchange_rate": "65.96", "record_date": "2025-12-31",
"rateAYearEarlier": "70.35", "rateDateAYearEarlier": "2024-12-31", "joinMatched": true},
{"country": "Euro Zone", "currency": "Euro", "exchange_rate": "0.851", "record_date": "2025-12-31",
"rateAYearEarlier": "0.961", "rateDateAYearEarlier": "2024-12-31", "joinMatched": true},
{"country": "Curacao", "currency": "Caribbean Guilder", "exchange_rate": "1.78", "record_date": "2025-12-31",
"rateAYearEarlier": null, "rateDateAYearEarlier": null, "joinMatched": false}
]

Ready-to-run examples

Each example opens with the input already filled in. Run it as it is, or change the input first.

Input

FieldWhat it does
Main data: Apify datasetThe rows to add fields to; every output row is one of these. Or a file in Or: main data file URL.
Lookup data (dataset or file URL)The rows with the fields to add, like VLOOKUP's second table.
Join onfield, or main field = lookup field; several lines join on all of them (name and city). Dots reach nested fields.
Match values asText (ignores case and extra spaces; the default), Exactly as written, or Website domain.
Which rows to keepEvery main row (left join, the default), only matches (inner), only non-matches (anti).
When a key matches several lookup rowsThe first (like VLOOKUP, the default) or one row per match (like SQL).
Lookup fields to add (and rename)field or field -> new name; empty adds every lookup field.
Prefix for the added fieldsE.g. crm_ gives crm_owner.
Add a matched true/false fieldAdds joinMatched (on by default).
Max rows per runCap the rows written.

Website domain matching reads https://www.acme.com/about, acme.com, ACME.com. and info@acme.com all as acme.com, so a Maps scraper's website, a crawler's page url and a CRM's email domain line up. Text matching treats 12 and "12" as the same (a CSV and a dataset join fine), and ignores how an accent is encoded. A row with an empty key never matches.

A full example, Google Maps leads joined with Tech Stack Detector's results:

{
"datasetId": "<Google Maps scraper's dataset>",
"lookupDatasetId": "<Tech Stack Detector's dataset>",
"joinOn": ["website = url"],
"matchAs": "domain",
"lookupFields": ["technologies -> techStack", "categories -> techCategories"]
}

Output

One dataset row per main row (or per match, with one row per match): the main row's fields, then the added lookup fields, then joinMatched. The dataset can be downloaded as CSV, JSON, Excel, XML or HTML at any time, or passed on to the next actor.

Run it after a scraper, or on a schedule

To join each new scrape with a lookup dataset you keep (a CRM export, a list of target domains), chain it:

  1. Open the scraper or its saved task, go to the Integrations tab and add Apify actor → Join Datasets, triggered when a run succeeds.
  2. Set "datasetId": "{{resource.defaultDatasetId}}" (Apify fills in the finished run's dataset), your lookupDatasetId (a named dataset keeps its name between runs) or lookupFileUrl, and joinOn.

Or save the input as a task and schedule it, and collect the rows from the API, a webhook, or Make, Zapier and n8n through Apify's integrations.

Can I use Join Datasets from an AI agent (MCP)?

Yes, through Apify's MCP server, from Claude, ChatGPT, Cursor or any other MCP client. Add this to your client's MCP configuration (or let the agent find it with the server's actor search); your client signs you in to Apify:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=humble-echidna/dataset-join"
}
}
}

To use an Apify API token instead of signing in, add "headers": {"Authorization": "Bearer <APIFY_TOKEN>"} next to url.

An agent that has run two actors can pass both datasets, e.g.

{"datasetId": "<leads>", "lookupDatasetId": "<stacks>", "joinOn": ["website = url"], "matchAs": "domain"}
, and get one combined result.

Who it's for

Anyone who runs more than one scraper on Apify and needs the results in one table: lead lists enriched with technology, contact or company data; product lists with prices from two shops; a fresh scrape checked against the records you already have (anti join).

Why this one?

  • VLOOKUP for datasets, without a spreadsheet. Datasets and CSV/TSV, JSON, JSON Lines and Excel files, in any combination, with nested fields and multi-field keys.
  • Website domains match out of the box. URLs, bare domains and email addresses line up by domain, the usual key between lead scrapers and website tools.
  • Never overwrites your data. Clashing names are added as lookup_<name>; every row has the same columns.
  • One flat price, no usage bill: $1.00 per 1,000 rows ($0.90 on Starter), matched or not, about half the price of the alternative for joined rows. Lookup rows are free.
  • Fails fast on a typo. If no row has the join field, the run stops before writing (and charging) anything and lists the fields the first row has.
  • Polite and safe with files. It identifies itself honestly (User-Agent HumbleEchidnaApify), follows each site's robots.txt, and only requests public web addresses on the standard ports (80 and 443).

Limits

  • Lookup data: up to 200,000 rows, held in memory (at most half of the run's memory; the status says to give the run more memory, keep fewer lookup fields or filter first). Main data: up to 1,000,000 rows, read as a stream.
  • Files: up to 200 MB (after decompression), public web addresses on ports 80 and 443; Excel's first sheet only.
  • A single output row can be up to 5 MB; larger ones are skipped and counted.

FAQ

Which lookup row is used when several have the same key?

The first one in the lookup data's order, as VLOOKUP does. Choose one row per match to get them all.

Why didn't my rows match?

Check that Join on names each side's field as written (case-sensitive; dots for nested fields). Values that differ in more than case and spaces don't match under Text: a URL and a domain need Website domain. The status counts the main rows without a key value.

Can I find rows that are NOT in another dataset?

Yes: Which rows to keep → only main rows without a match (anti join), e.g. leads not yet in your CRM export.

It only processes data you choose: your own Apify datasets, and files at addresses you supply, fetched politely (robots.txt honoured, honest User-Agent). Make sure you're allowed to use the files you point it at. The example data is the US Treasury's Fiscal Data API, which is "offered free, without restriction" for commercial and non-commercial use.

Something that used to work now fails. Why?

The run log and the status say what went wrong. Please open an issue with the input you used.

ActorUse it when
Dataset TransformerYou want to filter, dedupe or reshape either side before joining, or the result after, or save it as CSV.
Dataset DiffYou want what changed between two runs of the same data, not two different datasets combined.
Tech Stack DetectorYou need each lead's website technologies to join back onto your leads (it reads their dataset too).

Feedback and support

Found a bug, or need a join that isn't here? Open an issue on the Issues tab with the input you used.

Versions

Current version: 0.1. See the Changelog tab for what changed in each version.