How to get any kind of data dynamically in Google sheet without specifying column names in Google sheet in advance?

I have different types of CSV with different column names that I am uploading via webhook into my n8n workflow, and than I use the “Extract from CSV” node to get that data into JSON format, now I’m confused how I can put this data into my Google sheet without specifying column names. I want the Google sheet to automatically detect the number of columns and column names, is that possible?

Hey Abdullah! :waving_hand:

This is a very common challenge. The standard Append Row operation in the Google Sheets node (using “Auto-map”) actually requires the headers to
already exist in Row 1 of your sheet so it knows which column corresponds to which JSON key. It cannot magically create headers on a completely blank
existing sheet using the standard append node.

However, there are two elegant ways to solve this:

Option 1: Dynamically Create a NEW Sheet (Easiest)

Instead of appending to an existing blank sheet, use the Google Sheets node and set:

• Resource: Sheet
• Operation: Create
If you pass your JSON data into this node, n8n will automatically create a brand new Tab (Sheet) in your Google Document. It will intelligently read
all keys from your JSON, automatically write them as Headers in Row 1, and insert all your CSV data below it. Perfect for dynamic CSVs!

Option 2: The Google Sheets API (For existing blank sheets)

If you must push the data into a specific, already existing blank sheet, the native n8n node will struggle. You will have to use an HTTP Request Node
to talk to the Google Sheets API directly ( values:append endpoint).
You would use a Code Node first to convert your JSON array into a 2D Array (an array of arrays), where the first array contains your extracted
Object.keys() . Then you send this raw 2D array via the HTTP Request node to Google.

I highly recommend going with Option 1 if your workflow allows it. Just create a new sheet dynamically based on the CSV filename or current date!

Hope this helps you out :smiley:

@Abdullah_Shah2

My question for you. So you have different csv with different columns and you’re trying to upsert to a single sheet? A sheet is a basic database and the header row is the schema, in order to upsert you have to match on those columns.

So my first question, is the data the same type of data but with different headers? (ie. EMAIL, email_address, E-Mail) or are they genuinely different datasets?

A little more detail will go a long way here

Michael’s question is the key one. A sheet can only have one header row, so “dynamic columns into one existing sheet” is a schema problem, not a node problem — no node setting fixes it.

Two honest paths: if each CSV is genuinely a different dataset, yoel’s option 1 (create a new tab per file) is correct. If it’s the same data with messy header names (email vs E-Mail vs email_address), normalize the keys in a small Code node first, write your canonical headers into row 1 once by hand, and from then on Append with auto-map just works — it ignores extra keys and fills what matches. That second setup survives new CSV variants without touching the sheet again.

Hi @Abdullah_Shah2 ,

Yes, this is completely possible!

By default, the Google Sheets node expects Row 1 of your sheet to already contain pre-defined header names. Since your CSVs have dynamic/varying column names, the trick is to use a Code node to dynamically extract the headers from the CSV data and format everything into a 2D Array (where Row 1 = Dynamic Headers, and Rows 2+ = Values).

Here is the exact 3-step workflow setup:

Step 1: Extract from CSV Node

Keep your existing Extract from CSV node as is.

Step 2: Add a Code Node (Format for Google Sheets)

Connect a Code node (Mode: Run Once for All Items) right after your Extract from CSV node and paste this JavaScript code:

const items = $input.all();
if (items.length === 0) return [];

const headers = Object.keys(items[0].json);

const values = [headers];

for (const item of items) {
  const row = headers.map(header => item.json[header] ?? '');
  values.push(row);
}

return [{ json: { values } }];

Step 3: Google Sheets Node Setup

In your Google Sheets node, configure it like this:

  • Resource: Sheet (or Cell)
  • Operation: Update (or Clear then Update if you want to overwrite previous data)
  • Data Mode: Auto-Map or Define Below
  • Range: A1
  • Fields / Values: Map {{ $json.values }}

How this works:

  1. Object.keys() automatically reads whatever column names exist in your CSV payload.
  2. It writes those header names directly into Row 1 (A1, B1, C1…) in Google Sheets automatically.
  3. It fills all the data rows right below the dynamic headers.

Let me know if you run into any issues setting this up!

Thanks!