AutomationFlowsWeb Scraping › Find High-value Expired Domains with Domainkits and Moz and Email a CSV Report

Find High-value Expired Domains with Domainkits and Moz and Email a CSV Report

ByABTdomain @lyalphaniub on n8n.io

This workflow runs daily to find expired domain candidates in DomainKits based on a keyword, enriches them with SEO metrics from the Moz Links API, then compiles the results into a CSV report and emails it via SMTP. Runs every day at 2pm on a schedule. Sets the target keyword…

Cron / scheduled trigger★★★★☆ complexity13 nodesN8N Nodes DomainkitsHTTP RequestEmail Send
Web Scraping Trigger: Cron / scheduled Nodes: 13 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #18101 — we link there as the canonical source.

This workflow follows the Emailsend → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Find valuable expired domains with DomainKits and Moz",
  "nodes": [
    {
      "id": "d0000000-0000-0000-0000-000000000001",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -700,
        -260
      ],
      "parameters": {
        "width": 540,
        "height": 1180,
        "content": "## Find valuable expired domains with DomainKits and Moz\n\n### How it works\n\nOnce a day this workflow finds expired domains: names moving through the deletion lifecycle (expired, redemption, pending delete) that match your keyword. These names surface here before they reach drop lists and auctions, which is the point: you see them while there is still time to research and backorder.\n\nThe candidates then go to the Moz Links API in one batch call. Each domain comes back with Domain Authority, Page Authority, Spam Score and linking root domains. The report lists each candidate with its expiry stage and SEO metrics, sorted by Domain Authority. The workflow reports what the data says and leaves the judgement to you.\n\n### Setup steps\n\n- Set your keyword in the Set Target node. The downstream nodes derive from it.\n- Add your DomainKits API credentials to the Find Expired Domains node.\n- Create a free Moz account, generate an Access ID and Secret Key under API Access, and add them as a Basic Auth credential on the Get Moz Metrics node.\n- Configure the Send Report node with your SMTP credentials and recipients.\n\n### Customization\n\nSwap the Send Report node for Google Sheets to build a rolling candidate pool instead of a daily email. Tighten the filters in Find Expired Domains (age, length, character set) to raise the bar before Moz spends a row on a name. Swap the Get Moz Metrics node for your Ahrefs or Semrush endpoint if that is where your subscription lives; only the URL, the auth and the field names in Build Report change.\n\n### Data note\n\nResults contain domain names, registration dates, expiry stages and SEO metrics only. No registrant personal data is included (no names, emails, addresses or phone numbers), so the output is GDPR compliant and safe to forward or store."
      },
      "typeVersion": 1
    },
    {
      "id": "d0000000-0000-0000-0000-000000000002",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -40,
        -260
      ],
      "parameters": {
        "color": 7,
        "width": 760,
        "height": 560,
        "content": "## Collect\n\nRuns daily and searches expired domains across the deletion lifecycle that contain the keyword. Age and character filters keep the list to names with real history."
      },
      "typeVersion": 1
    },
    {
      "id": "d0000000-0000-0000-0000-000000000003",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        780,
        -260
      ],
      "parameters": {
        "color": 7,
        "width": 520,
        "height": 560,
        "content": "## Score with Moz\n\nSends the candidate list to the Moz Links API in one call and reads Domain Authority, Page Authority, Spam Score and linking root domains for each name."
      },
      "typeVersion": 1
    },
    {
      "id": "d0000000-0000-0000-0000-000000000004",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        -260
      ],
      "parameters": {
        "color": 7,
        "width": 1000,
        "height": 560,
        "content": "## Report and deliver\n\nMerges expiry data with SEO metrics, sorts by Domain Authority, converts the records to a CSV attachment and emails both."
      },
      "typeVersion": 1
    },
    {
      "id": "d1000000-0000-0000-0000-000000000001",
      "name": "Every Day at 2pm",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        80
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "triggerAtHour": 14
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "d1000000-0000-0000-0000-000000000002",
      "name": "Set Target",
      "type": "n8n-nodes-base.set",
      "position": [
        240,
        80
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "d2+1234567890-+1234567890",
              "name": "keyword",
              "type": "string",
              "value": "travel"
            },
            {
              "id": "d2+1234567890-+1234567890",
              "name": "domain_limit",
              "type": "number",
              "value": 50
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "d1000000-0000-0000-0000-000000000003",
      "name": "Find Expired Domains",
      "type": "n8n-nodes-domainkits.domainKits",
      "position": [
        480,
        80
      ],
      "parameters": {
        "limit": "={{ $json.domain_limit }}",
        "filters": {
          "age_range": "10-20,20+",
          "no_hyphen": true,
          "no_number": true
        },
        "keyword": "={{ $json.keyword }}",
        "resource": "expired",
        "operation": "search",
        "searchMode": "keyword"
      },
      "typeVersion": 1,
      "alwaysOutputData": true
    },
    {
      "id": "d1000000-0000-0000-0000-000000000004",
      "name": "Prepare Batch",
      "type": "n8n-nodes-base.code",
      "position": [
        820,
        80
      ],
      "parameters": {
        "jsCode": "const candidates = $input.all()\n  .map(i => i.json)\n  .filter(d => d.domain);\n\n// One item carrying the whole batch: the Moz url_metrics endpoint accepts up\n// to 50 targets per call, so the entire list costs one API request.\nreturn [{\n  json: {\n    domains: candidates.map(d => d.domain).slice(0, 50),\n    candidates,\n  },\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d1000000-0000-0000-0000-000000000005",
      "name": "Get Moz Metrics",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "maxTries": 3,
      "position": [
        1060,
        80
      ],
      "parameters": {
        "url": "https://lsapi.seomoz.com/v2/url_metrics",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ JSON.stringify({ targets: $json.domains }) }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "retryOnFail": true,
      "typeVersion": 4.2,
      "alwaysOutputData": true,
      "waitBetweenTries": 2000
    },
    {
      "id": "d1000000-0000-0000-0000-000000000006",
      "name": "Build Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1400,
        80
      ],
      "parameters": {
        "jsCode": "const candidates = $('Prepare Batch').first().json.candidates;\n\n// Moz echoes each target back in the result's page field. Keying on it keeps\n// every metric attached to its own domain even when some targets fail.\nconst mozByDomain = new Map();\nconst mozResults = ($json.results || []).filter(r => r && r.page);\nfor (const r of mozResults) {\n  mozByDomain.set(r.page.replace(/\\/$/, '').toLowerCase(), r);\n}\n\nconst records = candidates.map(c => {\n  const m = mozByDomain.get(c.domain.toLowerCase()) || {};\n  return {\n    domain: c.domain,\n    registered_date: c.registered_date || '',\n    age: c.age ?? '',\n    expiry_stage: c.status || '',\n    domain_authority: m.domain_authority ?? '',\n    page_authority: m.page_authority ?? '',\n    spam_score: (m.spam_score === -1 ? '' : m.spam_score) ?? '',\n    linking_root_domains: m.root_domains_to_root_domain ?? '',\n  };\n});\n\nrecords.sort((a, b) => (Number(b.domain_authority) || 0) - (Number(a.domain_authority) || 0) || a.domain.localeCompare(b.domain));\n\nconst keyword = $('Set Target').first().json.keyword;\nconst scored = records.filter(r => r.domain_authority !== '').length;\n\nconst lines = records.map(r => {\n  const parts = [\n    r.domain,\n    `  registered ${r.registered_date || 'not reported'}, stage: ${r.expiry_stage || 'not reported'}`,\n    `  DA ${r.domain_authority === '' ? 'n/a' : r.domain_authority}, PA ${r.page_authority === '' ? 'n/a' : r.page_authority}, spam ${r.spam_score === '' ? 'n/a' : r.spam_score}, linking root domains ${r.linking_root_domains === '' ? 'n/a' : r.linking_root_domains}`,\n  ];\n  return parts.join('\\n');\n});\n\nconst body = [\n  `Expired domain candidates for \"${keyword}\"`,\n  'Expired domains across the deletion lifecycle (expired, redemption, pending delete), scored with Moz.',\n  '',\n  `${records.length} candidates, ${scored} scored, sorted by Domain Authority. Empty metrics mean Moz returned nothing for that name.`,\n  '',\n  records.length ? lines.join('\\n\\n') : 'No expired domains matched the keyword today.',\n  '',\n  'Records are attached as CSV.',\n].join('\\n');\n\nreturn [{\n  json: {\n    subject: `${keyword}: ${records.length} expired domain candidates`,\n    message: body,\n    records,\n  },\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d1000000-0000-0000-0000-000000000007",
      "name": "Split Records",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        1640,
        80
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "records"
      },
      "typeVersion": 1
    },
    {
      "id": "d1000000-0000-0000-0000-000000000008",
      "name": "Records To CSV",
      "type": "n8n-nodes-base.convertToFile",
      "position": [
        1880,
        80
      ],
      "parameters": {
        "options": {},
        "operation": "csv"
      },
      "typeVersion": 1.1
    },
    {
      "id": "d1000000-0000-0000-0000-000000000009",
      "name": "Send Report",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        2120,
        80
      ],
      "parameters": {
        "text": "={{ $('Build Report').first().json.message }}",
        "options": {
          "attachments": "data"
        },
        "subject": "={{ $('Build Report').first().json.subject }}",
        "toEmail": "you@example.com",
        "fromEmail": "user@example.com",
        "emailFormat": "text"
      },
      "typeVersion": 2.1
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Set Target": {
      "main": [
        [
          {
            "node": "Find Expired Domains",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Report": {
      "main": [
        [
          {
            "node": "Split Records",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Batch": {
      "main": [
        [
          {
            "node": "Get Moz Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Records": {
      "main": [
        [
          {
            "node": "Records To CSV",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Records To CSV": {
      "main": [
        [
          {
            "node": "Send Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Moz Metrics": {
      "main": [
        [
          {
            "node": "Build Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Every Day at 2pm": {
      "main": [
        [
          {
            "node": "Set Target",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find Expired Domains": {
      "main": [
        [
          {
            "node": "Prepare Batch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow runs daily to find expired domain candidates in DomainKits based on a keyword, enriches them with SEO metrics from the Moz Links API, then compiles the results into a CSV report and emails it via SMTP. Runs every day at 2pm on a schedule. Sets the target keyword…

Source: https://n8n.io/workflows/18101/ — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

Birthday Automation - Production (Fixed). Uses stopAndError, httpRequest, emailSend, bannerbear. Scheduled trigger; 86 nodes.

Stop And Error, HTTP Request, Email Send +1
Web Scraping

This workflow is an improvement of this workflow by Greg Brzezinka.

HTTP Request, Email Send, XML +1
Web Scraping

N8N-Self-Updater. Uses ssh, emailSend, httpRequest. Scheduled trigger; 27 nodes.

Ssh, Email Send, HTTP Request
Web Scraping

> An automated n8n workflow originally built for DigitalOcean-based n8n deployments, but fully compatible with any VPS or cloud hosting (e.g., AWS, Google Cloud, Hetzner, Linode, etc.) where n8n ru

Ssh, Email Send, HTTP Request
Web Scraping

What if you could spot a major sales problem—or a winning campaign—the very next morning, instead of weeks later? Imagine receiving a beautiful, data-rich alert directly in your inbox the moment your

QuickBooks, HTTP Request, Email Send