At Outsourcify, we recently completed the development of AcadAsia Thailand, an ambitious international school advisory platform that represents a significant evolution in how families navigate Thailand’s complex educational landscape. This case study explores the technical architecture, data challenges, and engineering decisions behind building a scalable, content-rich platform that currently features over 300 international and bilingual schools.
Unlike traditional school directories, AcadAsia functions as a comprehensive advisory service where human expertise meets sophisticated technology. The platform combines structured content management, intelligent search capabilities, and personalized matching systems to help families find the right educational fit while providing schools with qualified prospective family leads.
What is AcadAsia?
AcadAsia is a bilingual (Thai/English) school advisory platform initially focused on Bangkok, with an architecture designed for expansion throughout Thailand and eventually across Asia. The platform differentiates itself through:
- Expert Advisory Model: School advisors personally visit institutions to provide authentic insights through video consultations
- Member-Gated Content: Detailed school comparisons, fee calculators, and advisor visit reports available to registered families
- Advanced Matching System: Sophisticated filtering that distinguishes between hard requirements (age/grade availability, language support) and weighted preferences (curriculum fit, distance, budget)
- Lead Generation Business Model: Completely free for families, with schools purchasing qualified leads rather than parents paying for access
The platform serves multiple stakeholder groups including parents seeking schools, educational institutions looking for qualified prospective families, and advisors providing expert guidance.

Technical Architecture Overview
WordPress as a Foundation
We selected WordPress as our core platform, but this isn’t your typical WordPress implementation. The architecture leverages WordPress’s robust content management capabilities while implementing modern development practices and a clean separation of concerns.
Key Technology Stack:
- WordPress Core: CMS foundation with custom post types and taxonomies
- Timber/Twig: Templating engine providing clean separation between PHP logic and HTML presentation
- Advanced Custom Fields (ACF): Complex data structure management
- Tailwind CSS: Utility-first styling framework
- JavaScript/jQuery: Interactive features and AJAX functionality
- Google Maps API: Location services and school mapping
- bbPress: Community forum integration (upcoming feature)
Why Timber/Twig?
Traditional WordPress theme development mixes PHP logic with HTML markup, creating maintenance challenges and reducing code readability. Timber brings the Twig templating engine to WordPress, offering several advantages:
twig
{# Clean, readable template syntax #}
{% for school in schools %}
<article class="school-card">
<h3>{{ school.title }}</h3>
<div class="curriculum-tags">
{% for curriculum in school.curricula %}
<span class="badge">{{ curriculum.name }}</span>
{% endfor %}
</div>
<p class="location">{{ school.location.city }}, {{ school.location.district }}</p>
</article>
{% endfor %}
This approach provides:
- Separation of Concerns: Business logic stays in PHP controllers, presentation in Twig templates
- Improved Readability: Cleaner syntax for designers and front-end developers
- Better Maintainability: Easier to update templates without touching PHP code
- Template Inheritance: DRY principles with base templates and child template extension
Content Structure Architecture
The platform’s strength lies in its meticulously structured content model. We’ve built a hierarchical taxonomy system that powers sophisticated filtering and matching capabilities.
Custom Post Types:
- school: Core school profiles with comprehensive data
- curriculum: Detailed curriculum descriptions (IB, British, American, etc.)
- qualification: Academic qualification pathways
- school-visit: Advisor visit reports (member-gated content)
- comparison: Side-by-side school comparisons (member-gated)
Custom Taxonomies:
- school-type: International, Bilingual, Thai Program
- age-range: Early Years, Primary, Secondary, Sixth Form
- curriculum-type: Hierarchical taxonomy for curriculum categorization
- qualification-type: IGCSE, A-Levels, IB Diploma, etc.
- location: Cities, districts, and regions for geographic filtering
- language: Medium of instruction and language support
- facilities: Sports facilities, performing arts, technology labs, etc.
ACF Field Architecture:
We’ve implemented extensive ACF field groups that create a comprehensive data model:
php
// Example: School Profile Field Structure
'school_basic_info' => [
'year_founded',
'student_population',
'student_teacher_ratio',
'class_size_average',
'nationality_mix'
],
'location_data' => [
'google_map' => 'Google Map Field',
'address_thai',
'address_english',
'nearest_bts_mrt',
'district',
'area'
],
'academic_info' => [
'curricula' => 'Relationship Field',
'qualifications' => 'Relationship Field',
'languages_instruction',
'languages_support',
'age_ranges' => 'Taxonomy Field'
],
'admissions' => [
'application_deadlines',
'entry_requirements',
'waiting_list_status',
'acceptance_rate'
],
'fees_structure' => [
'application_fee',
'tuition_fees' => 'Repeater Field by Grade Level',
'other_fees',
'scholarship_availability'
]
This structure enables:
- Granular Filtering: Users can filter by specific criteria combinations
- Relationship Mapping: Schools connect to curricula, qualifications, and facilities
- Data Validation: Field-level validation ensures data consistency
- Flexible Querying: Complex WP_Query arguments for advanced searches
The Data Collection Challenge
Aggregating data for 300+ schools presented one of the most significant technical challenges. International schools in Thailand rarely provide structured data feeds, and information is scattered across websites, PDF documents, and marketing materials.
Multi-Phase Data Collection Strategy
Phase 1: Web Scraping with Puppeteer
We built a Node.js scraping system using Puppeteer to systematically crawl school websites:
javascript
// Simplified example of our scraping approach
const puppeteer = require('puppeteer');
async function scrapeSchoolData(url) {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
});
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
// Extract structured data
const schoolData = await page.evaluate(() => {
// DOM extraction logic
return {
name: document.querySelector('h1')?.textContent,
description: document.querySelector('.about')?.textContent,
curriculum: extractCurriculumInfo(),
fees: extractFeeStructure(),
contact: extractContactInfo()
};
});
await browser.close();
return schoolData;
}
Challenges encountered:
- Dynamic Content: Many schools use JavaScript-heavy sites requiring full browser rendering
- Rate Limiting: Implementing polite crawling with delays and user-agent rotation
- Layout Variations: Each school’s website has unique structure requiring adaptive selectors
- PDF Content: Fee structures and admission guides often locked in PDFs requiring additional parsing
Phase 2: LLM-Assisted Data Analysis
Raw scraped HTML often contains unstructured text that requires interpretation. We integrated Large Language Models to analyze and extract meaningful information:
javascript
// Simplified LLM integration for data extraction
async function analyzeCurriculumDescription(rawText) {
const prompt = `
Analyze this curriculum description and extract:
- Curriculum type (IB PYP, British KS2, etc.)
- Age ranges covered
- Key features
- Language of instruction
Raw text: ${rawText}
Return structured JSON.
`;
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: [{ role: "user", content: prompt }],
response_format: { type: "json_object" }
});
return JSON.parse(response.choices[0].message.content);
}
LLMs proved particularly valuable for:
- Curriculum Identification: Distinguishing between IB PYP, MYP, DP and British KS1, KS2, KS3, IGCSE
- Fee Structure Normalization: Converting varied fee presentations into standardized formats
- Facility Extraction: Identifying facilities mentioned in prose descriptions
- Language Detection: Determining medium of instruction from descriptive text
Important Learning: While LLMs excel at interpretation, we discovered they can hallucinate factual data. Our final approach uses LLMs for analysis and categorization but requires human verification for critical information like fees and contact details.
Phase 3: Human Verification Layer
We built custom WordPress admin tools to streamline human data verification:
- GPS Coordinate Tool: Interface for content editors to accurately place schools on maps
- Fee Calculator Builder: Simplified interface for entering complex fee structures
- Bulk Import System: CSV import with validation and conflict resolution
- Data Quality Dashboard: Identifying missing or inconsistent information
Data Import Automation
php
// Simplified WordPress data import script
function import_school_data($school_data) {
// Create school post
$post_id = wp_insert_post([
'post_type' => 'school',
'post_title' => $school_data['name'],
'post_status' => 'draft', // Requires human review
'post_content' => $school_data['description']
]);
// Import ACF fields
update_field('year_founded', $school_data['founded'], $post_id);
update_field('student_population', $school_data['students'], $post_id);
// Map to taxonomies
wp_set_object_terms($post_id, $school_data['curricula'], 'curriculum-type');
wp_set_object_terms($post_id, $school_data['location'], 'location');
// Store location data
update_field('google_map', [
'lat' => $school_data['latitude'],
'lng' => $school_data['longitude'],
'address' => $school_data['address']
], $post_id);
return $post_id;
}
Advanced Features Implementation
Member Account System
AcadAsia v2 introduces sophisticated member accounts with child profiles and school shortlists:
php
// Custom user meta structure
function create_child_profile($user_id, $child_data) {
$existing_profiles = get_user_meta($user_id, 'child_profiles', true) ?: [];
$new_profile = [
'id' => uniqid(),
'name' => sanitize_text_field($child_data['name']),
'age' => absint($child_data['age']),
'current_grade' => sanitize_text_field($child_data['grade']),
'curriculum_preference' => $child_data['curriculum'],
'language_needs' => $child_data['languages'],
'special_needs' => $child_data['special_needs'],
'created' => current_time('mysql')
];
$existing_profiles[] = $new_profile;
update_user_meta($user_id, 'child_profiles', $existing_profiles);
return $new_profile['id'];
}
Intelligent School Matching
The matching algorithm distinguishes between hard requirements and weighted preferences:
php
function get_matched_schools($child_profile, $preferences) {
$args = [
'post_type' => 'school',
'posts_per_page' => -1,
'meta_query' => ['relation' => 'AND'],
'tax_query' => ['relation' => 'AND']
];
// Hard requirements (must match)
if ($child_profile['age']) {
$args['tax_query'][] = [
'taxonomy' => 'age-range',
'field' => 'slug',
'terms' => get_age_range_for_child($child_profile['age'])
];
}
if ($preferences['curriculum']) {
$args['tax_query'][] = [
'taxonomy' => 'curriculum-type',
'field' => 'slug',
'terms' => $preferences['curriculum']
];
}
// Get base results
$schools = get_posts($args);
// Apply weighted preferences for ranking
$ranked_schools = array_map(function($school) use ($preferences) {
$score = 0;
// Distance weight
if ($preferences['max_distance']) {
$distance = calculate_distance(
$preferences['location'],
get_field('google_map', $school->ID)
);
$score += (1 - ($distance / $preferences['max_distance'])) * 30;
}
// Budget weight
$fees = get_school_average_fees($school->ID);
if ($fees <= $preferences['max_budget']) {
$score += 25;
}
// Facility matches
$facility_matches = count(array_intersect(
$preferences['desired_facilities'],
get_school_facilities($school->ID)
));
$score += $facility_matches * 5;
return [
'school' => $school,
'match_score' => $score
];
}, $schools);
usort($ranked_schools, function($a, $b) {
return $b['match_score'] <=> $a['match_score'];
});
return $ranked_schools;
}
Bulk Enquiry System
Parents can shortlist multiple schools and send enquiries simultaneously:
javascript
// AJAX handler for bulk enquiries
jQuery('.bulk-enquiry-form').on('submit', function(e) {
e.preventDefault();
const selectedSchools = jQuery('.school-checkbox:checked')
.map(function() { return this.value; })
.get();
const enquiryData = {
action: 'submit_bulk_enquiry',
nonce: ajax_object.nonce,
schools: selectedSchools,
parent_info: {
name: jQuery('#parent-name').val(),
email: jQuery('#parent-email').val(),
phone: jQuery('#parent-phone').val()
},
child_info: {
age: jQuery('#child-age').val(),
current_grade: jQuery('#current-grade').val(),
start_date: jQuery('#start-date').val()
},
message: jQuery('#enquiry-message').val()
};
jQuery.post(ajax_object.ajax_url, enquiryData, function(response) {
if (response.success) {
// Schools receive qualified leads
// Parents get confirmation and school contact details
showConfirmation(response.data);
}
});
});
SEO Architecture
Given the competitive nature of educational search queries, we implemented comprehensive SEO strategies:
Technical SEO Foundation
Schema Markup Implementation:
php
function output_school_schema($school_id) {
$schema = [
'@context' => 'https://schema.org',
'@type' => 'EducationalOrganization',
'name' => get_the_title($school_id),
'url' => get_permalink($school_id),
'description' => get_field('school_description', $school_id),
'address' => [
'@type' => 'PostalAddress',
'streetAddress' => get_field('address_english', $school_id),
'addressLocality' => get_field('district', $school_id),
'addressCountry' => 'TH'
],
'geo' => [
'@type' => 'GeoCoordinates',
'latitude' => get_field('google_map', $school_id)['lat'],
'longitude' => get_field('google_map', $school_id)['lng']
],
'telephone' => get_field('phone', $school_id),
'email' => get_field('email', $school_id)
];
echo '<script type="application/ld+json">' .
json_encode($schema, JSON_UNESCAPED_SLASHES) .
'</script>';
}
URL Structure Optimization:
We implemented a hierarchical URL structure for SEO clarity:
/schools/ - Main directory
/schools/bangkok/ - City-level clustering
/schools/bangkok/sukhumvit/ - District-level clustering
/schools/bangkok/sukhumvit/[school-name]/ - Individual school
/curricula/international-baccalaureate/ - Curriculum pages
/curricula/international-baccalaureate/primary-years-programme/ - Specific programs
/compare/school-a-vs-school-b/ - Comparison pages
Content Strategy for SEO
Programmatic Content Generation:
We built tools to generate SEO-optimized landing pages for key search queries:
- Curriculum Pages: Detailed guides for “IB schools in Bangkok,” “British curriculum Thailand”
- Location Pages: “International schools in Sukhumvit,” “Bilingual schools in Phuket”
- Comparison Pages: “IB vs British curriculum,” “International vs Bilingual schools”
- Guide Pages: “Choosing an international school in Thailand,” “School admission process guide”
Dynamic Meta Description Generation:
php
function generate_school_meta_description($school_id) {
$curricula = wp_get_post_terms($school_id, 'curriculum-type');
$location = get_field('district', $school_id);
$age_ranges = wp_get_post_terms($school_id, 'age-range');
$description = sprintf(
'%s is a %s school in %s, Bangkok offering %s. %s',
get_the_title($school_id),
implode(' and ', array_map(fn($c) => $c->name, $curricula)),
$location,
implode(', ', array_map(fn($a) => $a->name, $age_ranges)),
wp_trim_words(get_field('school_overview', $school_id), 15)
);
return substr($description, 0, 155); // Meta description length limit
}
Performance Optimization
Lazy Loading and Asset Optimization:
javascript
// Intersection Observer for lazy-loaded school cards
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const schoolCard = entry.target;
const thumbnailImg = schoolCard.querySelector('img[data-src]');
if (thumbnailImg) {
thumbnailImg.src = thumbnailImg.dataset.src;
thumbnailImg.removeAttribute('data-src');
}
observer.unobserve(schoolCard);
}
});
}, {
rootMargin: '50px'
});
document.querySelectorAll('.school-card').forEach(card => {
observer.observe(card);
});
Database Query Optimization:
We implemented aggressive caching for expensive queries:
php
function get_schools_by_curriculum($curriculum_slug) {
$cache_key = 'schools_curriculum_' . $curriculum_slug;
$schools = wp_cache_get($cache_key);
if (false === $schools) {
$schools = get_posts([
'post_type' => 'school',
'posts_per_page' => -1,
'tax_query' => [[
'taxonomy' => 'curriculum-type',
'field' => 'slug',
'terms' => $curriculum_slug
]]
]);
wp_cache_set($cache_key, $schools, '', 3600); // 1 hour cache
}
return $schools;
}
Multilingual Implementation
The bilingual nature (Thai/English) required thoughtful architecture:
Language Switching System:
php
// Custom language handler
function acadasia_get_current_language() {
if (isset($_COOKIE['acadasia_language'])) {
return $_COOKIE['acadasia_language'];
}
// Detect from browser
$browser_lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
return ($browser_lang === 'th') ? 'th' : 'en';
}
function acadasia_get_translated_field($field_name, $post_id = null) {
$lang = acadasia_get_current_language();
$field_key = $field_name . '_' . $lang;
$value = get_field($field_key, $post_id);
// Fallback to English if Thai translation missing
if (empty($value) && $lang === 'th') {
$value = get_field($field_name . '_en', $post_id);
}
return $value;
}
Bilingual Search Implementation:
php
function search_schools_bilingual($query_string) {
$lang = acadasia_get_current_language();
$args = [
'post_type' => 'school',
'meta_query' => [
'relation' => 'OR',
[
'key' => 'school_name_' . $lang,
'value' => $query_string,
'compare' => 'LIKE'
],
[
'key' => 'school_description_' . $lang,
'value' => $query_string,
'compare' => 'LIKE'
]
]
];
return new WP_Query($args);
}
Roadmap and Future Development
Short-Term Roadmap (Q1-Q2 2025)
Parent Community Forum (bbPress Integration):
- Verified parent discussions and peer support
- Topic categorization by curriculum, age range, location
- Moderation tools and community guidelines
- Integration with member accounts for verified participation
School News & Events Hub:
- Subscription-based event publishing for schools (new revenue stream)
- Calendar integration and event notifications
- Targeted distribution to qualified families
- Event RSVP and tracking systems
Enhanced Mobile Experience:
- Progressive Web App (PWA) capabilities
- Offline school shortlist management
- Push notifications for new school matches
- Mobile-optimized comparison tools
Medium-Term Roadmap (Q3-Q4 2025)
Geographic Expansion:
- Coverage expansion throughout Thailand (Chiang Mai, Phuket, Pattaya)
- Regional subdomain architecture (chiangmai.acadasia.com)
- Localized content and advisor networks
- Multi-city search and comparison capabilities
Advanced Matching Algorithm:
- Machine learning for preference learning
- Historical data analysis for improved recommendations
- Success tracking (accepted applications, family satisfaction)
- Personalized school suggestions based on profile similarity
API Development:
- RESTful API for school data access
- Webhook integrations for school CRM systems
- Third-party integration possibilities
- Mobile app backend support
Long-Term Vision (2026+)
Asia-Pacific Expansion:
- Country-specific deployments (Singapore, Malaysia, Hong Kong)
- Multi-currency and multi-language support
- Regional advisor network development
- Cross-border school search capabilities
Analytics Dashboard for Schools:
- Lead quality metrics and conversion tracking
- Profile view analytics and engagement data
- Competitive positioning insights
- ROI reporting for school partners
AI-Powered Features:
- Chatbot for initial parent queries
- Automated document analysis for admissions
- Predictive acceptance likelihood
- Natural language search interface
Key Technical Learnings
Data Quality Over Automation
Our initial assumption was that LLMs could fully automate data collection. However, we discovered that while AI excels at interpretation and categorization, it can produce hallucinated data for factual information. Our hybrid approach combines automated scraping and AI analysis with mandatory human verification for critical data points.
Content Structure as Foundation
The time invested in designing a comprehensive taxonomy and field structure paid immediate dividends. The ability to query and filter schools by granular criteria creates value far beyond basic directory functionality.
Performance Matters
With 300+ schools and growing, early performance optimization was crucial. Implementing caching strategies, lazy loading, and database query optimization from the beginning prevented technical debt.
User Experience First
Technical sophistication means nothing if users can’t accomplish their goals. Our member account system required multiple iterations to balance comprehensive data collection with user friction.
Conclusion
Building AcadAsia Thailand has been a fascinating exercise in combining traditional content management with modern web technologies, data science, and user experience design. The platform demonstrates that WordPress, when architected thoughtfully with tools like Timber/Twig and ACF, can power sophisticated web applications far beyond its reputation as a simple blogging platform.
The challenges of collecting, structuring, and presenting educational data for 300+ schools have driven innovation in our scraping methodologies, LLM integration, and content verification workflows. As we expand across Thailand and eventually Asia, the scalable architecture we’ve built will enable rapid deployment in new markets while maintaining data quality and user experience standards.
For schools, AcadAsia represents a qualified lead generation channel that connects them with families actively seeking their specific educational offering. For families, it’s a trusted advisory service that cuts through marketing noise to provide objective, expert guidance. For us at Outsourcify, it’s a showcase of technical capability, thoughtful architecture, and the power of combining human expertise with modern technology.
Technical Stack Summary:
- CMS: WordPress with custom post types and taxonomies
- Templating: Timber/Twig for separation of concerns
- Data Management: Advanced Custom Fields (ACF)
- Styling: Tailwind CSS with custom design system
- Data Collection: Puppeteer for scraping, LLM integration for analysis
- Mapping: Google Maps API integration
- Community: bbPress forums (upcoming)
- Languages: PHP, JavaScript, Node.js
- Infrastructure: Optimized for performance and scalability
Visit thailand.acadasia.com to explore the platform, or contact Outsourcify to discuss how we can build sophisticated content platforms for your business needs.