Claude-code-plugins-plus-skills hubspot-performance-tuning
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/hubspot-pack/skills/hubspot-performance-tuning" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-hubspot-performance-tuning && rm -rf "$T"
manifest:
plugins/saas-packs/hubspot-pack/skills/hubspot-performance-tuning/SKILL.mdsource content
HubSpot Performance Tuning
Overview
Optimize HubSpot API performance through batch operations, caching, search optimization, and request minimization.
Prerequisites
installed@hubspot/api-client- Understanding of your access patterns (read-heavy vs write-heavy)
- Optional: Redis for distributed caching
Instructions
Step 1: Use Batch APIs Everywhere
The single biggest performance win: batch operations reduce API calls by up to 100x.
import * as hubspot from '@hubspot/api-client'; const client = new hubspot.Client({ accessToken: process.env.HUBSPOT_ACCESS_TOKEN!, numberOfApiCallRetries: 3, }); // BAD: 100 API calls for 100 contacts async function getContactsSlow(ids: string[]) { return Promise.all(ids.map(id => client.crm.contacts.basicApi.getById(id, ['email', 'firstname']) )); } // GOOD: 1 API call for 100 contacts async function getContactsFast(ids: string[], properties: string[]) { // POST /crm/v3/objects/contacts/batch/read const result = await client.crm.contacts.batchApi.read({ inputs: ids.map(id => ({ id })), properties, propertiesWithHistory: [], }); return result.results; } // For more than 100 records, chunk: async function getContactsChunked(ids: string[], properties: string[]) { const results = []; for (let i = 0; i < ids.length; i += 100) { const chunk = ids.slice(i, i + 100); const batch = await getContactsFast(chunk, properties); results.push(...batch); } return results; }
Step 2: Request Only Needed Properties
// BAD: Returns ALL default properties (slow, large payload) const contact = await client.crm.contacts.basicApi.getById(id); // GOOD: Only request what you need (fast, small payload) const contact = await client.crm.contacts.basicApi.getById( id, ['email', 'firstname', 'lastname', 'lifecyclestage'] // specific properties );
Step 3: Cache Frequently Accessed Data
import { LRUCache } from 'lru-cache'; const contactCache = new LRUCache<string, any>({ max: 5000, // max entries ttl: 5 * 60 * 1000, // 5 minute TTL updateAgeOnGet: true, }); async function getCachedContact(id: string, properties: string[]) { const cacheKey = `contact:${id}`; const cached = contactCache.get(cacheKey); if (cached) return cached; const contact = await client.crm.contacts.basicApi.getById(id, properties); contactCache.set(cacheKey, contact); return contact; } // Invalidate on webhook events function invalidateContactCache(contactId: string): void { contactCache.delete(`contact:${contactId}`); }
Step 4: Optimize Search Queries
// BAD: Overly broad search, returns too much data const bad = await client.crm.contacts.searchApi.doSearch({ filterGroups: [], // no filters = scan entire DB properties: [], // returns default properties limit: 100, after: 0, sorts: [], }); // GOOD: Specific filters, minimal properties, sorted const good = await client.crm.contacts.searchApi.doSearch({ filterGroups: [{ filters: [ { propertyName: 'lifecyclestage', operator: 'EQ', value: 'customer' }, { propertyName: 'createdate', operator: 'GTE', value: String(Date.now() - 86400000) }, ], }], properties: ['email', 'firstname'], // only what you need limit: 50, after: 0, sorts: [{ propertyName: 'createdate', direction: 'DESCENDING' }], }); // Search limits: max 5 filterGroups, 6 filters per group, 18 filters total // Search returns max 10,000 results total (pagination limit)
Step 5: Pipeline and Property Caching
Pipeline stages and custom properties rarely change -- cache them aggressively:
let pipelineCache: Map<string, any> | null = null; let pipelineCacheExpiry = 0; async function getCachedPipelines(objectType: 'deals' | 'tickets') { if (pipelineCache && Date.now() < pipelineCacheExpiry) { return pipelineCache.get(objectType); } const pipelines = await client.crm.pipelines.pipelinesApi.getAll(objectType); if (!pipelineCache) pipelineCache = new Map(); pipelineCache.set(objectType, pipelines.results); pipelineCacheExpiry = Date.now() + 3600000; // 1 hour cache return pipelines.results; } // Same pattern for properties let propertyCache: Map<string, any> | null = null; async function getCachedProperties(objectType: string) { if (propertyCache?.has(objectType)) { return propertyCache.get(objectType); } const props = await client.crm.properties.coreApi.getAll(objectType); if (!propertyCache) propertyCache = new Map(); propertyCache.set(objectType, props.results); return props.results; }
Step 6: Pagination with Async Generators
// Memory-efficient streaming for large exports async function* streamAllContacts(properties: string[]) { let after: string | undefined; do { const page = await client.crm.contacts.basicApi.getPage( 100, // max page size after, properties ); yield* page.results; after = page.paging?.next?.after; } while (after); } // Process millions of records without running out of memory let count = 0; for await (const contact of streamAllContacts(['email', 'firstname'])) { await processContact(contact); count++; if (count % 1000 === 0) console.log(`Processed ${count} contacts`); }
Output
- Batch operations reducing API calls by 100x
- Property-specific requests reducing payload size
- LRU caching for frequently accessed records
- Optimized search queries with proper filters
- Streaming pagination for large datasets
Error Handling
| Issue | Cause | Solution |
|---|---|---|
Batch returns | Some records failed | Check individual in response |
| Cache stale data | TTL too long | Invalidate on webhook events |
| Search returns max 10,000 | HubSpot search limit | Use pagination instead |
| Memory pressure | Caching too much | Set entries on LRU cache |
Resources
Next Steps
For cost optimization, see
hubspot-cost-tuning.