GEO AI provides hooks and filters throughout the codebase for developers to customize functionality without modifying core files.
All hooks follow WordPress naming conventions and are prefixed with geoai_.
Filters
Filters allow you to modify data before it’s used or displayed.
Compatibility
geoai_detected_conflicts
Modify the list of detected SEO plugin conflicts.
Location: includes/class-geoai-compat.php:52
apply_filters ( 'geoai_detected_conflicts' , array $conflicts )
Parameters:
$conflicts (array) - List of detected plugin names
Example:
add_filter ( 'geoai_detected_conflicts' , function ( $conflicts ) {
// Add custom SEO plugin detection
if ( defined ( 'MY_SEO_PLUGIN_VERSION' ) ) {
$conflicts [] = 'My SEO Plugin' ;
}
// Remove a plugin from conflict detection
$key = array_search ( 'Yoast SEO' , $conflicts );
if ( false !== $key ) {
unset ( $conflicts [ $key ] );
}
return $conflicts ;
});
Use Cases:
Detect custom or niche SEO plugins
Force compatibility mode with specific plugins
Override automatic detection logic
Schema
geoai_schema_output
Modify schema markup before it’s output to the page.
Location: includes/class-geoai-schema.php:51
apply_filters ( 'geoai_schema_output' , array $schema )
Parameters:
$schema (array) - JSON-LD schema array
Example:
add_filter ( 'geoai_schema_output' , function ( $schema ) {
// Add custom property to Article schema
if ( isset ( $schema [ '@type' ] ) && 'Article' === $schema [ '@type' ] ) {
$schema [ 'author' ][ 'sameAs' ] = array (
'https://twitter.com/yourhandle' ,
'https://linkedin.com/in/yourprofile' ,
);
}
// Add organization logo
if ( isset ( $schema [ 'publisher' ] ) ) {
$schema [ 'publisher' ][ 'logo' ] = array (
'@type' => 'ImageObject' ,
'url' => get_theme_file_uri ( '/assets/logo.png' ),
'width' => 600 ,
'height' => 60 ,
);
}
return $schema ;
});
Use Cases:
Add custom schema properties
Enhance author/publisher data
Inject third-party identifiers (Google Analytics, Facebook, etc.)
Override schema values conditionally
Breadcrumbs
geoai_breadcrumb_items
Modify breadcrumb items before rendering.
Location: includes/class-geoai-breadcrumbs.php:105
apply_filters ( 'geoai_breadcrumb_items' , array $items )
Parameters:
$items (array) - Array of breadcrumb items
Item Structure:
array (
'title' => 'Page Title' ,
'url' => 'https://example.com/page' ,
)
Example:
add_filter ( 'geoai_breadcrumb_items' , function ( $items ) {
// Always start with custom home text
if ( ! empty ( $items ) ) {
$items [ 0 ][ 'title' ] = 'Start Here' ;
}
// Add custom breadcrumb for specific post type
if ( is_singular ( 'product' ) ) {
// Insert "Shop" before current item
$current = array_pop ( $items );
$items [] = array (
'title' => 'Shop' ,
'url' => home_url ( '/shop' ),
);
$items [] = $current ;
}
// Remove URL from last item (current page)
$last_key = array_key_last ( $items );
unset ( $items [ $last_key ][ 'url' ] );
return $items ;
});
Use Cases:
Customize home breadcrumb text
Add intermediate breadcrumb levels
Modify URLs for custom post types
Remove URLs from active breadcrumb
Content Analysis
the_content
GEO AI uses the standard WordPress the_content filter when analyzing posts.
Location: includes/class-geoai-analyzer.php:137
$content = apply_filters ( 'the_content' , $post -> post_content );
This ensures analyzed content matches what’s displayed on the frontend, including:
Shortcode expansion
Block rendering
Auto-paragraph insertion
Content from page builders
Example:
// Exclude specific shortcodes from analysis
add_filter ( 'the_content' , function ( $content ) {
// Only during GEO AI analysis
if ( doing_filter ( 'geoai_analyze_content' ) ) {
// Remove advertisement shortcodes
$content = preg_replace ( '/ \[ advertisement. * ? \] /' , '' , $content );
}
return $content ;
}, 5 ); // Priority 5 to run before GEO AI
Actions
Actions allow you to execute custom code at specific points in GEO AI’s execution.
Common WordPress Actions
GEO AI hooks into standard WordPress actions:
init
Compatibility mode initialization.
add_action ( 'init' , array ( $this , 'init_compat_mode' ) );
Example - Disable compatibility mode programmatically:
add_action ( 'init' , function () {
// Force standalone mode on staging
if ( defined ( 'WP_ENV' ) && 'staging' === WP_ENV ) {
update_option ( 'geoai_compat_mode' , 'standalone' );
}
}, 5 ); // Before GEO AI's init
save_post
Dashboard cache clearing.
Location: includes/analyzers/class-seo-dashboard.php:24
add_action ( 'save_post' , array ( $this , 'clear_cache' ) );
Example - Clear custom caches:
add_action ( 'save_post' , function ( $post_id ) {
// Clear your plugin's cache when GEO AI clears its cache
if ( metadata_exists ( 'post' , $post_id , '_geoai_keyword_score' ) ) {
delete_transient ( 'my_plugin_seo_cache_' . $post_id );
}
});
delete_post
Dashboard cache clearing on post deletion.
Location: includes/analyzers/class-seo-dashboard.php:25
add_action ( 'delete_post' , array ( $this , 'clear_cache' ) );
wp_head
Meta tag and schema output suppression.
Location: includes/class-geoai-compat.php:60
add_action ( 'wp_head' , array ( $this , 'maybe_suppress_outputs' ), 999 );
Example - Add custom meta tags:
add_action ( 'wp_head' , function () {
if ( is_singular ( 'post' ) ) {
$score = get_post_meta ( get_the_ID (), '_geoai_keyword_score' , true );
if ( $score >= 80 ) {
echo '<meta name="content-quality" content="high" />' ;
}
}
}, 1 ); // Priority 1 to output before GEO AI
Custom Hooks (Planned)
These hooks are planned for future versions. Vote for your favorites on GitHub Discussions .
geoai_before_analyze
Fires before running content analysis.
do_action ( 'geoai_before_analyze' , int $post_id , array $data );
Example:
add_action ( 'geoai_before_analyze' , function ( $post_id , $data ) {
error_log ( sprintf ( 'Analyzing post #%d' , $post_id ) );
// Update last analyzed timestamp
update_post_meta ( $post_id , '_last_analyzed' , time () );
});
geoai_after_analyze
Fires after analysis completes.
do_action ( 'geoai_after_analyze' , int $post_id , array $results );
Example:
add_action ( 'geoai_after_analyze' , function ( $post_id , $results ) {
// Send notification if score drops below threshold
if ( $results [ 'scores' ][ 'total' ] < 60 ) {
wp_mail (
'[email protected] ' ,
sprintf ( 'Low SEO score on post #%d' , $post_id ),
sprintf ( 'Score: %d/100' , $results [ 'scores' ][ 'total' ] )
);
}
}, 10 , 2 );
geoai_analyzer_result
Modify individual analyzer results.
apply_filters ( 'geoai_analyzer_result' , array $result , string $analyzer_name , array $input );
Example:
add_filter ( 'geoai_analyzer_result' , function ( $result , $analyzer , $input ) {
// Boost readability scores by 5 points
if ( 'readability' === $analyzer ) {
$result [ 'score' ] = min ( 100 , $result [ 'score' ] + 5 );
}
return $result ;
}, 10 , 3 );
GEO AI stores data in post meta. You can read/modify these values:
Keyword Analysis
Readability
SEO Meta
Social
// Focus keyword
get_post_meta ( $post_id , '_geoai_focus_keyword' , true );
// Keyword score (0-100)
get_post_meta ( $post_id , '_geoai_keyword_score' , true );
// Keyword density (%)
get_post_meta ( $post_id , '_geoai_keyword_density' , true );
Practical Examples
Add Custom Analyzer
Create a custom analyzer and integrate with GEO AI:
namespace My_Plugin ;
class Custom_Analyzer {
public function analyze ( $content ) {
// Your analysis logic
$score = $this -> calculate_score ( $content );
return array (
'score' => $score ,
'issues' => array (
array (
'id' => 'custom_check' ,
'severity' => 'warning' ,
'message' => 'Custom issue found' ,
),
),
);
}
}
// Hook into GEO AI analysis
add_action ( 'geoai_after_analyze' , function ( $post_id , $results ) {
$analyzer = new Custom_Analyzer ();
$content = get_post_field ( 'post_content' , $post_id );
$custom_result = $analyzer -> analyze ( $content );
// Save custom score
update_post_meta ( $post_id , '_custom_analyzer_score' , $custom_result [ 'score' ] );
}, 10 , 2 );
Conditional Schema Output
Only output schema for high-quality content:
add_filter ( 'geoai_schema_output' , function ( $schema ) {
if ( is_singular ( 'post' ) ) {
$score = get_post_meta ( get_the_ID (), '_geoai_keyword_score' , true );
// Only output Article schema if score is 70+
if ( $score < 70 ) {
return array (); // Empty schema
}
}
return $schema ;
});
Auto-Fix Low Scores
Automatically apply fixes when posts fall below threshold:
add_action ( 'save_post' , function ( $post_id ) {
// Skip autosave/revisions
if ( wp_is_post_autosave ( $post_id ) || wp_is_post_revision ( $post_id ) ) {
return ;
}
$score = get_post_meta ( $post_id , '_geoai_readability_score' , true );
if ( $score < 60 ) {
// Send notification
wp_mail (
get_option ( 'admin_email' ),
'Low readability score' ,
sprintf ( 'Post #%d has readability score of %d' , $post_id , $score )
);
// Set post status to draft if too low
if ( $score < 40 ) {
wp_update_post ( array (
'ID' => $post_id ,
'post_status' => 'draft' ,
));
}
}
}, 20 ); // Priority 20 to run after GEO AI saves meta
Track Analysis History
Log score changes over time:
add_action ( 'geoai_after_analyze' , function ( $post_id , $results ) {
$history = get_post_meta ( $post_id , '_geoai_score_history' , true ) ?: array ();
$history [] = array (
'date' => current_time ( 'mysql' ),
'score' => $results [ 'scores' ][ 'total' ],
);
// Keep last 10 audits
$history = array_slice ( $history , - 10 );
update_post_meta ( $post_id , '_geoai_score_history' , $history );
}, 10 , 2 );
Hook Reference Table
Hook Type Location Description geoai_detected_conflictsFilter class-geoai-compat.php:52Modify detected SEO plugins geoai_schema_outputFilter class-geoai-schema.php:51Modify schema markup geoai_breadcrumb_itemsFilter class-geoai-breadcrumbs.php:105Modify breadcrumb items the_contentFilter class-geoai-analyzer.php:137Process content before analysis initAction class-geoai-compat.php:30Compatibility initialization save_postAction class-seo-dashboard.php:24Clear dashboard cache delete_postAction class-seo-dashboard.php:25Clear dashboard cache wp_headAction class-geoai-compat.php:60Suppress meta tag output
Best Practices
Hook Development Guidelines
Always check context - Use conditionals to ensure hooks only run when needed
Use appropriate priority - Higher priority (20+) runs later, lower (5-) runs earlier
Validate input - Never trust data passed to hooks
Return values - Filters must return modified value
Avoid heavy operations - Hooks fire frequently; cache expensive operations
Document custom hooks - Help other developers integrate with your code
Analyzers Learn what data is available in analyzer results
WordPress Plugin Handbook Official WordPress hooks documentation
Architecture Understand GEO AI’s code structure
Contributing Submit your custom hooks to core