Optimize Apollo.io API performance. Use when improving API response times, reducing latency, or optimizing bulk operations. Trigger with phrases like "apollo performance", "optimize apollo", "apollo slow", "apollo latency", "speed up apollo".
Optimize Apollo.io API performance through response caching, connection pooling, bulk operations, parallel fetching, and result slimming. Key insight: search is free but slow (~500ms), enrichment costs credits — cache aggressively and batch enrichment calls.
Reuse TCP connections to avoid TLS handshake overhead on every request.
// src/apollo/optimized-client.ts
import axios from 'axios';
import https from 'https';
const httpsAgent = new https.Agent({
keepAlive: true,
maxSockets: 10,
maxFreeSockets: 5,
timeout: 30_000,
});
export const optimizedClient = axios.create({
baseURL: 'https://api.apollo.io/api/v1',
headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.APOLLO_API_KEY! },
httpsAgent,
timeout: 15_000,
});
// src/apollo/cache.ts
import { LRUCache } from 'lru-cache';
// Different TTLs based on data volatility
const CACHE_TTLS: Record<string, number> = {
'/organizations/enrich': 24 * 60 * 60 * 1000, // 24h — company data rarely changes
'/people/match': 4 * 60 * 60 * 1000, // 4h — contact data changes occasionally
'/mixed_people/api_search': 15 * 60 * 1000, // 15min — search results are dynamic
'/mixed_companies/search': 30 * 60 * 1000, // 30min — company search
'/contact_stages': 60 * 60 * 1000, // 1h — stages rarely change
};
const cache = new LRUCache<string, { data: any; at: number }>({
max: 5000,
maxSize: 50 * 1024 * 1024,
sizeCalculation: (v) => JSON.stringify(v).length,
});
function cacheKey(endpoint: string, params: any): string {
return `${endpoint}:${JSON.stringify(params)}`;
}
export async function cachedRequest<T>(
endpoint: string,
requestFn: () => Promise<T>,
params: any,
): Promise<T> {
const key = cacheKey(endpoint, params);
const ttl = CACHE_TTLS[endpoint] ?? 15 * 60 * 1000;
const cached = cache.get(key);
if (cached && Date.now() - cached.at < ttl) return cached.data;
const data = await requestFn();
cache.set(key, { data, at: Date.now() });
return data;
}
export function getCacheStats() {
return { entries: cache.size, sizeBytes: cache.calculatedSize };
}
Apollo's bulk enrichment endpoint handles 10 records per call vs 1. Massive performance gain.
// src/apollo/bulk-ops.ts
import { optimizedClient } from './optimized-client';
import PQueue from 'p-queue';
const queue = new PQueue({ concurrency: 3, intervalCap: 2, interval: 1000 });
// Enrich 100 people: 100 individual calls = 100 requests @ 500ms = 50s
// Batch of 10: 10 bulk calls @ 600ms = 6s (8x faster, same credits)
export async function batchEnrich(
details: Array<{ email?: string; linkedin_url?: string; first_name?: string; last_name?: string; organization_domain?: string }>,
): Promise<any[]> {
const results: any[] = [];
for (let i = 0; i < details.length; i += 10) {
const batch = details.slice(i, i + 10);
const result = await queue.add(async () => {
const { data } = await optimizedClient.post('/people/bulk_match', {
details: batch,
reveal_personal_emails: false,
reveal_phone_number: false,
});
return data.matches ?? [];
});
results.push(...(result ?? []));
}
return results;
}
export async function parallelSearch(
domains: string[],
concurrency: number = 5,
): Promise<Map<string, any[]>> {
const searchQueue = new PQueue({ concurrency });
const results = new Map<string, any[]>();
await searchQueue.addAll(
domains.map((domain) => async () => {
const data = await cachedRequest(
'/mixed_people/api_search',
() => optimizedClient.post('/mixed_people/api_search', {
q_organization_domains_list: [domain],
person_seniorities: ['vp', 'director', 'c_suite'],
per_page: 25,
}).then((r) => r.data),
{ domain },
);
results.set(domain, data.people ?? []);
}),
);
return results;
}
Apollo returns large person objects (~2KB each). Extract only needed fields to reduce memory.
interface SlimPerson {
id: string;
name: string;
title: string;
email?: string;
company: string;
seniority: string;
}
function slimPerson(raw: any): SlimPerson {
return {
id: raw.id,
name: raw.name,
title: raw.title,
email: raw.email,
company: raw.organization?.name ?? '',
seniority: raw.seniority ?? '',
};
}
// Use immediately after API call to free memory
const { data } = await optimizedClient.post('/mixed_people/api_search', { ... });
const slim = data.people.map(slimPerson); // ~200 bytes each instead of ~2KB
async function benchmark(includePaidEndpoints = false) {
const endpoints = [
{ name: 'People Search', fn: () => optimizedClient.post('/mixed_people/api_search',
{ q_organization_domains_list: ['apollo.io'], per_page: 1 }) },
{ name: 'Auth Health', fn: () => optimizedClient.get('/auth/health') },
];
// Paid enrichment benchmarks require an approved, budgeted test run.
if (includePaidEndpoints) {
endpoints.push({ name: 'Org Enrich', fn: () => optimizedClient.get('/organizations/enrich',
{ params: { domain: 'apollo.io' } }) });
}
for (const ep of endpoints) {
const times: number[] = [];
for (let i = 0; i < 5; i++) {
const start = Date.now();
try { await ep.fn(); } catch {}
times.push(Date.now() - start);
}
const avg = Math.round(times.reduce((a, b) => a + b) / times.length);
const p95 = times.sort((a, b) => a - b)[Math.floor(times.length * 0.95)];
console.log(`${ep.name}: avg=${avg}ms, p95=${p95}ms`);
}
}
keepAlive and configurable maxSockets/people/bulk_match (10x fewer requests)p-queue concurrency controlFor a search-latency regression, capture a baseline with the free search and health endpoints against a mock or approved sandbox fixture, then introduce connection reuse and a bounded cache behind a feature flag. Compare p95, error rate, cache-hit behavior, and result equivalence before promoting the change. A paid-enrichment benchmark requires an explicit approval flag, a fixed credit budget, and a public organization fixture; otherwise it remains out of the run. Roll back the flag if latency improves at the cost of stale, incorrect, or over-broadly cached results.
| Issue | Resolution | |-------|------------| | High latency | Enable connection pooling, check for stale cache | | Cache misses | Increase TTL for stable data (org enrichment) | | Rate limits with parallelism | Reduce p-queue concurrency | | Memory growth | Lower LRU max entries, slim response payloads |
Proceed to apollo-cost-tuning for cost optimization.
下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer