Enrich Google Sheets with DataLedger using N8N - automatically

Add a company number to your sheet and let N8N fill the rest: name, local authority, employees, assets, liabilities, equity, and SIC. No PDFs. No manual copy-paste.

Intro

What you'll learn (5 minutes setup, ~10 minutes mapping):

  • Trigger an N8N workflow when a company_number is added in Google Sheets

  • Call the DataLedger API securely with your API key

  • Update the same row with profile + balance-sheet fields

  • Add a status column to make troubleshooting easy

What you need:

  • Google account + a simple Google Sheet

  • N8N account (cloud or self-hosted)

  • DataLedger API key (from your account area)

Set up your Google Sheet

Option A (quick start):
👉 Download the Google Sheets template (preloaded with all DataLedger fields)

This template includes every available column (profile, officers, SICs, balance sheet, profit & loss, ratios). Perfect if you want the full dataset ready to go.

Option B (custom setup):
If you’d rather start small, create a sheet with these minimum columns:

  • company_number (your input column)

  • dl_status (optional, to track success/errors)

  • A few target fields such as:

    • company_name

    • local_authority

    • sic1_description

    • c_calculated_total_assets

    • c_calculated_total_liabilities

    • c_calculated_equity

You can add more columns later as you need them.

Create the N8N workflow trigger

In N8N, create a new workflow and add:

Node: Google Sheets Trigger

  • Operation: On Row Added/Updated

  • Credential: Connect your Google account

  • Document: Choose your spreadsheet

  • Sheet: Choose your worksheet

  • Trigger Column: company_number

Tip: Add an IF node after the trigger — continue only if company_number is not empty.

Add a filter to prevent a loop

⚙️ Important filter: Add a FILTER node straight after the trigger:

Condition: {{ $json.dl_status }} is empty/undefined

This means the workflow will only fire on fresh company numbers. Once the row has been updated and dl_status is set to "fetched" (or "error"), it won't run again — avoiding an endless loop.

Call the DataLedger API (Webhooks)

Node: HTTP Request

  • Method: GET

  • URL: https://api.dataledger.uk/v1/companies/{{ $json.company_number }}

  • Headers:

    • Key: x-api-key

    • Value: YOUR_API_KEY

Authentication: None (we use the header instead)

cURL reference:

curl -X GET \ "https://api.dataledger.uk/v1/companies/12345678" \ -H "x-api-key: YOUR_API_KEY"

Common mistake to avoid: Don't append the entire row as query params. Only pass company_number in the URL path.

Map API response fields to your row

Now we tell N8N how to update the same row that triggered the workflow:

Node: Google Sheets → Update

  • Operation: Append or Update Row in Sheet

  • Document: Same spreadsheet as trigger

  • Sheet: Same worksheet as trigger

  • Row Number: {{ $('Google Sheets Trigger').item.json["__rowNumber"] }}

Field mapping: For each column in your sheet, select the matching property from the HTTP Request response.

Example mappings:

  • company_name{{ $json.companyName }}

  • local_authority{{ $json.registeredAddress.localAuthority }}

  • c_calculated_total_assets{{ $json.financials.cCalculatedTotalAssets }}

  • c_calculated_total_liabilities{{ $json.financials.cCalculatedTotalLiabilities }}

  • c_calculated_equity{{ $json.financials.cCalculatedEquity }}

  • dl_status: Set this manually to "fetched"

    This is essential — it marks the row as complete so the workflow won't keep re-triggering when the row changes.

Test end-to-end

Enter a real company_number in the next empty row

  1. Watch the workflow run (N8N Executions tab)

  2. Confirm the row fills with data; dl_status = "fetched"

Troubleshooting

403 Forbidden

  • Check that x-api-key is added as a header in the HTTP Request node

  • Ensure only company_number is added to the URL path

  • Verify you have DataLedger credits available

Row didn't update

  • Confirm Row Number from the trigger is mapped correctly in Update node

  • Use {{ $('Google Sheets Trigger').item.json["__rowNumber"] }}

  • Verify JSON keys exist in the HTTP Request test output

  • Re-test the HTTP Request node and remap any changed keys

Workflow keeps running in a loop

  • Make sure you have a dl_status column

  • Add an IF node → only continue if dl_status is empty

  • When updating the row, always set dl_status to "fetched" (or "error" if the API fails)

Data comes back blank or unexpected

  • Double-check the company number is valid and active

  • Some fields may not exist for smaller companies (e.g. turnover, P&L)

  • Use c_ for current year values, p_ for previous year

  • If a field is missing, the API will return null

Workflow runs but Google Sheets isn't updating

  • Ensure you've selected the correct Document and Sheet in the update node

  • Check that column headers in your sheet exactly match what you're mapping

  • Avoid hidden rows or merged cells — they can break the row mapping

N8N-specific tips

Expression editor

Use N8N's expression editor for dynamic values:

  • {{ $json.company_number }} - gets company number from current node

  • {{ $('HTTP Request').item.json.companyName }} - gets data from specific node

  • {{ $('Google Sheets Trigger').item.json["__rowNumber"] }} - gets triggered row number

Error handling

Add an IF node after the HTTP Request to handle API errors:

  • Condition: {{ $json.error }} exists

  • True branch: Update dl_status to "error"

  • False branch: Continue with normal field mapping

Multiple sheets

If you have multiple sheets, create separate workflows or use a Switch node to route based on sheet name.

Frequently Asked Questions

What does the dl_status column do?

It prevents loops. When you first add a company number, dl_status is empty, so the workflow runs. When updating, N8N sets dl_status to "fetched". That stops it re-triggering when the row changes.

Can I enrich multiple company numbers at once?

Yes. Paste or import several company numbers into the sheet. N8N will process each new row individually. For bulk jobs, add a Wait node or use N8N's built-in rate limiting.

Which DataLedger fields can I get in Google Sheets?

All major profile, balance sheet, and P&L fields are available, including:

  • Company name, address, SIC codes, officers

  • Assets, liabilities, equity (current and previous years)

  • Turnover and profit/loss (where disclosed)

  • Growth rates and ratios (debt-to-asset, debt-to-equity)

Download the template sheet to see the full column list.

Why are some values empty in the returned row?

Some companies file abbreviated accounts. Smaller companies often don't disclose revenue, profit, or detailed breakdowns. In those cases, the API returns null.

Can I filter for companies with specific assets or liabilities directly in N8N?

N8N just handles the enrichment. To filter companies by assets/liabilities, use DataLedger's Chat or API search endpoints to get the relevant company numbers, then push results into Sheets.

Is my API key safe in N8N?

Yes — keep it in the x-api-key header. Never paste it into the sheet or URL. N8N encrypts credentials. If a key leaks, contact us and we will reset it for you.

How often is the data updated?

DataLedger syncs with Companies House the day after the accounts are submitted. Balance sheet and P&L fields update as soon as filings are available.

Can I use this workflow for lead generation?

Yes. Many users enrich lists of company numbers (from LinkedIn, CRM exports, or other sources) to pull structured financials and employee counts directly into Sheets.

N8N Cloud vs Self-hosted?

Both work identically. N8N Cloud is easier to set up, while self-hosted gives you more control. The workflow JSON is compatible between both.