Skip to main content
Glama

Searchspring Integration Assistant

by yangjeep
searchspring-client.ts•57.8 kB
import { SearchspringConfig } from "./config.js"; export interface PlatformImplementationParams { platform: "shopify" | "bigcommerce-blueprint" | "bigcommerce-stencil" | "magento1" | "magento2" | "miva" | "commercev3" | "3dcart" | "volusion" | "custom"; eventType: "product" | "cart" | "sale" | "search-click"; sku?: string; price?: number; quantity?: number; } export interface CodeValidationParams { code: string; codeType: "search" | "autocomplete" | "suggest" | "trending" | "recommendations" | "finder" | "beacon" | "bulk-index" | "tracking"; platform?: "shopify" | "bigcommerce" | "magento1" | "magento2" | "miva" | "commercev3" | "3dcart" | "volusion" | "custom" | "other"; issue?: string; } export interface ApiGuideParams { api: "search" | "autocomplete" | "suggest" | "trending" | "recommendations" | "finder" | "beacon" | "bulk-index"; } export interface ParameterGuideParams { api: "search" | "autocomplete" | "suggest" | "trending" | "recommendations" | "finder" | "beacon" | "bulk-index"; parameter: string; } export interface CodeGeneratorParams { api: "search" | "autocomplete" | "suggest" | "trending" | "recommendations" | "finder" | "beacon" | "bulk-index" | "tracking"; platform: "shopify" | "bigcommerce" | "magento1" | "magento2" | "miva" | "commercev3" | "3dcart" | "volusion" | "javascript" | "php" | "python" | "custom"; eventType?: "product" | "cart" | "sale" | "search-click" | "impression"; useCase?: string; } export class SearchspringClient { private config: SearchspringConfig; constructor(config: SearchspringConfig) { this.config = config; } private getSiteIdOrExample(): string { return this.config.siteId || "xyz789"; } private addSiteIdNote(text: string): string { if (!this.config.siteId) { return text + "\n\n**Note**: Replace 'xyz789' with your actual Searchspring site ID (also known as tracking code) in all URLs and code examples above. This is a 6-character alphanumeric identifier provided by your Searchspring representative."; } return text; } async getApiGuide(params: ApiGuideParams) { const { api } = params; const siteId = this.getSiteIdOrExample(); const apiGuides = { search: { name: "Search API", description: "Full-text product search with filtering, sorting, and pagination", endpoint: `https://${siteId}.a.searchspring.io/api/search/search.json`, requiredParams: ["siteId", "resultsFormat", "userId", "sessionId", "pageLoadId", "domain"], optionalParams: ["q", "page", "resultsPerPage", "filter.*", "bgfilter.*", "sort.*", "redirectResponse", "landingPage", "tag", "includedFacets", "excludedFacets", "disableInlineBanners", "lastViewed", "cart", "shopper"], example: `fetch('https://${siteId}.a.searchspring.io/api/search/search.json?siteId=${siteId}&resultsFormat=json&q=shoes&userId=user123&sessionId=session456&pageLoadId=page789&domain=https://yoursite.com') .then(response => response.json()) .then(data => { console.log('Total results:', data.pagination.totalResults); data.results.forEach(product => { console.log(product.title, product.price); }); }) .catch(error => console.error('Search error:', error));`, useCases: [ "Product catalog search", "Category page filtering", "Search results page", "Faceted navigation" ], bestPractices: [ "Always include tracking parameters: userId (from ssUserId cookie), sessionId (from ssSessionIdNamespace cookie), pageLoadId (UUID v4), domain (window.location.href)", "Use background filters (bgfilter.*) for permanent filtering, not filter.*", "Implement proper error handling with .catch()", "Use debouncing for real-time search (minimum 50ms for autocomplete)", "Include pagination for large result sets", "Use query parameter (q) for all search requests" ] }, autocomplete: { name: "Autocomplete API", description: "Real-time product preview as user types - REQUIRED for autocomplete functionality (not Search API)", endpoint: `https://${siteId}.a.searchspring.io/api/search/autocomplete.json`, requiredParams: ["siteId", "resultsFormat", "userId", "sessionId", "pageLoadId", "domain"], optionalParams: ["q", "resultsPerPage", "page", "filter.*", "bgfilter.*", "sort.*", "redirectResponse", "lastViewed", "cart", "shopper"], example: `const searchInput = document.getElementById('search'); let debounceTimer; searchInput.addEventListener('input', function(e) { clearTimeout(debounceTimer); const query = e.target.value; if (query.length >= 2) { debounceTimer = setTimeout(() => { fetchAutocomplete(query); }, 50); // 50ms debounce as recommended by Searchspring docs } }); function fetchAutocomplete(query) { fetch('https://${siteId}.a.searchspring.io/api/search/autocomplete.json?siteId=${siteId}&resultsFormat=json&q=' + query + '&userId=user123&sessionId=session456&pageLoadId=page789&domain=https://yoursite.com') .then(response => response.json()) .then(data => displaySuggestions(data)) .catch(error => console.error('Autocomplete error:', error)); }`, useCases: [ "Search input suggestions", "Product finder autocomplete", "Category autocomplete", "Brand/attribute suggestions" ], bestPractices: [ "Use debouncing (300ms delay recommended)", "Start suggesting after 2+ characters", "Limit results to 8-10 suggestions", "Handle keyboard navigation (up/down arrows)", "Clear suggestions on blur or escape" ] }, suggest: { name: "Suggest API", description: "Spell correction and alternative search term suggestions", endpoint: `https://${siteId}.a.searchspring.io/api/suggest/query`, requiredParams: ["siteId"], optionalParams: ["q", "language", "suggestionCount", "productCount"], example: `function getSuggestions(query) { fetch('https://${siteId}.a.searchspring.io/api/suggest/query?siteId=${siteId}&q=' + query + '&language=en&suggestionCount=4') .then(response => response.json()) .then(data => { if (data.spellCorrection && data.spellCorrection.corrected) { showSpellCorrection(data.spellCorrection.corrected); } if (data.suggestions && data.suggestions.length > 0) { showAlternativeSuggestions(data.suggestions); } }) .catch(error => console.error('Suggest error:', error)); }`, useCases: [ "Spell correction for misspelled queries", "Alternative search suggestions", "No results page suggestions", "Query expansion" ], bestPractices: [ "Show spell corrections prominently", "Limit suggestions to 3-5 alternatives", "Use on no-results pages", "Consider user's language/locale" ] }, trending: { name: "Trending API", description: "Popular search terms and trending content", endpoint: `https://${siteId}.a.searchspring.io/api/suggest/trending`, requiredParams: ["siteId"], optionalParams: ["limit"], example: `function loadTrendingTerms() { fetch('https://${siteId}.a.searchspring.io/api/suggest/trending?siteId=${siteId}&limit=6') .then(response => response.json()) .then(data => { const container = document.getElementById('trending-terms'); data.terms.forEach(term => { const link = document.createElement('a'); link.href = '/search?q=' + encodeURIComponent(term.query); link.textContent = term.query; container.appendChild(link); }); }) .catch(error => console.error('Trending error:', error)); }`, useCases: [ "Homepage trending searches", "No results page alternatives", "Popular content widgets", "Search inspiration" ], bestPractices: [ "Update trending terms regularly", "Limit to 5-8 terms for better UX", "Use on homepage and no-results pages", "Make terms clickable for easy searching" ] }, recommendations: { name: "Recommendations API", description: "Personalized product recommendations", endpoint: `https://${siteId}.a.searchspring.io/boost/${siteId}/recommend`, requiredParams: ["tags"], optionalParams: ["products", "blockedItems", "categories", "brands", "shopper", "cart", "lastViewed", "limits", "filter.*"], example: `function getRecommendations() { const params = new URLSearchParams({ tags: 'similar-products,trending', products: 'PRODUCT-123', limits: '5,10', shopper: 'user123' }); fetch('https://${siteId}.a.searchspring.io/boost/${siteId}/recommend?' + params) .then(response => response.json()) .then(data => { data.profiles.forEach(profile => { console.log('Profile:', profile.tag); profile.results.forEach(product => { console.log(' -', product.title, product.price); }); }); }) .catch(error => console.error('Recommendations error:', error)); }`, useCases: [ "Product page cross-sells", "Homepage personalization", "Cart page upsells", "Related products" ], bestPractices: [ "Use multiple recommendation types (tags)", "Include personalization data (shopper, cart, lastViewed)", "Block out-of-stock or inappropriate items", "Set reasonable limits per profile" ] }, finder: { name: "Finder API", description: "Faceted search for building product finder interfaces - uses Search API with resultsPerPage=0", endpoint: `https://${siteId}.a.searchspring.io/api/search/search.json`, requiredParams: ["siteId", "resultsPerPage=0"], optionalParams: ["filter.*", "bgfilter.*", "includedFacets", "excludedFacets"], example: `function getFacets() { fetch('https://${siteId}.a.searchspring.io/api/search/search.json?siteId=${siteId}&resultsPerPage=0&filter.category=shoes') .then(response => response.json()) .then(data => { data.facets.forEach(facet => { console.log('Facet:', facet.label); facet.values.forEach(value => { console.log(' -', value.label, '(' + value.count + ')'); }); }); }) .catch(error => console.error('Finder error:', error)); }`, useCases: [ "Product finder widgets", "Advanced filtering interfaces", "Category navigation", "Faceted search" ], bestPractices: [ "Always set resultsPerPage=0 for facets-only", "Use includedFacets to limit returned facets", "Apply background filters for category pages", "Show facet counts for better UX" ] }, beacon: { name: "Beacon API", description: "Advanced event tracking for Personalized Recommendations - NOTE: For basic product tracking use IntelliSuggest ss.track.* methods", endpoint: `https://beacon.searchspring.io/api/event`, requiredParams: ["array of event objects with: category, context, event, id, type"], optionalParams: ["pid (for profile.product.* events)"], example: `function trackEvent(eventType, eventData) { const payload = { type: eventType, category: 'searchspring.recommendations.user-interactions', siteId: '${siteId}', id: 'event-' + Date.now(), userid: 'user123', sessionid: 'session456', data: eventData, context: { website: { trackingCode: '${siteId}' }, page: { url: window.location.href } } }; fetch('https://beacon.searchspring.io/api/event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }) .then(response => response.json()) .then(data => console.log('Event tracked:', data)) .catch(error => console.error('Tracking error:', error)); }`, useCases: [ "Recommendation impression tracking", "Click-through analytics", "User behavior analytics", "Custom event tracking" ], bestPractices: [ "Use consistent event categories", "Include user and session identifiers", "Batch events when possible", "Handle tracking failures gracefully", "Include relevant context data" ] }, "bulk-index": { name: "Bulk Indexing API", description: "Bulk product data indexing and management", endpoint: `https://index-api.searchspring.net/api/index/feed`, requiredParams: ["feedId"], optionalParams: ["requestedBy"], example: `// Platform-specific bulk indexing implementation // For cart platforms (Shopify, BigCommerce) - use PUT method function triggerCartPlatformIndex(feedId, secretKey) { // Authentication via URL (siteId:secretKey@host) const url = 'https://${siteId}:' + secretKey + '@index-api.searchspring.net/api/index/feed?feedId=' + feedId; fetch(url, { method: 'PUT' // Downloads feed from cart platform }) .then(response => response.json()) .then(data => { if (data.success) { console.log('Bulk index triggered successfully'); } }) .catch(error => console.error('Bulk index error:', error)); } // For custom feeds - use POST with file upload function uploadCustomFeed(feedId, secretKey, feedFile) { const formData = new FormData(); formData.append('feedFile', feedFile); const url = 'https://${siteId}:' + secretKey + '@index-api.searchspring.net/api/index/feed?feedId=' + feedId; fetch(url, { method: 'POST', body: formData // multipart/form-data }) .then(response => response.json()) .then(data => console.log('Feed uploaded:', data)) .catch(error => console.error('Upload error:', error)); }`, useCases: [ "Product catalog updates", "Inventory synchronization", "Scheduled data imports", "Content management integration" ], bestPractices: [ "Use PUT method for cart platforms (Shopify, BigCommerce, etc.)", "Use POST method for custom feed uploads", "Authentication: siteId:secretKey@ in URL (not headers)", "Check status endpoint before triggering new index", "Only one index per hour allowed", "Use multipart/form-data for POST requests" ] } }; const guide = apiGuides[api]; if (!guide) { throw new Error(`Unknown API: ${api}`); } return { content: [ { type: "text", text: this.addSiteIdNote(`# ${guide.name} Implementation Guide ## Overview ${guide.description} **API Endpoint**: ${guide.endpoint} ## Required Parameters ${guide.requiredParams.map(param => `- \`${param}\``).join('\n')} ## Optional Parameters ${guide.optionalParams.map(param => `- \`${param}\``).join('\n')} ## Implementation Example \`\`\`javascript ${guide.example} \`\`\` ## Common Use Cases ${guide.useCases.map(useCase => `- ${useCase}`).join('\n')} ## Best Practices ${guide.bestPractices.map(practice => `- ${practice}`).join('\n')} ## Documentation šŸ“– **Full API Documentation**: https://docs.searchspring.com/api/${api}/ šŸŽÆ **Help Center**: https://help.searchspring.net/ šŸ”§ **Implementation Guides**: https://help.searchspring.net/hc/en-us/sections/201185149`), }, ], }; } async getParameterGuide(params: ParameterGuideParams) { const { api, parameter } = params; const parameterGuides: Record<string, Record<string, any>> = { search: { q: { description: "Search query string - the terms users want to search for", type: "string", example: "running shoes", bestPractices: [ "URL encode special characters", "Trim whitespace before sending", "Consider query preprocessing (stemming, synonyms)", "Handle empty queries gracefully" ], useCases: ["Product search", "Site search", "Autocomplete"], relatedParams: ["redirectResponse", "landingPage"] }, filters: { description: "Apply filters to search results using filter.{field}=value format", type: "object", example: "filter.brand=Nike&filter.color=blue&filter.price=50-100", bestPractices: [ "Use exact field names from your product data", "For multiple values: filter.brand=Nike&filter.brand=Adidas", "For ranges: filter.price=min-max", "URL encode filter values" ], useCases: ["Category filtering", "Faceted navigation", "Price filtering"], relatedParams: ["bgfilters", "includedFacets", "excludedFacets"] }, bgfilters: { description: "Background filters applied permanently, not visible to users", type: "object", example: "bgfilter.status=active&bgfilter.visibility=public", bestPractices: [ "Use for system-level filtering (active products, visible items)", "Hide discontinued or out-of-stock products", "Apply site-specific rules", "Don't expose in filter UI" ], useCases: ["Product visibility", "Inventory filtering", "Site restrictions"], relatedParams: ["filters"] }, sort: { description: "Sort search results by field and direction", type: "object", example: "sort.price=asc&sort.popularity=desc", bestPractices: [ "Primary sort first, secondary sort second", "Use 'asc' for ascending, 'desc' for descending", "Common sorts: price, popularity, title, date", "Provide sort options to users" ], useCases: ["Price sorting", "Popularity sorting", "Name sorting", "Date sorting"], relatedParams: ["resultsPerPage", "page"] }, userId: { description: "Unique identifier for tracking user behavior - sourced from ssUserId cookie", type: "string", example: "value from ssUserId cookie", bestPractices: [ "Always get value from ssUserId cookie", "Automatically set by Searchspring tracking", "Do not manually generate - use cookie value", "Required for analytics and personalization" ], useCases: ["User tracking", "Personalization", "Analytics"], relatedParams: ["sessionId", "pageLoadId", "domain"] }, sessionId: { description: "Session identifier for tracking user journey - sourced from ssSessionIdNamespace cookie", type: "string", example: "value from ssSessionIdNamespace cookie", bestPractices: [ "Always get value from ssSessionIdNamespace cookie", "Automatically set by Searchspring tracking", "Do not manually generate - use cookie value", "Used for analytics and behavior tracking" ], useCases: ["Session tracking", "User journey analysis", "Analytics"], relatedParams: ["userId", "pageLoadId"] }, pageLoadId: { description: "Unique identifier for each page load/search - should be UUID v4", type: "string", example: "e560933a-b0fe-408d-8df5-807270e79fb8", bestPractices: [ "Generate new UUID v4 for each page load", "Update on every physical page load", "For SPA/headless: update on URL route change", "Critical for proper analytics tracking" ], useCases: ["Search tracking", "Click analytics", "Performance monitoring"], relatedParams: ["userId", "sessionId", "domain"] }, domain: { description: "Current page URL - should be window.location.href", type: "string", example: "https://yoursite.com/search?q=shoes", bestPractices: [ "Always use window.location.href value", "Include full URL with protocol and parameters", "Required for proper analytics and personalization", "Do not hardcode - get dynamically" ], useCases: ["Page tracking", "Analytics", "Personalization context"], relatedParams: ["userId", "sessionId", "pageLoadId"] } }, autocomplete: { q: { description: "Partial search query for autocomplete suggestions", type: "string", example: "runn (user typing 'running')", bestPractices: [ "Start suggestions after 2+ characters", "Use debouncing (50ms delay recommended by Searchspring)", "Handle special characters properly", "Clear suggestions when query is empty" ], useCases: ["Search input autocomplete", "Product suggestions", "Query completion"], relatedParams: ["resultsPerPage"] } }, recommendations: { tags: { description: "Recommendation profile tags/IDs that define recommendation types", type: "array", example: "['similar-products', 'trending', 'recently-viewed']", bestPractices: [ "Use meaningful profile names", "Configure profiles in Searchspring dashboard first", "Combine multiple recommendation types", "Order by priority/importance" ], useCases: ["Product recommendations", "Cross-selling", "Upselling", "Related products"], relatedParams: ["limits", "products", "shopper"] }, limits: { description: "Maximum number of products per recommendation profile", type: "array", example: "[5, 10, 3] (corresponds to tags array order)", bestPractices: [ "Match array length to tags array", "Consider page layout constraints", "Balance variety vs. performance", "Typical limits: 4-12 products" ], useCases: ["Controlling recommendation quantity", "Layout optimization"], relatedParams: ["tags"] }, shopper: { description: "Logged-in user identifier for personalization", type: "string", example: "user-12345", bestPractices: [ "Use only for authenticated users", "Consistent with your user system", "Improves recommendation quality", "Don't use for anonymous users" ], useCases: ["Personalized recommendations", "User-specific suggestions"], relatedParams: ["cart", "lastViewed"] } }, beacon: { type: { description: "Event type identifier for analytics tracking", type: "string", example: "profile.impression, profile.click, custom.event", bestPractices: [ "Use descriptive event names", "Follow consistent naming conventions", "Group related events with prefixes", "Document custom event types" ], useCases: ["Event categorization", "Analytics filtering", "Reporting"], relatedParams: ["category", "data"] }, category: { description: "Event category for grouping analytics events", type: "string", example: "searchspring.recommendations.user-interactions", bestPractices: [ "Use Searchspring standard categories when possible", "Create logical category hierarchies", "Document custom categories", "Keep category names consistent" ], useCases: ["Analytics grouping", "Event filtering", "Reporting dashboards"], relatedParams: ["type", "data"] }, data: { description: "Event payload containing specific event data", type: "object", example: "{profile: {tag: 'similar-products'}, product: {id: 'SKU123'}}", bestPractices: [ "Include relevant event context", "Keep payload size reasonable", "Use consistent data structure", "Validate data before sending" ], useCases: ["Event details", "Analytics context", "Personalization data"], relatedParams: ["type", "context"] } } }; const apiParams = parameterGuides[api]; if (!apiParams) { return { content: [ { type: "text", text: `Parameter guidance for ${api} API is not yet available. Use the searchspring_api_guide tool to get comprehensive API documentation.`, }, ], }; } const paramGuide = apiParams[parameter]; if (!paramGuide) { const availableParams = Object.keys(apiParams); return { content: [ { type: "text", text: `Parameter '${parameter}' not found for ${api} API. Available parameters for ${api}: ${availableParams.map(p => `- ${p}`).join('\n')} Use one of these parameter names to get detailed guidance.`, }, ], }; } return { content: [ { type: "text", text: `# ${parameter} Parameter Guide (${api.toUpperCase()} API) ## Description ${paramGuide.description} **Type**: ${paramGuide.type} ## Example Usage \`\`\` ${paramGuide.example} \`\`\` ## Best Practices ${paramGuide.bestPractices.map((practice: string) => `- ${practice}`).join('\n')} ## Common Use Cases ${paramGuide.useCases.map((useCase: string) => `- ${useCase}`).join('\n')} ## Related Parameters ${paramGuide.relatedParams.map((param: string) => `- \`${param}\``).join('\n')} ## Documentation šŸ“– **API Documentation**: https://docs.searchspring.com/api/${api}/ šŸŽÆ **Parameter Reference**: https://docs.searchspring.com/api/${api}/#parameters`, }, ], }; } async generateCode(params: CodeGeneratorParams) { const { api, platform, eventType, useCase } = params; // For backward compatibility, handle "tracking" as legacy alias const targetApi = api === "tracking" ? "beacon" : api; if (targetApi === "beacon" && eventType) { return this.generateTrackingCode(platform, eventType); } return this.generateApiCode(targetApi, platform, useCase); } private async generateApiCode(api: string, platform: string, useCase?: string) { const siteId = this.getSiteIdOrExample(); // Generate implementation code for specific APIs const codeTemplates: Record<string, Record<string, string>> = { search: { javascript: `// Search API implementation function searchProducts(query, filters = {}) { const params = new URLSearchParams({ siteId: '${siteId}', resultsFormat: 'json', q: query, userId: getUserId(), sessionId: getSessionId(), pageLoadId: getPageLoadId(), domain: window.location.origin }); // Add filters Object.entries(filters).forEach(([key, value]) => { params.append(\`filter.\${key}\`, value); }); fetch('https://${siteId}.a.searchspring.io/api/search/search.json?' + params) .then(response => response.json()) .then(data => { console.log('Total results:', data.pagination.totalResults); displaySearchResults(data.results); }) .catch(error => console.error('Search error:', error)); }`, shopify: `<!-- Shopify search implementation --> <form action="/search" method="get"> <input type="text" name="q" placeholder="Search products..." /> <button type="submit">Search</button> </form> <script> // Enhanced search with Searchspring API document.querySelector('form').addEventListener('submit', function(e) { e.preventDefault(); const query = this.querySelector('input[name="q"]').value; fetch('https://${siteId}.a.searchspring.io/api/search/search.json?siteId=${siteId}&resultsFormat=json&q=' + query + '&userId={{ customer.id | default: "anonymous" }}&sessionId={{ session.id }}&domain={{ shop.permanent_domain }}') .then(response => response.json()) .then(data => displayResults(data)) .catch(error => console.error('Search error:', error)); }); </script>` }, autocomplete: { javascript: `// Autocomplete implementation const searchInput = document.getElementById('search'); let debounceTimer; searchInput.addEventListener('input', function(e) { clearTimeout(debounceTimer); const query = e.target.value; if (query.length < 2) return; debounceTimer = setTimeout(() => { const params = new URLSearchParams({ siteId: '${siteId}', resultsFormat: 'json', q: query, userId: getUserId(), sessionId: getSessionId() }); fetch('https://${siteId}.a.searchspring.io/api/search/autocomplete.json?' + params) .then(response => response.json()) .then(data => displayAutocomplete(data.suggestions)) .catch(error => console.error('Autocomplete error:', error)); }, 300); });`, shopify: `<!-- Shopify autocomplete implementation --> <div class="search-autocomplete"> <input type="text" id="search-input" placeholder="Search products..." /> <div id="autocomplete-results"></div> </div> <script> const searchInput = document.getElementById('search-input'); const resultsDiv = document.getElementById('autocomplete-results'); let debounceTimer; searchInput.addEventListener('input', function(e) { clearTimeout(debounceTimer); const query = e.target.value; if (query.length < 2) { resultsDiv.innerHTML = ''; return; } debounceTimer = setTimeout(() => { fetch('https://${siteId}.a.searchspring.io/api/search/autocomplete.json?siteId=${siteId}&resultsFormat=json&q=' + query + '&userId={{ customer.id | default: "anonymous" }}&sessionId={{ session.id }}') .then(response => response.json()) .then(data => { resultsDiv.innerHTML = data.suggestions.map(s => '<div class="suggestion" onclick="selectSuggestion("' + s.text + '")">' + s.text + '</div>' ).join(''); }) .catch(error => console.error('Autocomplete error:', error)); }, 300); }); </script>` }, suggest: { javascript: `// Suggest API implementation function getSuggestions(query) { const params = new URLSearchParams({ siteId: '${siteId}', resultsFormat: 'json', q: query, userId: getUserId(), sessionId: getSessionId() }); fetch('https://${siteId}.a.searchspring.io/api/suggest/query?' + params) .then(response => response.json()) .then(data => { if (data.suggested) { console.log('Did you mean:', data.suggested); displaySuggestion(data.suggested); } }) .catch(error => console.error('Suggest error:', error)); }`, shopify: `<!-- Shopify spell check implementation --> <script> function checkSpelling(query) { fetch('https://${siteId}.a.searchspring.io/api/suggest/query?siteId=${siteId}&resultsFormat=json&q=' + query + '&userId={{ customer.id | default: "anonymous" }}&sessionId={{ session.id }}') .then(response => response.json()) .then(data => { if (data.suggested && data.suggested !== query) { document.getElementById('suggestion').innerHTML = 'Did you mean: <a href="/search?q=' + data.suggested + '">' + data.suggested + '</a>?'; } }); } </script>` }, trending: { javascript: `// Trending API implementation function getTrendingSearches() { const params = new URLSearchParams({ siteId: '${siteId}', resultsFormat: 'json', userId: getUserId(), sessionId: getSessionId() }); fetch('https://${siteId}.a.searchspring.io/api/search/trending.json?' + params) .then(response => response.json()) .then(data => { console.log('Trending searches:', data.trending); displayTrendingSearches(data.trending); }) .catch(error => console.error('Trending error:', error)); }`, shopify: `<!-- Shopify trending searches implementation --> <div class="trending-searches"> <h3>Trending Searches</h3> <div id="trending-list"></div> </div> <script> fetch('https://${siteId}.a.searchspring.io/api/search/trending.json?siteId=${siteId}&resultsFormat=json&userId={{ customer.id | default: "anonymous" }}&sessionId={{ session.id }}') .then(response => response.json()) .then(data => { const trendingList = document.getElementById('trending-list'); trendingList.innerHTML = data.trending.map(term => '<a href="/search?q=' + term + '" class="trending-term">' + term + '</a>' ).join(''); }) .catch(error => console.error('Trending error:', error)); </script>` }, recommendations: { javascript: `// Recommendations API implementation function getRecommendations(options = {}) { const params = new URLSearchParams({ siteId: '${siteId}', resultsFormat: 'json', userId: getUserId(), sessionId: getSessionId(), pageLoadId: getPageLoadId(), domain: window.location.origin, ...options }); fetch('https://${siteId}.a.searchspring.io/api/recommend/recommend.json?' + params) .then(response => response.json()) .then(data => { console.log('Recommendations:', data.results); displayRecommendations(data.results); }) .catch(error => console.error('Recommendations error:', error)); }`, shopify: `<!-- Shopify recommendations implementation --> <div class="product-recommendations"> <h3>Recommended Products</h3> <div id="recommendations-grid"></div> </div> <script> fetch('https://${siteId}.a.searchspring.io/api/recommend/recommend.json?siteId=${siteId}&resultsFormat=json&userId={{ customer.id | default: "anonymous" }}&sessionId={{ session.id }}&domain={{ shop.permanent_domain }}') .then(response => response.json()) .then(data => { const grid = document.getElementById('recommendations-grid'); grid.innerHTML = data.results.map(product => '<div class="product-card"><img src="' + product.imageUrl + '"><h4>' + product.name + '</h4><p>$' + product.price + '</p></div>' ).join(''); }) .catch(error => console.error('Recommendations error:', error)); </script>` }, finder: { javascript: `// Finder API implementation function buildProductFinder() { const params = new URLSearchParams({ siteId: '${siteId}', resultsFormat: 'json', userId: getUserId(), sessionId: getSessionId() }); fetch('https://${siteId}.a.searchspring.io/api/search/search.json?resultsPerPage=0&' + params) .then(response => response.json()) .then(data => { console.log('Finder facets:', data.facets); buildFinderInterface(data.facets); }) .catch(error => console.error('Finder error:', error)); }`, shopify: `<!-- Shopify product finder implementation --> <div class="product-finder"> <h3>Find Your Perfect Product</h3> <div id="finder-facets"></div> <div id="finder-results"></div> </div> <script> fetch('https://${siteId}.a.searchspring.io/api/search/search.json?siteId=${siteId}&resultsPerPage=0&resultsFormat=json&userId={{ customer.id | default: "anonymous" }}&sessionId={{ session.id }}') .then(response => response.json()) .then(data => { const facetsDiv = document.getElementById('finder-facets'); facetsDiv.innerHTML = data.facets.map(facet => '<div class="facet"><label>' + facet.label + '</label><select data-facet="' + facet.field + '">' + facet.values.map(v => '<option value="' + v.value + '">' + v.label + '</option>').join('') + '</select></div>' ).join(''); }) .catch(error => console.error('Finder error:', error)); </script>` }, beacon: { javascript: `// Beacon API for Personalized Recommendations tracking // NOTE: This is for recommendations beacon tracking, not basic product tracking // For basic IntelliSuggest tracking use ss.track.* methods instead function trackRecommendationEvent(events) { // Events should be an array of event objects fetch('https://beacon.searchspring.io/api/event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(events) }) .then(response => response.json()) .then(data => console.log('Recommendation events tracked:', data)) .catch(error => console.error('Beacon error:', error)); } // Example: Track profile render event function trackProfileRender(profileId, tag, placement) { const events = [{ category: "searchspring.recommendations.user-interactions", context: { pageLoadId: getPageLoadId(), userId: getUserId(), sessionId: getSessionId(), website: { trackingCode: '${siteId}' } }, event: { context: { type: "product-recommendation", tag: tag, placement: placement }, profile: { tag: tag, placement: placement, seed: [""] } }, id: profileId, type: "profile.render" }]; trackRecommendationEvent(events); }`, shopify: `<!-- Shopify beacon tracking for Personalized Recommendations --> <!-- NOTE: For basic product tracking, use IntelliSuggest ss.track.* methods instead --> <script> function trackRecommendationEvents(events) { fetch('https://beacon.searchspring.io/api/event', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(events) }) .then(response => response.json()) .then(result => console.log('Recommendation events tracked:', result)) .catch(error => console.error('Beacon error:', error)); } // Example: Track recommendation profile impression function trackProfileImpression(profileId, tag, placement) { const events = [{ category: "searchspring.recommendations.user-interactions", context: { pageLoadId: generateUUID(), userId: '{{ customer.id | default: "anonymous" }}', sessionId: '{{ session.id }}', website: { trackingCode: '${siteId}' } }, event: { context: { type: "product-recommendation", tag: tag, placement: placement }, profile: { tag: tag, placement: placement, seed: [""] } }, id: profileId, type: "profile.impression" }]; trackRecommendationEvents(events); } </script>` }, "bulk-index": { javascript: `// Bulk Index API implementation // NOTE: Use PUT for cart platforms (Shopify, BigCommerce), POST for custom feeds // For cart platforms (Shopify, BigCommerce, etc.) - triggers feed download function triggerBulkIndex(feedId, secretKey) { const url = 'https://${siteId}:' + secretKey + '@index-api.searchspring.net/api/index/feed?feedId=' + feedId; fetch(url, { method: 'PUT' }) .then(response => response.json()) .then(data => { console.log('Bulk index triggered:', data); if (data.success) { console.log('Index successfully queued'); checkIndexStatus(); } }) .catch(error => console.error('Bulk index error:', error)); } // For custom feeds - uploads feed file function uploadCustomFeed(feedId, secretKey, feedFile) { const formData = new FormData(); formData.append('feedFile', feedFile); const url = 'https://${siteId}:' + secretKey + '@index-api.searchspring.net/api/index/feed?feedId=' + feedId; fetch(url, { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { console.log('Feed uploaded:', data); if (data.success) { console.log('Index successfully queued'); } }) .catch(error => console.error('Feed upload error:', error)); }`, shopify: `<!-- Shopify Bulk Index implementation --> <!-- Use PUT method for cart platforms to trigger feed download --> <script> function triggerShopifyIndex(feedId, secretKey) { const url = 'https://${siteId}:' + secretKey + '@index-api.searchspring.net/api/index/feed?feedId=' + feedId; fetch(url, { method: 'PUT' }) .then(response => response.json()) .then(data => { if (data.success) { console.log('Shopify feed index triggered successfully'); } }) .catch(error => console.error('Shopify index error:', error)); } </script>`, php: `<?php // PHP Bulk Index API implementation // For cart platforms - use PUT to trigger feed download function triggerBulkIndex($feedId, $secretKey) { $url = 'https://${siteId}:' . $secretKey . '@index-api.searchspring.net/api/index/feed?feedId=' . $feedId; $ch = curl_init($url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { return json_decode($response, true); } else { throw new Exception('Bulk index failed with HTTP code: ' . $httpCode); } } // For custom feeds - use POST with multipart/form-data function uploadCustomFeed($feedId, $secretKey, $feedFilePath) { $url = 'https://${siteId}:' . $secretKey . '@index-api.searchspring.net/api/index/feed?feedId=' . $feedId; $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, [ 'feedFile' => new CURLFile($feedFilePath) ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); $response = curl_exec($ch); curl_close($ch); return json_decode($response, true); } ?>` } }; const apiCode = codeTemplates[api]?.[platform] || codeTemplates[api]?.javascript; if (!apiCode) { return { content: [{ type: "text", text: `Code generation for ${api} API on ${platform} platform is not yet available. Use the searchspring_api_guide tool to get implementation guidance.` }] }; } return { content: [{ type: "text", text: `# ${api.toUpperCase()} API Code for ${platform.toUpperCase()} ${useCase ? `**Use Case**: ${useCase}\n\n` : ''}**Generated Implementation:** \`\`\`${platform === 'javascript' ? 'javascript' : 'html'} ${apiCode} \`\`\` **Next Steps:** 1. Customize the code for your specific needs 2. Add error handling and user feedback 3. Test the implementation thoroughly 4. Use the code validator tool to check your implementation **Documentation**: https://docs.searchspring.com/api/${api}/` }] }; } private async generateTrackingCode(platform: string, eventType: string) { const siteId = this.getSiteIdOrExample(); const sku = "PRODUCT_SKU"; const price = 99.99; const quantity = 1; const implementations: Record<string, Record<string, string>> = { shopify: { product: `<!-- Add to product page template --> <script> if (typeof ss != 'undefined') { ss.track.product.view({ sku: '{{ product.variants.first.sku }}', name: '{{ product.title }}', price: {{ product.price | money_without_currency }} }); } </script>`, cart: `<!-- Add to cart page or add-to-cart action --> <script> if (typeof ss != 'undefined') { {% for item in cart.items %} ss.track.cart.add({ sku: '{{ item.sku }}', name: '{{ item.product.title }}', price: {{ item.price | money_without_currency }}, quantity: {{ item.quantity }} }); {% endfor %} } </script>`, sale: `<!-- IMPORTANT: Shopify Checkout Extensibility Notice --> <!-- For modern Shopify stores with checkout extensibility, sales tracking --> <!-- must be implemented via Shopify Web Pixel apps, not thank you page code --> <!-- See: https://help.searchspring.net/hc/en-us/articles/24882106349467 --> <!-- Traditional method (for older Shopify stores): --> <!-- Add to order-status-url (thank you page) template --> <script> if (typeof ss != 'undefined') { {% for line_item in order.line_items %} ss.track.purchase.buy({ sku: '{{ line_item.sku }}', name: '{{ line_item.title }}', price: {{ line_item.price | money_without_currency }}, quantity: {{ line_item.quantity }} }); {% endfor %} } </script> <!-- Modern Shopify (Checkout Extensibility) Method: --> <!-- This requires a Shopify Web Pixel app implementation --> <!-- Contact Searchspring support for Web Pixel app setup --> <!-- Web Pixel apps handle checkout-complete events automatically -->` }, bigcommerce: { product: `<!-- Add to product page template (BigCommerce Stencil) --> <script> if (typeof ss != 'undefined') { ss.track.product.view({ sku: '{{product.sku}}', name: '{{product.title}}', price: {{product.price.without_tax.value}} }); } </script>`, cart: `<!-- Add to cart page template --> <script> if (typeof ss != 'undefined') { {{#each cart.items}} ss.track.cart.add({ sku: '{{sku}}', name: '{{name}}', price: {{price.value}}, quantity: {{quantity}} }); {{/each}} } </script>`, sale: `<!-- Add to order confirmation page --> <script> if (typeof ss != 'undefined') { {{#each order.products}} ss.track.purchase.buy({ sku: '{{sku}}', name: '{{name}}', price: {{price_ex_tax}}, quantity: {{quantity}} }); {{/each}} } </script>` }, magento2: { product: `<!-- Add to product page template (Magento 2) --> <script> if (typeof ss != 'undefined') { ss.track.product.view({ sku: '<?= $block->escapeHtml($_product->getSku()) ?>', name: '<?= $block->escapeHtml($_product->getName()) ?>', price: <?= $_product->getFinalPrice() ?> }); } </script>`, cart: `<!-- Add to cart page template --> <script> if (typeof ss != 'undefined') { <?php foreach ($block->getItems() as $item): ?> ss.track.cart.add({ sku: '<?= $block->escapeHtml($item->getSku()) ?>', name: '<?= $block->escapeHtml($item->getName()) ?>', price: <?= $item->getPrice() ?>, quantity: <?= $item->getQty() ?> }); <?php endforeach; ?> } </script>`, sale: `<!-- Add to success page template --> <script> if (typeof ss != 'undefined') { <?php foreach ($block->getOrderItems() as $item): ?> ss.track.purchase.buy({ sku: '<?= $block->escapeHtml($item->getSku()) ?>', name: '<?= $block->escapeHtml($item->getName()) ?>', price: <?= $item->getPrice() ?>, quantity: <?= $item->getQtyOrdered() ?> }); <?php endforeach; ?> } </script>` }, custom: { product: `<!-- Generic implementation for product view --> <script> // Ensure IntelliSuggest is loaded if (typeof ss != 'undefined') { ss.track.product.view({ sku: '${sku}', name: 'Product Name', price: ${price} }); } </script>`, cart: `<!-- Generic implementation for cart/add to cart --> <script> if (typeof ss != 'undefined') { ss.track.cart.add({ sku: '${sku}', name: 'Product Name', price: ${price}, quantity: ${quantity} }); } </script>`, sale: `<!-- Generic implementation for purchase --> <script> if (typeof ss != 'undefined') { ss.track.purchase.buy({ sku: '${sku}', name: 'Product Name', price: ${price}, quantity: ${quantity} }); } </script>` } }; const platformCode = implementations[platform] || implementations.custom; const code = platformCode?.[eventType] || platformCode?.product || implementations.custom?.product || 'Code generation not available'; const documentationUrls: Record<string, string> = { shopify: "https://help.searchspring.net/hc/en-us/articles/206972376-IntelliSuggest-Tracking-in-Shopify", custom: "https://help.searchspring.net/hc/en-us/articles/201185129-Adding-IntelliSuggest-Tracking" }; return { content: [ { type: "text", text: `IntelliSuggest Tracking Implementation for ${platform.toUpperCase()} - ${eventType} event: IMPORTANT: Include IntelliSuggest script first: <script src="//cdn.searchspring.net/intellisuggest/is.min.js"></script> Implementation Code: ${code} Documentation: ${documentationUrls[platform] || documentationUrls.custom} Requirements: - _isuid cookie must be set - IntelliSuggest script must NOT have defer/async attributes - SKU values must match Searchspring indexed product SKU core field`, }, ], }; } async validateCode(params: CodeValidationParams) { const { code, codeType, platform, issue } = params; const siteId = this.getSiteIdOrExample(); const validationResults: string[] = []; const warnings: string[] = []; const suggestions: string[] = []; // Common validation checks if (codeType === "tracking" || codeType === "beacon") { // Check for IntelliSuggest script inclusion if (!code.includes("cdn.searchspring.net/intellisuggest") && !code.includes("is.min.js")) { validationResults.push("āŒ Missing IntelliSuggest script: <script src='//cdn.searchspring.net/intellisuggest/is.min.js'></script>"); } else { validationResults.push("āœ… IntelliSuggest script inclusion detected"); } // Check for ss.track usage if (!code.includes("ss.track")) { validationResults.push("āŒ No tracking calls found (ss.track.product.view, ss.track.cart.add, ss.track.purchase.buy)"); } else { validationResults.push("āœ… Tracking calls detected"); } // Check for typeof ss check if (!code.includes("typeof ss")) { warnings.push("āš ļø Consider adding safety check: if (typeof ss != 'undefined')"); } // Check for required tracking fields if (code.includes("ss.track.product") && !code.includes("sku")) { validationResults.push("āŒ Product tracking missing required 'sku' field"); } if (code.includes("ss.track.cart") && (!code.includes("sku") || !code.includes("quantity"))) { validationResults.push("āŒ Cart tracking missing required 'sku' and/or 'quantity' fields"); } if (code.includes("ss.track.purchase") && (!code.includes("sku") || !code.includes("quantity"))) { validationResults.push("āŒ Purchase tracking missing required 'sku' and/or 'quantity' fields"); } } if (codeType === "search" || codeType === "autocomplete" || codeType === "suggest" || codeType === "trending" || codeType === "finder" || codeType === "recommendations") { // Check for API endpoint if (!code.includes(".a.searchspring.io")) { validationResults.push("āŒ Missing Searchspring API endpoint (should include .a.searchspring.io)"); } else { validationResults.push("āœ… Searchspring API endpoint detected"); } // Check for required parameters if (!code.includes("siteId")) { validationResults.push("āŒ Missing required 'siteId' parameter"); } else { validationResults.push("āœ… siteId parameter detected"); } if (!code.includes("userId") && !code.includes("sessionId")) { warnings.push("āš ļø Missing tracking parameters (userId, sessionId) - these are required for analytics"); } // API-specific validations if (codeType === "search" && !code.includes("q=")) { validationResults.push("āŒ Search API missing query parameter 'q'"); } if (codeType === "autocomplete" && !code.includes("debounce")) { warnings.push("āš ļø Consider implementing debouncing for autocomplete to reduce API calls"); } if (codeType === "recommendations" && !code.includes("pageLoadId")) { warnings.push("āš ļø Recommendations work better with pageLoadId parameter"); } // Check for error handling if (!code.includes(".catch") && !code.includes("try")) { warnings.push("āš ļø No error handling detected - consider adding .catch() or try/catch"); } } if (codeType === "beacon") { // Check for beacon endpoint if (!code.includes("beacon.searchspring.io")) { validationResults.push("āŒ Missing Beacon API endpoint (should be beacon.searchspring.io)"); } else { validationResults.push("āœ… Beacon API endpoint detected"); } // Check for POST method if (!code.includes("POST")) { validationResults.push("āŒ Beacon API requires POST method"); } // Check for required beacon fields if (!code.includes("type")) { validationResults.push("āŒ Beacon tracking missing required 'type' field"); } } if (codeType === "bulk-index") { // Check for bulk index endpoint if (!code.includes("index-api.searchspring.net")) { validationResults.push("āŒ Missing Bulk Index API endpoint (should be index-api.searchspring.net)"); } else { validationResults.push("āœ… Bulk Index API endpoint detected"); } // Check for secret key if (!code.includes("secretKey")) { validationResults.push("āŒ Bulk Index API requires 'secretKey' parameter"); } // Check for products array if (!code.includes("products")) { validationResults.push("āŒ Bulk Index API missing 'products' array"); } } // Platform-specific checks if (platform === "shopify") { if (codeType === "tracking" && !code.includes("{{")) { warnings.push("āš ļø No Liquid template variables detected - make sure you're using Shopify's template syntax"); } if (code.includes("product.variants.first.sku") && !code.includes("product.selected_or_first_available_variant.sku")) { suggestions.push("šŸ’” Consider using product.selected_or_first_available_variant.sku for better variant handling"); } // Check for modern Shopify checkout extensibility if (codeType === "tracking" && code.includes("ss.track.purchase.buy") && code.includes("order.line_items")) { warnings.push("āš ļø IMPORTANT: For Shopify stores with checkout extensibility, sales tracking must use Web Pixel apps instead of thank you page code"); suggestions.push("šŸ’” Modern Shopify: Contact Searchspring support for Web Pixel app setup (https://help.searchspring.net/hc/en-us/articles/24882106349467)"); } } if (platform === "bigcommerce") { if (codeType === "tracking" && !code.includes("{{#") && !code.includes("{{")) { warnings.push("āš ļø No Handlebars template syntax detected - make sure you're using BigCommerce's Stencil template syntax"); } if (code.includes("product.sku") && !code.includes("{{product.sku}}")) { suggestions.push("šŸ’” Use {{product.sku}} for BigCommerce Stencil template syntax"); } } if (platform === "magento2") { if (codeType === "tracking" && !code.includes("<?=")) { warnings.push("āš ļø No PHP template syntax detected - make sure you're using Magento's .phtml syntax"); } if (code.includes("$_product") && !code.includes("escapeHtml")) { suggestions.push("šŸ’” Use $block->escapeHtml() for proper data escaping in Magento 2"); } } // Issue-specific troubleshooting let troubleshooting = ""; if (issue) { troubleshooting = ` Troubleshooting for: "${issue}" `; if (issue.toLowerCase().includes("not working") || issue.toLowerCase().includes("not tracking")) { troubleshooting += `Common causes: - IntelliSuggest script not loaded or blocked by ad blockers - Script placed in wrong location (should be in <head> or before tracking calls) - Missing _isuid cookie (check browser dev tools > Application > Cookies) - SKU values don't match Searchspring indexed product SKU field - Browser console errors preventing script execution - SHOPIFY SPECIFIC: Modern Shopify stores with checkout extensibility require Web Pixel apps for sales tracking `; } if (issue.toLowerCase().includes("undefined") || issue.toLowerCase().includes("is not defined")) { troubleshooting += `Script loading issue: - Ensure IntelliSuggest script loads before your tracking code - Remove async/defer attributes from the IntelliSuggest script tag - Check browser console for script loading errors `; } if (issue.toLowerCase().includes("shopify") && issue.toLowerCase().includes("sales")) { troubleshooting += `Shopify sales tracking issues: - Modern Shopify (checkout extensibility): Use Web Pixel apps instead of thank you page - Traditional Shopify: Add code to order-status-url template - Contact Searchspring support for Web Pixel app setup - Verify _isuid cookie is being set correctly - Documentation: https://help.searchspring.net/hc/en-us/articles/24882106349467 `; } if (issue.toLowerCase().includes("search") || issue.toLowerCase().includes("results")) { troubleshooting += `API integration issues: - Verify siteId is correct: ${siteId} - Check CORS settings if calling from browser - Ensure all required parameters are included - Check network tab for API response errors `; } } const summary = `Code Validation Results for ${codeType.toUpperCase()} implementation${platform ? ` on ${platform.toUpperCase()}` : ''} ${validationResults.join('\n')} ${warnings.length > 0 ? `Warnings:\n${warnings.join('\n')}\n` : ''}${suggestions.length > 0 ? `Suggestions:\n${suggestions.join('\n')}\n` : ''}${troubleshooting} For additional support: - Documentation: https://docs.searchspring.com/ - Support: https://help.searchspring.net/ - Implementation guides: https://help.searchspring.net/hc/en-us/sections/201185149`; return { content: [ { type: "text", text: summary, }, ], }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yangjeep/playground-searchspring-api-assist-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server