1. Watch the 2-min demo here.
2. Start an account at app.theswarm.com (14-day trial, plans start at $99/month) and retrieve your API key here.
3. Create an Airtable automation (Team plan required) with the script below and replace YOURKEY).
/**
* Swarm — Network Mapper → Airtable (per official docs)
* Table fields:
* - "Domain" (text)
* - "Relationship Count" (number)
* - "Top Connector" (text)
* - "Paths (JSON)" (long text)
*/
const TABLE_NAME = 'Profiles';
const DOMAIN_FIELD = 'Domain';
const REL_COUNT_FIELD = 'Relationship Count';
const TOP_CONNECTOR_FIELD = 'Top Connector';
const PATHS_JSON_FIELD = 'Paths (JSON)';
const SWARM_API_KEY = 'YOURKEY';
const SWARM_URL = 'https://bee.theswarm.com/v2/profiles/network-mapper';
// ---------- helpers ----------
function chunk(arr, n) { const out=[]; for (let i=0;i<arr.length;i+=n) out.push(arr.slice(i,i+n)); return out; }
function toItems(data) { return (data && Array.isArray(data.items)) ? data.items : []; }
function normDomain(raw) {
if (!raw) return '';
let d = String(raw).trim().toLowerCase();
d = d.replace(/^https?:\/\//,'').replace(/^www\./,'').split('/')[0];
return d;
}
// ---------- main ----------
const table = base.getTable(TABLE_NAME);
const query = await table.selectRecordsAsync();
const updates = [];
let processed = 0;
for (const record of query.records) {
const domainRaw = record.getCellValue(DOMAIN_FIELD);
if (!domainRaw) continue;
const domain = normDomain(domainRaw);
// Per docs: term on profile_info.current_company_website with { value: "<domain>" }
const body = {
query: {
term: {
'profile_info.current_company_website': { value: domain }
}
}
};
let json;
try {
const resp = await fetch(SWARM_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'x-api-key': SWARM_API_KEY },
body: JSON.stringify(body)
});
if (!resp.ok) {
const txt = await resp.text();
console.log(`API failed for ${domain}: ${resp.status} ${resp.statusText} :: ${txt.slice(0,300)}`);
// write zeros so the row is “done”
updates.push({ id: record.id, fields: {
[REL_COUNT_FIELD]: 0, [TOP_CONNECTOR_FIELD]: '', [PATHS_JSON_FIELD]: '[]'
}});
processed += 1;
continue;
}
json = await resp.json();
} catch (e) {
console.log(`Request error for ${domain}: ${e.message}`);
continue;
}
const items = toItems(json);
// Flatten connections
const connections = [];
for (const item of items) {
const profile = item?.profile || {};
const conns = Array.isArray(item?.connections) ? item.connections : [];
for (const c of conns) {
connections.push({
profileName: profile.full_name || '',
profileTitle: profile.current_title || '',
profileCompany: profile.current_company_name || '',
profileWebsite: profile.current_company_website || '',
teamUser: c.connector_name || c.connector_id || '',
teamUserLinkedIn: c.connector_linkedin_url || '',
strength: typeof c.connection_strength === 'number'
? c.connection_strength
: (typeof c.connection_strength_normalized === 'number' ? c.connection_strength_normalized : 0),
sources: (c.sources || []).map(s => s?.origin).filter(Boolean)
});
}
}
const relationshipCount = connections.length;
let topConnector = '';
if (relationshipCount > 0) {
connections.sort((a,b)=>(b.strength||0)-(a.strength||0));
const t = connections[0];
topConnector = `${t.teamUser} -> ${t.profileName} (${(t.strength||0).toFixed(2)})`;
}
// Compact JSON for Airtable cell limits
const compact = connections.map(c => ({
profileName: c.profileName,
profileTitle: c.profileTitle,
teamUser: c.teamUser,
strength: c.strength,
sources: [...new Set(c.sources)]
}));
updates.push({
id: record.id,
fields: {
[REL_COUNT_FIELD]: relationshipCount,
[TOP_CONNECTOR_FIELD]: topConnector,
[PATHS_JSON_FIELD]: JSON.stringify(compact, null, 2)
}
});
processed += 1;
}
// Batch updates
for (const group of chunk(updates, 50)) {
await table.updateRecordsAsync(group);
}
console.log(`Processed ${processed} record(s). Updated ${updates.length} record(s).`);