Overview

This guide shows you how to integrate SpiderIQ’s orchestrated campaigns with n8n, the powerful workflow automation platform. You’ll build a complete visual workflow that:
  1. Creates orchestrated campaigns via SpiderIQ API
  2. Loops through locations until complete
  3. Fetches aggregated results
  4. Exports to Google Sheets, Airtable, or your database

Prerequisites

n8n Instance

Self-hosted or n8n Cloud account

SpiderIQ Credentials

Your API token from SpiderIQ dashboard

Step 1: Create SpiderIQ Credentials

First, set up authentication for SpiderIQ in n8n.
1

Open Credentials

In n8n, go to SettingsCredentialsAdd Credential
2

Select Type

Choose Header Auth (since SpiderIQ uses Bearer token)
3

Configure

  • Name: SpiderIQ API
  • Header Name: Authorization
  • Header Value: Bearer cli_xxx:sk_xxx:secret_xxx (your full token)
4

Save

Click Save to store the credential securely
Store your token securely in n8n credentials. Never hardcode it in workflow nodes.

Step 2: Create Campaign Node

Set up the HTTP Request node to create an orchestrated campaign.

HTTP Request Node Configuration

General Settings:
SettingValue
MethodPOST
URLhttps://spideriq.di-atomic.com/api/v1/jobs/spiderMaps/campaigns/submit
AuthenticationPredefined Credential Type → Header Auth
CredentialSpiderIQ API
Headers:
HeaderValue
Content-Typeapplication/json
Body (JSON):
{
  "query": "{{ $json.query }}",
  "country_code": "{{ $json.country_code }}",
  "name": "n8n Campaign - {{ $now.format('yyyy-MM-dd') }}",
  "filter": {
    "mode": "population",
    "min_population": {{ $json.min_population || 50000 }}
  },
  "workflow": {
    "spidersite": {
      "enabled": true,
      "max_pages": 10,
      "crawl_strategy": "bestfirst",
      "enable_spa": true,
      "extract_company_info": true,
      "product_description": "{{ $json.product_description }}",
      "icp_description": "{{ $json.icp_description }}"
    },
    "spiderverify": {
      "enabled": true,
      "max_emails_per_business": 5
    },
    "filter_social_media": true,
    "filter_review_sites": true,
    "filter_directories": true,
    "filter_maps": true
  }
}

Response

The node outputs:
{
  "campaign_id": "camp_fr_restaurants_20251223_abc123",
  "status": "active",
  "total_locations": 42,
  "has_workflow": true
}

Step 3: Loop Until Complete

Create a loop that calls /next until all locations are processed.

Loop Structure

┌─────────────────────────────────────────┐
│  Loop                                    │
│  ┌─────────────────────────────────────┐│
│  │ HTTP Request: POST /next            ││
│  └─────────────────────────────────────┘│
│  ┌─────────────────────────────────────┐│
│  │ IF: has_more == true                ││
│  │   → Continue Loop                   ││
│  │   → Else: Exit Loop                 ││
│  └─────────────────────────────────────┘│
│  ┌─────────────────────────────────────┐│
│  │ Wait: 2 seconds                     ││
│  └─────────────────────────────────────┘│
└─────────────────────────────────────────┘

HTTP Request Node: Call /next

Configuration:
SettingValue
MethodPOST
URLhttps://spideriq.di-atomic.com/api/v1/jobs/spiderMaps/campaigns/{{ $json.campaign_id }}/next
AuthenticationHeader Auth → SpiderIQ API
Response:
{
  "has_more": true,
  "campaign_id": "camp_fr_restaurants_20251223_abc123",
  "current_task": {
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "search_string": "restaurants in Paris, France",
    "status": "queued"
  },
  "progress": {
    "completed": 5,
    "total": 42,
    "percentage": 11.9
  }
}

IF Node: Check has_more

Condition:
  • Value 1: {{ $json.has_more }}
  • Operation: equal
  • Value 2: true
Branches:
  • True: Continue to Wait node, then loop back
  • False: Exit loop, proceed to get results

Wait Node

Configuration:
  • Wait Time: 2 seconds
  • Reason: Rate limiting and allowing jobs to process
SpiderIQ processes jobs asynchronously across 112 workers. The /next endpoint returns immediately after queuing each job, so a short delay helps prevent overwhelming the API.

Step 4: Get Workflow Results

After the loop completes, fetch aggregated results.

HTTP Request Node: Get Results

Configuration:
SettingValue
MethodGET
URLhttps://spideriq.di-atomic.com/api/v1/jobs/spiderMaps/campaigns/{{ $json.campaign_id }}/workflow-results
AuthenticationHeader Auth → SpiderIQ API
Response Structure:
{
  "campaign_id": "camp_fr_restaurants_20251223_abc123",
  "total_businesses": 840,
  "total_valid_emails": 892,
  "workflow_progress": {
    "sites_completed": 485,
    "verifies_completed": 485,
    "emails_verified": 1250
  },
  "locations": [
    {
      "display_name": "Paris",
      "businesses": [
        {
          "business_name": "Le Petit Bistro",
          "business_phone": "+33 1 42 96 12 34",
          "domain": "lepetitbistro.fr",
          "emails_verified": [
            {"email": "contact@lepetitbistro.fr", "status": "valid"}
          ]
        }
      ]
    }
  ]
}

Step 5: Transform and Flatten Data

Use a Code node or Item Lists node to flatten the nested results.

Code Node: Flatten Results

JavaScript:
const results = $input.all()[0].json;
const leads = [];

for (const location of results.locations) {
  for (const business of location.businesses) {
    // Get valid emails only
    const validEmails = (business.emails_verified || [])
      .filter(e => e.status === 'valid')
      .map(e => e.email);

    if (validEmails.length > 0) {
      leads.push({
        campaign_id: results.campaign_id,
        location: location.display_name,
        business_name: business.business_name,
        business_phone: business.business_phone || '',
        business_address: business.business_address || '',
        business_rating: business.business_rating || 0,
        domain: business.domain || '',
        email: validEmails[0], // Primary email
        all_emails: validEmails.join(', '),
        lead_score: business.lead_scoring?.champ_score || 0,
        workflow_stage: business.workflow_stage
      });
    }
  }
}

return leads.map(lead => ({ json: lead }));
Output:
[
  {
    "campaign_id": "camp_fr_restaurants_20251223_abc123",
    "location": "Paris",
    "business_name": "Le Petit Bistro",
    "business_phone": "+33 1 42 96 12 34",
    "business_address": "123 Rue de Rivoli, Paris",
    "business_rating": 4.5,
    "domain": "lepetitbistro.fr",
    "email": "contact@lepetitbistro.fr",
    "all_emails": "contact@lepetitbistro.fr, reservations@lepetitbistro.fr",
    "lead_score": 78,
    "workflow_stage": "complete"
  }
]

Step 6: Export to Destination

Connect the flattened data to your preferred destination.

Option A: Google Sheets

Google Sheets Node Configuration:
SettingValue
OperationAppend or Update
DocumentYour Google Sheet
SheetLeads
MappingDefine each column
Column Mapping:
ColumnValue
A - Business Name{{ $json.business_name }}
B - Phone{{ $json.business_phone }}
C - Address{{ $json.business_address }}
D - Rating{{ $json.business_rating }}
E - Domain{{ $json.domain }}
F - Email{{ $json.email }}
G - Lead Score{{ $json.lead_score }}
H - Location{{ $json.location }}
I - Campaign ID{{ $json.campaign_id }}

Option B: Airtable

Airtable Node Configuration:
SettingValue
OperationCreate
BaseYour Airtable Base
TableLeads
Field Mapping: Map each field from the Code node output to Airtable columns.

Option C: PostgreSQL / MySQL

Database Node Configuration:
SettingValue
OperationInsert
Tableleads
Use the Execute Query node for bulk inserts:
INSERT INTO leads (campaign_id, business_name, email, phone, rating, domain, location)
VALUES ($1, $2, $3, $4, $5, $6, $7)

Step 7: Schedule the Workflow

Set up automated execution with the Schedule Trigger node.

Schedule Trigger Configuration

Example Schedules:
ScheduleCron ExpressionDescription
Daily at 8 AM0 8 * * *Run every morning
Weekly Monday0 9 * * 1Weekly on Monday at 9 AM
Every 6 hours0 */6 * * *Four times per day

Manual Trigger for Testing

Add a Manual Trigger node in parallel for testing:
[Manual Trigger] ─┐
                  ├─→ [Set Parameters] → [Create Campaign] → ...
[Schedule Trigger]┘

Set Parameters Node

Configure campaign parameters:
{
  "query": "restaurants",
  "country_code": "FR",
  "min_population": 100000,
  "product_description": "Restaurant management software",
  "icp_description": "Restaurant owners seeking efficiency tools"
}

Complete Workflow

Here’s the full workflow structure:
[Schedule Trigger]


[Set Parameters]


[HTTP Request: Create Campaign]


[Loop] ◄──────────────────────┐
  │                           │
  ▼                           │
[HTTP Request: POST /next]    │
  │                           │
  ▼                           │
[IF: has_more == true?]       │
  │           │               │
  │ Yes       │ No            │
  ▼           ▼               │
[Wait 2s]  [Exit Loop]        │
  │                           │
  └───────────────────────────┘


[HTTP Request: GET /workflow-results]


[Code: Flatten Results]


[Google Sheets / Airtable / Database]


[Send Notification (Optional)]

Importable Workflow JSON

Copy this JSON and import it into n8n:
{
  "name": "SpiderIQ Orchestrated Campaign",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [{"field": "cronExpression", "expression": "0 8 * * *"}]
        }
      },
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [250, 300]
    },
    {
      "parameters": {
        "values": {
          "string": [
            {"name": "query", "value": "restaurants"},
            {"name": "country_code", "value": "FR"},
            {"name": "product_description", "value": "Restaurant management software"},
            {"name": "icp_description", "value": "Restaurant owners seeking efficiency"}
          ],
          "number": [{"name": "min_population", "value": 100000}]
        }
      },
      "name": "Set Parameters",
      "type": "n8n-nodes-base.set",
      "position": [450, 300]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://spideriq.di-atomic.com/api/v1/jobs/spiderMaps/campaigns/submit",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [{"name": "Content-Type", "value": "application/json"}]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {"name": "query", "value": "={{ $json.query }}"},
            {"name": "country_code", "value": "={{ $json.country_code }}"},
            {"name": "workflow", "value": "={\"spidersite\":{\"enabled\":true,\"max_pages\":10},\"spiderverify\":{\"enabled\":true}}"}
          ]
        }
      },
      "name": "Create Campaign",
      "type": "n8n-nodes-base.httpRequest",
      "position": [650, 300],
      "credentials": {"httpHeaderAuth": {"id": "1", "name": "SpiderIQ API"}}
    }
  ],
  "connections": {
    "Schedule Trigger": {"main": [[{"node": "Set Parameters", "type": "main", "index": 0}]]},
    "Set Parameters": {"main": [[{"node": "Create Campaign", "type": "main", "index": 0}]]}
  }
}
After importing, you’ll need to:
  1. Create the SpiderIQ API credential
  2. Complete the Loop and remaining nodes
  3. Configure your destination (Sheets/Airtable/DB)

Error Handling

Add error handling with the Error Trigger workflow.

Error Trigger Workflow

Create a separate workflow that triggers on errors:
[Error Trigger]


[Get Error Details]


[Send Slack/Email Alert]

Retry Logic

Wrap API calls in a Loop with retry logic:
// In Code node before HTTP Request
const maxRetries = 3;
let attempt = 0;
let success = false;

while (attempt < maxRetries && !success) {
  try {
    // API call happens in next node
    success = true;
  } catch (error) {
    attempt++;
    await new Promise(r => setTimeout(r, 5000)); // Wait 5s
  }
}

Best Practices

Use Sub-Workflows

Break complex logic into sub-workflows for maintainability.

Add Logging

Use the Notion or Airtable node to log each run for debugging.

Set Timeouts

Configure execution timeouts for long campaigns.

Monitor Executions

Check n8n’s Execution History for failed runs.

Troubleshooting

  • Verify your SpiderIQ token is correct
  • Check the token includes all three parts: cli_xxx:sk_xxx:secret_xxx
  • Ensure the Header Auth credential has Bearer prefix
  • Check the IF node condition: {{ $json.has_more }} == true
  • Verify the /next response includes has_more field
  • Add a maximum iterations safety limit
  • Confirm the campaign completed (check /status)
  • Verify has_workflow: true in campaign response
  • Check if locations had any businesses
  • Increase workflow timeout in Settings
  • For large campaigns, consider splitting by region
  • Add longer Wait times between /next calls

Next Steps