WordPress SEO Plugin Integration
WordPress SEO Plugin Integration
Instructions
Implement cross-compatible integration with the four major WordPress SEO plugins. Products that manage SEO data must detect which plugin is active and use the correct APIs, hooks, and storage formats.
1. Plugin Detection
Detect the active SEO plugin before any read/write operations:
function detect_seo_plugin(): string {
if ( defined( 'WPSEO_VERSION' ) ) {
return 'yoast';
}
if ( defined( 'RANK_MATH_VERSION' ) ) {
return 'rankmath';
}
if ( defined( 'AIOSEO_VERSION' ) ) {
return 'aioseo';
}
if ( defined( 'SEOPRESS_VERSION' ) ) {
return 'seopress';
}
return 'none';
}
Cache the result with wp_cache_set() since this runs frequently.
2. Meta Field Storage Formats
Each plugin stores SEO data in different postmeta keys:
Focus Keyphrase
| Plugin | Meta Key | Format |
|---|---|---|
| Yoast | _yoast_wpseo_focuskw |
Plain string |
| Rank Math | rank_math_focus_keyword |
Comma-separated string (multiple keywords) |
| AIOSEO | _aioseo_keywords |
JSON string: [{"keyword":"term","score":0}] |
| SEOPress | _seopress_analysis_target_kw |
Comma-separated string |
Meta Title
| Plugin | Meta Key | Format |
|---|---|---|
| Yoast | _yoast_wpseo_title |
String with %% variables: %%title%% %%sep%% %%sitename%% |
| Rank Math | rank_math_title |
String with % variables: %title% %sep% %sitename% |
| AIOSEO | _aioseo_title |
String with # tags: #post_title #separator_sa #site_title |
| SEOPress | _seopress_titles_title |
Plain string (variables resolved at render) |
Meta Description
| Plugin | Meta Key | Format |
|---|---|---|
| Yoast | _yoast_wpseo_metadesc |
Plain string |
| Rank Math | rank_math_description |
Plain string |
| AIOSEO | _aioseo_description |
Plain string |
| SEOPress | _seopress_titles_desc |
Plain string |
Canonical URL
| Plugin | Meta Key | Format |
|---|---|---|
| Yoast | _yoast_wpseo_canonical |
URL string |
| Rank Math | rank_math_canonical_url |
URL string |
| AIOSEO | _aioseo_canonical_url |
URL string |
| SEOPress | _seopress_robots_canonical |
URL string |
Open Graph Title / Description
| Plugin | OG Title Key | OG Description Key |
|---|---|---|
| Yoast | _yoast_wpseo_opengraph-title |
_yoast_wpseo_opengraph-description |
| Rank Math | rank_math_facebook_title |
rank_math_facebook_description |
| AIOSEO | _aioseo_og_title |
_aioseo_og_description |
| SEOPress | _seopress_social_fb_title |
_seopress_social_fb_desc |
3. Unified Read/Write Interface
Abstract plugin differences behind a unified interface:
interface SEO_Data_Provider {
public function get_focus_keyphrase( int $post_id ): string;
public function set_focus_keyphrase( int $post_id, string $keyphrase ): bool;
public function get_meta_title( int $post_id ): string;
public function set_meta_title( int $post_id, string $title ): bool;
public function get_meta_description( int $post_id ): string;
public function set_meta_description( int $post_id, string $description ): bool;
public function get_canonical_url( int $post_id ): string;
public function set_canonical_url( int $post_id, string $url ): bool;
public function get_og_title( int $post_id ): string;
public function set_og_title( int $post_id, string $title ): bool;
public function get_og_description( int $post_id ): string;
public function set_og_description( int $post_id, string $description ): bool;
public function get_robots_meta( int $post_id ): array;
public function set_robots_meta( int $post_id, array $directives ): bool;
}
Implement one class per plugin (Yoast_SEO_Provider, RankMath_SEO_Provider, etc.) and use a factory:
function get_seo_provider(): SEO_Data_Provider {
return match ( detect_seo_plugin() ) {
'yoast' => new Yoast_SEO_Provider(),
'rankmath' => new RankMath_SEO_Provider(),
'aioseo' => new AIOSEO_SEO_Provider(),
'seopress' => new SEOPress_SEO_Provider(),
default => new Null_SEO_Provider(),
};
}
4. Plugin-Specific APIs and Hooks
Yoast SEO
Programmatic API (preferred over direct meta):
// Read via helper
$meta = YoastSEO()->meta->for_post( $post_id );
$title = $meta->title;
$description = $meta->description;
// Hooks for modifying output
add_filter( 'wpseo_title', function( $title ) { return $title; } );
add_filter( 'wpseo_metadesc', function( $desc ) { return $desc; } );
add_filter( 'wpseo_canonical', function( $canonical ) { return $canonical; } );
Rank Math
// Read
$keyphrase = RankMath\Post::get_meta( 'focus_keyword', $post_id );
$description = RankMath\Post::get_meta( 'description', $post_id );
// Hooks
add_filter( 'rank_math/frontend/title', function( $title ) { return $title; } );
add_filter( 'rank_math/frontend/description', function( $desc ) { return $desc; } );
AIOSEO
// Read via model
$aioseo_post = \AIOSEO\Plugin\Models\Post::getPost( $post_id );
$title = $aioseo_post->title;
$description = $aioseo_post->description;
$keyphrases = json_decode( $aioseo_post->keyphrases, true );
SEOPress
// Direct meta access (no public PHP API)
$title = get_post_meta( $post_id, '_seopress_titles_title', true );
$desc = get_post_meta( $post_id, '_seopress_titles_desc', true );
// Hooks
add_filter( 'seopress_titles_title', function( $title ) { return $title; } );
add_filter( 'seopress_titles_desc', function( $desc ) { return $desc; } );
5. Robots Meta Directives
Each plugin stores robots directives differently:
| Plugin | Storage | noindex Key |
|---|---|---|
| Yoast | _yoast_wpseo_meta-robots-noindex |
1 = noindex |
| Rank Math | rank_math_robots |
Serialized array: ['noindex'] |
| AIOSEO | _aioseo_robots_noindex |
Boolean |
| SEOPress | _seopress_robots_index |
yes = noindex |
6. Schema/Structured Data Integration
When adding custom schema alongside the SEO plugin’s schema:
- Yoast: Use
wpseo_schema_graphfilter to add to the schema graph - Rank Math: Use
rank_math/json_ldfilter - AIOSEO: Use
aioseo_schema_outputfilter - SEOPress: Use
seopress_schemas_auto_json_ldfilter
Avoid outputting duplicate schemas. Check if the SEO plugin already outputs Article, Organization, or WebPage schema before adding your own.
7. REST API Endpoints
Some plugins expose REST API endpoints:
| Plugin | Namespace | Key Endpoints |
|---|---|---|
| Yoast | /yoast/v1/ |
Limited — primarily internal use |
| Rank Math | /rankmath/v1/ |
updateMeta, getHead |
| AIOSEO | /aioseo/v1/ |
Full CRUD for SEO data |
| SEOPress | /seopress/v1/ |
Limited endpoints |
For cross-plugin compatibility, prefer direct postmeta read/write over REST APIs since not all plugins expose comprehensive endpoints.
8. Data Migration Between Plugins
When building migration tools:
- Read all SEO data from source plugin using the unified interface
- Map fields to target plugin format (handle variable syntax conversion)
- Write to target plugin’s meta keys
- Verify by reading back through the target plugin’s API
- Preserve original data in a backup meta key:
_seo_migration_backup_{source}
Variable conversion table:
- Yoast
%%title%%→ Rank Math%title%→ AIOSEO#post_title→ SEOPress (resolved at save) - Yoast
%%sep%%→ Rank Math%sep%→ AIOSEO#separator_sa→ SEOPress (literal separator)
Inputs Required
- Target SEO plugin(s) to integrate with (one or all four)
- Read vs. write requirements (some integrations only need to read existing data)
- Fields to access (keyphrase, meta title, meta description, OG, robots, schema)
- WordPress version and PHP version constraints
- Whether REST API integration is needed alongside PHP
- Migration requirements (if switching between plugins)
Output Format
- Plugin detection implementation
- Unified SEO data provider interface and plugin-specific implementations
- Meta key reference table for target fields
- Hook integration points for output modification
- Schema integration code avoiding duplicates
- Migration utility (if needed)
- Unit test coverage for each plugin adapter
Anti-Patterns
- Hardcoding a single plugin: Always implement the detection + provider pattern. Users switch SEO plugins, and your integration should survive the switch.
- Writing directly to meta without sanitization: SEO fields are rendered in
. Sanitize all values for HTML output:esc_attr()for meta values,esc_url()for canonical URLs. - Ignoring variable syntax differences: Writing Yoast
%%title%%variables into Rank Math fields produces broken output. Always convert between formats. - Overwriting user-edited SEO data without confirmation: If the user has manually set a meta description, don’t silently replace it. Warn or offer a merge.
- Outputting duplicate schema: Two
Organizationschemas on one page confuses search engines. Check what the SEO plugin already outputs. - Assuming all plugins have APIs: SEOPress has minimal PHP API — you’ll need direct
postmetaaccess. Plan for the lowest common denominator. - Not testing with each plugin active: Integration that works with Yoast may break with Rank Math. Test with each plugin individually and with no SEO plugin installed (the
Null_SEO_Providerpath).
