Optimize Customer.io API performance.
Use when improving response times, reducing latency,
or optimizing high-volume integrations.
Trigger with phrases like "customer.io performance", "optimize customer.io",
"customer.io latency", "customer.io speed".
Optimize Customer.io API performance for high-volume integrations: HTTP connection pooling, identify deduplication caching, event batching with flush control, fire-and-forget async tracking, and regional routing.
Prerequisites
Working Customer.io integration
Understanding of your traffic patterns and volume
Monitoring to measure improvement (see customerio-observability)
Performance Targets
Operation
Baseline
Optimized
Technique
Single identify
~200ms
~80ms
Connection pooling
Single track
~200ms
~80ms
Connection pooling
100 events batch
~20s serial
~500ms
Parallel batching
Duplicate identify
~200ms
~0ms
Dedup cache
Non-critical track
Blocking
Non-blocking
Fire-and-forget
Instructions
Step 1: HTTP Connection Pooling
// lib/customerio-pooled.ts
import { TrackClient, RegionUS } from "customerio-node";
import https from "https";
// The customerio-node SDK creates new connections by default.
// Reuse connections with a keep-alive agent.
const agent = new https.Agent({
keepAlive: true,
maxSockets: 25, // Max concurrent connections
maxFreeSockets: 10, // Keep idle connections open
timeout: 30000, // 30s socket timeout
keepAliveMsecs: 15000, // TCP keep-alive probe interval
});
// Apply to the SDK by creating a singleton with the agent
// Note: customerio-node doesn't directly accept an agent,
// but we configure Node.js global agent for HTTPS
https.globalAgent = agent;
// Singleton client — one instance = one connection pool
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
export { cio };
Step 2: Identify Deduplication Cache
// lib/customerio-dedup.ts
// Skip duplicate identify() calls within a time window
class LRUCache<K, V> {
private map = new Map<K, V>();
constructor(private maxSize: number) {}
get(key: K): V | undefined {
const val = this.map.get(key);
if (val !== undefined) {
// Move to end (most recent)
this.map.delete(key);
this.map.set(key, val);
}
return val;
}
set(key: K, val: V): void {
this.map.delete(key);
this.map.set(key, val);
if (this.map.size > this.maxSize) {
const oldest = this.map.keys().next().value;
this.map.delete(oldest!);
}
}
}
import { createHash } from "crypto";
import { TrackClient, RegionUS } from "customerio-node";
const identifyCache = new LRUCache<string, number>(10_000);
const DEDUP_TTL_MS = 5 * 60 * 1000; // 5 minutes
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
export async function dedupIdentify(
userId: string,
attrs: Record<string, any>
): Promise<void> {
// Create a hash of userId + attributes
const hash = createHash("sha256")
.update(userId + JSON.stringify(attrs))
.digest("hex")
.substring(0, 16);
const cached = identifyCache.get(hash);
if (cached && Date.now() - cached < DEDUP_TTL_MS) {
return; // Skip — identical identify() call within TTL window
}
await cio.identify(userId, attrs);
identifyCache.set(hash, Date.now());
}