Skip to main content

Yext Categories Service

๐Ÿ“– Overviewโ€‹

The Yext Categories service retrieves available business categories from Yext's category taxonomy. Categories are used to classify business entities (locations) and determine which publisher directories are appropriate for listing distribution. Proper category selection ensures listings appear in relevant directories.

Source File: external/Integrations/Yext/services/categories.service.js
External API: Yext Categories API
Primary Use: List available business categories for entity classification

๐Ÿ—„๏ธ Collections Usedโ€‹

accountโ€‹

  • Operations: Read (via account service)
  • Model: external/models/account.js
  • Usage Context: Verify account has entity initialized before API call

๐Ÿ”„ Data Flowโ€‹

sequenceDiagram
participant Client
participant CategoriesService
participant AccountService
participant AccountDB
participant YextAPI

Client->>CategoriesService: list(accountId, query)
CategoriesService->>AccountService: find(accountId)
AccountService->>AccountDB: Check entity initialization

alt Entity initialized
AccountDB-->>AccountService: Return entity ID
AccountService-->>CategoriesService: Entity ID
CategoriesService->>YextAPI: GET /categories
YextAPI-->>CategoriesService: Categories list
CategoriesService-->>Client: Return categories
else Not initialized
AccountDB-->>AccountService: Error object
AccountService-->>CategoriesService: Error
CategoriesService-->>Client: 404 Not Found
end

style CategoriesService fill:#e3f2fd
style YextAPI fill:#fff4e6

๐Ÿ”ง Business Logic & Functionsโ€‹

list({ query, accountId })โ€‹

Purpose: Retrieve list of available business categories from Yext

Source: services/categories.service.js

External API Endpoint: GET https://api.yext.com/v2/categories

Parameters:

  • query (Object) - Query parameters
    • language (String, optional) - Language code (e.g., 'en', 'es')
    • country (String, optional) - Country code (e.g., 'US', 'CA')
  • accountId (ObjectId) - DashClicks account ID

Returns: Promise<Object> - Categories data from Yext

Business Logic Flow:

  1. Verify Account Initialization

    const account = await accountService.find(accountId);

    if (account?.error) {
    throw notFound('Record not found.');
    }
  2. Fetch Categories from Yext

    const v = process.env.YEXT_API_VPARAM || '20200525';
    const url = 'https://api.yext.com/v2/categories?' + querystring.encode({ v: v });

    const options = {
    method: 'GET',
    headers: { 'api-key': process.env.YEXT_API_KEYS },
    params: query,
    url,
    };

    const dns = await axios(options);
    return dns.data;

API Request Example:

GET https://api.yext.com/v2/categories?v=20200525&language=en&country=US
Headers:
api-key: {YEXT_API_KEY}

API Response Example:

{
"meta": {
"uuid": "abc-def-123",
"errors": []
},
"response": {
"categories": [
{
"id": "8040",
"name": "Restaurant",
"fullName": "Food & Beverage > Restaurant",
"parent": "1000",
"shortName": "Restaurant"
},
{
"id": "1234",
"name": "Coffee Shop",
"fullName": "Food & Beverage > Coffee & Tea > Coffee Shop",
"parent": "8040",
"shortName": "Coffee Shop"
},
{
"id": "5678",
"name": "Plumber",
"fullName": "Home Services > Plumbing > Plumber",
"parent": "5000",
"shortName": "Plumber"
},
{
"id": "9101",
"name": "Law Firm",
"fullName": "Professional Services > Legal > Law Firm",
"parent": "9000",
"shortName": "Law Firm"
}
// ... hundreds more categories
]
}
}

Category Structure:

  • id - Unique category identifier (used in entity creation)
  • name - Display name
  • fullName - Full hierarchical path
  • parent - Parent category ID (for hierarchical navigation)
  • shortName - Abbreviated name

Error Handling:

  • Account Not Initialized: Throws 404 Not Found with "Record not found."
  • API Errors: Yext API errors propagate up

Example Usage:

// Get all categories
const categories = await categoriesService.list({
accountId: '507f1f77bcf86cd799439011',
query: {
language: 'en',
country: 'US',
v: '20200525',
},
});

console.log('Total categories:', categories.response.categories.length);

// Find restaurant categories
const restaurants = categories.response.categories.filter(cat =>
cat.fullName.includes('Restaurant'),
);

console.log('Restaurant categories:', restaurants);

Use in Entity Creation:

// 1. Get categories
const categories = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});

// 2. Find appropriate category
const coffeeCat = categories.response.categories.find((cat) => cat.name === 'Coffee Shop');

// 3. Create entity with category
const entity = await entitiesService.create({
accountId: accountId,
body: {
name: 'My Coffee Shop',
categoryIds: [coffeeCat.id], // Use category ID
address: {...},
phone: '...'
},
query: { v: '20200525' }
});

Query Parameters:

ParameterTypeDescriptionRequired
languageStringLanguage code (e.g., 'en', 'es', 'fr')โŒ
countryStringCountry code (e.g., 'US', 'CA', 'GB')โŒ
vStringAPI version (default: from env variable)โœ…

Key Business Rules:

  • Account Verification: Requires account to have entity initialized
  • Hierarchical Structure: Categories organized in parent-child hierarchy
  • Localization: Supports multiple languages and countries
  • Static Data: Categories rarely change, suitable for caching
  • Multiple Categories: Entities can have multiple category IDs

๐Ÿ”€ Integration Pointsโ€‹

Entity Creation Workflowโ€‹

// Complete workflow: Get categories โ†’ Select category โ†’ Create entity
async function createBusinessListing(accountId, businessData) {
// 1. Get available categories
const categoriesResponse = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});

// 2. Find appropriate category (e.g., by industry)
const category = categoriesResponse.response.categories.find(cat => {
return cat.name.toLowerCase().includes(businessData.industry.toLowerCase());
});

if (!category) {
throw new Error('No matching category found for industry: ' + businessData.industry);
}

// 3. Create entity with category
const entity = await entitiesService.create({
accountId: accountId,
body: {
name: businessData.name,
categoryIds: [category.id],
address: businessData.address,
phone: businessData.phone,
websiteUrl: businessData.website,
},
query: { v: '20200525' },
});

return {
entity: entity,
category: category,
};
}

Category Search Helperโ€‹

// Helper function to search categories
function searchCategories(categories, searchTerm) {
const term = searchTerm.toLowerCase();

return categories.filter(cat => {
return (
cat.name.toLowerCase().includes(term) ||
cat.fullName.toLowerCase().includes(term) ||
cat.shortName.toLowerCase().includes(term)
);
});
}

// Usage
const categories = await categoriesService.list({
accountId: accountId,
query: { v: '20200525' },
});

const plumberCategories = searchCategories(categories.response.categories, 'plumber');
console.log('Plumber categories:', plumberCategories);

Category Hierarchy Builderโ€‹

// Build category tree from flat list
function buildCategoryTree(categories) {
const categoryMap = {};
const roots = [];

// First pass: create map
categories.forEach(cat => {
categoryMap[cat.id] = { ...cat, children: [] };
});

// Second pass: build tree
categories.forEach(cat => {
if (cat.parent && categoryMap[cat.parent]) {
categoryMap[cat.parent].children.push(categoryMap[cat.id]);
} else {
roots.push(categoryMap[cat.id]);
}
});

return roots;
}

// Usage
const categories = await categoriesService.list({
accountId: accountId,
query: { v: '20200525' },
});

const tree = buildCategoryTree(categories.response.categories);
console.log('Category tree:', JSON.stringify(tree, null, 2));

๐Ÿงช Edge Cases & Special Handlingโ€‹

Account Not Initializedโ€‹

Issue: Account doesn't have Yext entity

Handling: Throws 404 Not Found

const account = await accountService.find(accountId);
if (account?.error) {
throw notFound('Record not found.');
}

Solution: Create entity first using entitiesService.create()

Large Category Listโ€‹

Issue: Yext returns hundreds of categories (can be 500+)

Handling: No pagination, returns all categories

Recommendation: Cache categories in application or client-side

Example Caching:

let categoriesCache = null;
let cacheTimestamp = null;
const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

async function getCategoriesWithCache(accountId) {
const now = Date.now();

if (categoriesCache && cacheTimestamp && now - cacheTimestamp < CACHE_TTL) {
return categoriesCache;
}

const categories = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});

categoriesCache = categories;
cacheTimestamp = now;

return categories;
}

Missing Parent Categoriesโ€‹

Issue: Category references parent that doesn't exist in list

Handling: No validation, parent relationship may be broken

Prevention: Use fullName for display instead of building tree

Localizationโ€‹

Issue: Category names vary by language/country

Handling: Pass appropriate language and country codes

Example:

// English categories
const enCategories = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});

// Spanish categories
const esCategories = await categoriesService.list({
accountId: accountId,
query: { language: 'es', country: 'US', v: '20200525' },
});

โš ๏ธ Important Notesโ€‹

  1. Read-Only: This service only retrieves categories, doesn't create/modify
  2. Account Required: Must have entity initialized even though not used
  3. Static Data: Categories rarely change, suitable for aggressive caching
  4. Hierarchical: Categories have parent-child relationships via parent field
  5. Use fullName: fullName provides complete category path
  6. Multiple Languages: Support for localized category names
  7. No Pagination: Returns all categories in single response
  8. Large Response: Can return 500+ categories
  9. Category IDs: Use id field when creating entities
  10. Search: Consider implementing client-side search for better UX

๐ŸŽฏ Common Use Casesโ€‹

1. Populate Category Dropdownโ€‹

// Get categories for UI dropdown
const categories = await categoriesService.list({
accountId: accountId,
query: { language: 'en', country: 'US', v: '20200525' },
});

// Format for dropdown
const options = categories.response.categories.map(cat => ({
value: cat.id,
label: cat.fullName,
}));

2. Category Autocompleteโ€‹

// Search categories as user types
function autocompleteSuggest Categories(categories, input) {
const term = input.toLowerCase();
return categories
.filter((cat) => cat.name.toLowerCase().includes(term))
.slice(0, 10) // Limit to 10 suggestions
.map((cat) => ({
id: cat.id,
name: cat.name,
fullName: cat.fullName,
}));
}

3. Industry-Specific Categoriesโ€‹

// Get categories for specific industries
const industries = {
restaurants: ['restaurant', 'cafe', 'coffee', 'bar', 'food'],
legal: ['law', 'attorney', 'legal'],
medical: ['doctor', 'dentist', 'medical', 'clinic', 'hospital'],
retail: ['store', 'shop', 'retail', 'boutique'],
};

function getCategoriesByIndustry(categories, industry) {
const keywords = industries[industry] || [];

return categories.filter(cat => {
return keywords.some(keyword => cat.fullName.toLowerCase().includes(keyword));
});
}
๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM