Xano Integration
Overview
This guide shows you how to integrate SpiderIQ's orchestrated campaigns with Xano, a no-code backend platform. You'll build a complete lead generation system that:
- Creates orchestrated campaigns via SpiderIQ API
- Processes locations automatically with workflow chaining
- Stores enriched leads in your Xano database
- Schedules recurring campaigns with Xano Tasks
Prerequisites
Xano Account
Free tier works for testing. Pro tier recommended for production.
SpiderIQ Credentials
Your API token from SpiderIQ dashboard.
Step 1: Store API Credentials
First, securely store your SpiderIQ API token in Xano's environment variables.
Navigate to your Xano workspace → Settings → Environment Variables
Create a new variable:
- Name:
SPIDERIQ_TOKEN - Value: Your SpiderIQ API token (e.g.,
cli_xxx:sk_xxx:secret_xxx) - Environment: Production (and optionally Development)
Click Save. This token is now accessible in all Function Stacks.
Never hardcode API tokens in Function Stacks. Always use environment variables for security.
Step 2: Create Database Tables
Create three tables to store campaign data and leads.
Table: campaigns
| Field | Type | Description |
|---|---|---|
id | Integer (Auto) | Primary key |
campaign_id | Text | SpiderIQ campaign ID |
query | Text | Search query (e.g., "restaurants") |
country_code | Text | ISO country code |
status | Text | Campaign status |
total_locations | Integer | Number of locations |
has_workflow | Boolean | Workflow enabled |
created_at | Timestamp | Creation time |
completed_at | Timestamp | Completion time |
Table: leads
| Field | Type | Description |
|---|---|---|
id | Integer (Auto) | Primary key |
campaign_id | Text | Foreign key to campaigns |
business_name | Text | Business name |
business_address | Text | Full address |
business_phone | Text | Phone number |
business_rating | Decimal | Google rating |
domain | Text | Website domain |
location | Text | City/location name |
workflow_stage | Text | Current stage |
created_at | Timestamp | Creation time |
Table: lead_emails
| Field | Type | Description |
|---|---|---|
id | Integer (Auto) | Primary key |
lead_id | Integer | Foreign key to leads |
email | Text | Email address |
status | Text | valid, invalid, risky, unknown |
score | Integer | Deliverability score (0-100) |
is_deliverable | Boolean | Is deliverable |
is_role_account | Boolean | Is role account (info@, etc.) |
Add indexes on campaign_id fields for better query performance when processing large campaigns.
Step 3: Create Campaign Function Stack
Build a Function Stack to create orchestrated campaigns.
Function Stack: create_campaign
Input Parameters:
query(Text, required) - Search querycountry_code(Text, required) - ISO country codemin_population(Integer, optional) - Minimum city population
Step-by-Step Configuration
Node 1: Build Request Body
- Type: Create Variable
- Name:
request_body - Value (Object):
{
"query": "$input.query",
"country_code": "$input.country_code",
"filter": {
"mode": "population",
"min_population": "$input.min_population"
},
"workflow": {
"spidersite": {
"enabled": true,
"max_pages": 10,
"extract_company_info": true
},
"spiderverify": {
"enabled": true,
"max_emails_per_business": 5
},
"filter_social_media": true,
"filter_review_sites": true
}
}
Node 2: HTTP Request
- Type: External API Request
- Method: POST
- URL:
https://spideriq.ai/api/v1/jobs/spiderMaps/campaigns/submit - Headers:
Authorization:Bearer $env.SPIDERIQ_TOKENContent-Type:application/json
- Body:
$var.request_body
Node 3: Store Campaign
- Type: Add Record (campaigns table)
- Fields:
campaign_id:$external.campaign_idquery:$input.querycountry_code:$input.country_codestatus:$external.statustotal_locations:$external.total_locationshas_workflow:$external.has_workflowcreated_at:now()
Node 4: Return Response
- Type: Return
- Value:
$external(full SpiderIQ response)
Example Response
{
"campaign_id": "camp_fr_restaurants_20251223_abc123",
"status": "active",
"total_locations": 42,
"has_workflow": true
}
Step 4: Process Campaign Loop
Create a Function Stack that processes all locations in a campaign.
Function Stack: process_campaign
Input Parameters:
campaign_id(Text, required) - The SpiderIQ campaign ID
Step-by-Step Configuration
Node 1: Initialize Loop Variable
- Type: Create Variable
- Name:
has_more - Value:
true
Node 2: While Loop
- Type: Loop (While)
- Condition:
$var.has_more == true
Inside Loop:
Node 2.1: Call /next Endpoint
- Type: External API Request
- Method: POST
- URL:
https://spideriq.ai/api/v1/jobs/spiderMaps/campaigns/$input.campaign_id/next - Headers:
Authorization:Bearer $env.SPIDERIQ_TOKEN
Node 2.2: Update has_more
- Type: Update Variable
- Name:
has_more - Value:
$external.has_more
Node 2.3: Conditional (if current_task exists)
- Type: Conditional
- Condition:
$external.current_task != null
Node 2.3.1: Log Progress (inside conditional)
- Type: Create Variable
- Name:
progress_log - Value:
Processing: $external.current_task.search_string
Node 2.4: Wait (Rate Limiting)
- Type: Utilities → Wait
- Duration: 2 seconds
End Loop
Node 3: Update Campaign Status
- Type: Edit Record (campaigns table)
- Filter:
campaign_id == $input.campaign_id - Fields:
status:completedcompleted_at:now()
Node 4: Return
- Type: Return
- Value:
{ "status": "completed", "campaign_id": "$input.campaign_id" }
Rate Limiting: Include a 1-2 second delay between /next calls to avoid overwhelming the API. SpiderIQ processes jobs asynchronously, so there's no need to rush.
Step 5: Get and Store Results
After the campaign completes, fetch and store the aggregated results.
Function Stack: get_campaign_results
Input Parameters:
campaign_id(Text, required) - The SpiderIQ campaign ID
Step-by-Step Configuration
Node 1: Fetch Workflow Results
- Type: External API Request
- Method: GET
- URL:
https://spideriq.ai/api/v1/jobs/spiderMaps/campaigns/$input.campaign_id/workflow-results - Headers:
Authorization:Bearer $env.SPIDERIQ_TOKEN
Node 2: Loop Through Locations
- Type: Loop (For Each)
- Array:
$external.locations - Item Variable:
location
Node 2.1: Loop Through Businesses
- Type: Loop (For Each)
- Array:
$var.location.businesses - Item Variable:
business
Node 2.1.1: Add Lead Record
- Type: Add Record (leads table)
- Fields:
campaign_id:$input.campaign_idbusiness_name:$var.business.business_namebusiness_address:$var.business.business_addressbusiness_phone:$var.business.business_phonebusiness_rating:$var.business.business_ratingdomain:$var.business.domainlocation:$var.location.display_nameworkflow_stage:$var.business.workflow_stagecreated_at:now()
Node 2.1.2: Get Lead ID
- Type: Create Variable
- Name:
lead_id - Value:
$addrecord.id
Node 2.1.3: Loop Through Verified Emails
- Type: Loop (For Each)
- Array:
$var.business.emails_verified - Item Variable:
email - Condition:
$var.email.status == "valid"
Node 2.1.3.1: Add Email Record
- Type: Add Record (lead_emails table)
- Fields:
lead_id:$var.lead_idemail:$var.email.emailstatus:$var.email.statusscore:$var.email.scoreis_deliverable:$var.email.is_deliverableis_role_account:$var.email.is_role_account
End All Loops
Node 3: Return Summary
- Type: Return
- Value:
{
"campaign_id": "$input.campaign_id",
"total_businesses": "$external.total_businesses",
"total_valid_emails": "$external.total_valid_emails"
}
Step 6: Schedule with Xano Tasks
Automate your lead generation with scheduled tasks.
Create a Scheduled Task
Go to your Xano workspace → Background Tasks
Click "Add Task" and configure:
- Name:
daily_lead_campaign - Schedule: Cron expression (e.g.,
0 8 * * *for 8 AM daily)
Build a Function Stack that:
- Creates a new campaign with today's parameters
- Calls
process_campaignto iterate locations - Calls
get_campaign_resultsto store leads - Optionally sends notification (email, Slack webhook)
Complete Scheduled Task Function Stack
1. Create Campaign
→ Call: create_campaign(query: "restaurants", country_code: "US", min_population: 50000)
→ Store campaign_id
2. Process All Locations
→ Call: process_campaign(campaign_id: $step1.campaign_id)
→ Wait for completion
3. Fetch and Store Results
→ Call: get_campaign_results(campaign_id: $step1.campaign_id)
4. Send Notification (Optional)
→ External API Request to Slack/Email
→ Include: total_businesses, total_valid_emails
Step 7: Build a Dashboard API
Create endpoints for your frontend to display campaign progress.
Endpoint: GET /campaigns
Returns all campaigns with progress stats.
// Response
[
{
"campaign_id": "camp_fr_restaurants_20251223_abc123",
"query": "restaurants",
"country_code": "FR",
"status": "completed",
"total_locations": 42,
"leads_count": 840,
"valid_emails_count": 892,
"created_at": "2025-12-23T10:30:00Z"
}
]
Endpoint: GET /campaigns/{id}/leads
Returns leads for a specific campaign with filtering.
Query Parameters:
min_rating- Filter by minimum Google ratinghas_email- Only leads with valid emailspage,page_size- Pagination
// Response
{
"leads": [
{
"business_name": "Le Petit Bistro",
"business_address": "123 Rue de Rivoli, Paris",
"business_rating": 4.5,
"domain": "lepetitbistro.fr",
"emails": [
{"email": "contact@lepetitbistro.fr", "status": "valid", "score": 95}
]
}
],
"total": 840,
"page": 1
}
Complete Xano Workflow
Here's the complete automated flow:
Error Handling
Add error handling to your Function Stacks:
API Error Handling
After each External API Request, add a Conditional:
Condition: $external.statusCode >= 400
If True:
→ Log Error: "SpiderIQ API Error: $external.statusCode"
→ Update campaign status to "error"
→ Stop execution / Return error
Retry Logic
For transient failures, implement retry:
1. Create Variable: retry_count = 0
2. While retry_count < 3:
→ Try API call
→ If success: break
→ If fail: retry_count++, wait 5 seconds
3. If still failing: log and alert
Best Practices
Batch Processing
For large countries, split into regional campaigns to avoid timeouts.
Rate Limiting
Add 1-2 second delays between API calls. SpiderIQ queues jobs asynchronously.
Incremental Storage
Store leads as they come in via /next responses, not just at the end.
Monitoring
Log campaign progress and set up alerts for failures.
SpiderMedia Integration
SpiderMedia provides per-client file and video storage. Use the base64 endpoint for file uploads in Xano.
Xano doesn't support multipart/form-data natively. Always use the base64 endpoint for file uploads.
Upload Files (Images, Documents)
Use /media/files/upload-base64 with JSON:
| Setting | Value |
|---|---|
| Method | POST |
| URL | https://spideriq.ai/api/v1/media/files/upload-base64 |
| Headers | Authorization: Bearer $env.SPIDERIQ_TOKEN |
| Headers | Content-Type: application/json |
Body:
{
"file_data": "{base64_encoded_content}",
"filename": "logo.png",
"folder": "uploads"
}
Use Xano's base64_encode() function to encode file content before sending.
Import Videos (from URL)
Video import uses standard JSON - no base64 needed:
| Setting | Value |
|---|---|
| Method | POST |
| URL | https://spideriq.ai/api/v1/media/videos/import |
| Headers | Authorization: Bearer $env.SPIDERIQ_TOKEN |
| Headers | Content-Type: application/json |
Body:
{
"url": "https://instagram.com/reel/ABC123",
"title": "Product Demo",
"privacy": 2
}
Poll Video Status
After importing, poll until status is ready:
| Setting | Value |
|---|---|
| Method | GET |
| URL | https://spideriq.ai/api/v1/media/videos/{video_id}/status |
When status === "ready", the response includes:
file_url- Direct .mp4 URL for downloadingdownload_url- Download endpoint URL
See the SpiderMedia Storage Guide for complete examples.