Skip to main content

Overview

GEO AI provides a REST API under the geoai/v1 namespace for programmatic access to AI audits, quick fixes, and content analysis. All endpoints require WordPress authentication.

Base URL

https://yoursite.com/wp-json/geoai/v1

Authentication

All endpoints use WordPress REST API authentication:

Endpoints

Run Audit

Runs an AI-powered content audit using Google Gemini API.
curl -X POST https://example.com/wp-json/geoai/v1/audit \
  -H "Content-Type: application/json" \
  -H "X-WP-Nonce: YOUR_NONCE" \
  -d '{"post_id": 123}'

Request

Method: POST
Endpoint: /geoai/v1/audit
Parameters:
ParameterTypeRequiredDescription
post_idintegerYesThe ID of the post to audit

Response

Success (200):
{
  "success": true,
  "data": {
    "scores": {
      "answerability": 85,
      "structure": 78,
      "trust": 90,
      "technical": 82,
      "total": 84
    },
    "issues": [
      {
        "id": "missing_tldr",
        "severity": "high",
        "msg": "Post lacks a TL;DR summary for quick understanding",
        "quickFix": "insert_answer_card"
      },
      {
        "id": "low_readability",
        "severity": "med",
        "msg": "Content readability could be improved with shorter sentences",
        "quickFix": null
      }
    ],
    "schema": {
      "article": true,
      "faq": false,
      "howto": false,
      "errors": []
    },
    "suggestions": {
      "titleOptions": [
        "How to Build SEO-Optimized Content for AI Search",
        "AI Search Optimization: Complete Guide for 2024"
      ],
      "entities": [
        "Google AI Overviews",
        "Perplexity AI",
        "ChatGPT Search",
        "Schema.org"
      ],
      "citations": [
        "https://developers.google.com/search/docs",
        "https://schema.org/Article"
      ]
    },
    "runAt": "2024-03-15T14:30:00+00:00"
  }
}
Error (500):
{
  "success": false,
  "message": "Google Gemini API key not configured."
}

Permissions

Requires: edit_post capability for the specified post.

Source

Location: includes/class-geoai-rest.php:83-107

Apply Quick Fix

Applies automated content improvements to a post.
curl -X POST https://example.com/wp-json/geoai/v1/quick-fix \
  -H "Content-Type: application/json" \
  -H "X-WP-Nonce: YOUR_NONCE" \
  -d '{
    "post_id": 123,
    "fix_id": "insert_answer_card"
  }'

Request

Method: POST
Endpoint: /geoai/v1/quick-fix
Parameters:
ParameterTypeRequiredDescription
post_idintegerYesThe ID of the post to modify
fix_idstringYesThe ID of the fix to apply
Available Fix IDs:
Fix IDDescription
insert_answer_cardInserts Answer Card block at top of post
add_authorAdds author byline (planned)

Response

Success (200):
{
  "success": true,
  "message": "Quick fix applied successfully.",
  "data": {
    "content": "<!-- wp:geoai/answer-card... -->\n\n<original content>",
    "notice": "Answer Card inserted at the top of the post."
  }
}
Error - Already Exists (500):
{
  "success": false,
  "message": "Answer Card already exists in this post."
}
Error - Invalid Fix (500):
{
  "success": false,
  "message": "Invalid fix ID."
}

Permissions

Requires: edit_post capability for the specified post.

Source

Location: includes/class-geoai-rest.php:109-183

Custom Meta Registration

GEO AI registers custom post meta fields that are exposed via REST API:
register_post_meta(
    '',  // All post types
    '_geoai_audit_timestamp',
    array(
        'show_in_rest'  => true,
        'single'        => true,
        'type'          => 'string',
        'auth_callback' => function () {
            return current_user_can( 'edit_posts' );
        },
    )
);

Accessing Meta via REST

curl https://example.com/wp-json/wp/v2/posts/123
Response includes:
{
  "id": 123,
  "title": {...},
  "content": {...},
  "meta": {
    "_geoai_audit_timestamp": "2024-03-15 14:30:00"
  }
}

Error Handling

Error Response Format

All errors follow this structure:
{
  "success": false,
  "message": "Error description"
}

Common Error Codes

HTTP CodeMeaningCommon Causes
400Bad RequestMissing required parameters
401UnauthorizedNot authenticated
403ForbiddenInsufficient permissions
404Not FoundPost doesn’t exist
500Server ErrorAPI error, plugin configuration issue

Handling Errors in JavaScript

try {
    const response = await wp.apiFetch({
        path: '/geoai/v1/audit',
        method: 'POST',
        data: { post_id: 123 }
    });
    
    if (response.success) {
        console.log('Audit complete:', response.data);
    }
} catch (error) {
    if (error.code === 'no_api_key') {
        alert('Please configure your Gemini API key in settings.');
    } else {
        console.error('Audit failed:', error.message);
    }
}

Rate Limiting

GEO AI respects Google Gemini API rate limits:
  • Free tier: 15 requests per minute
  • Paid tier: 1000+ requests per minute
Exceeding rate limits will result in 429 Too Many Requests errors from the Gemini API.

Implementing Client-Side Rate Limiting

class AuditQueue {
    constructor() {
        this.queue = [];
        this.processing = false;
        this.minInterval = 4000; // 4 seconds between requests
    }
    
    async add(postId) {
        this.queue.push(postId);
        if (!this.processing) {
            await this.process();
        }
    }
    
    async process() {
        this.processing = true;
        
        while (this.queue.length > 0) {
            const postId = this.queue.shift();
            
            try {
                await wp.apiFetch({
                    path: '/geoai/v1/audit',
                    method: 'POST',
                    data: { post_id: postId }
                });
            } catch (error) {
                console.error(`Audit failed for post ${postId}:`, error);
            }
            
            // Wait before next request
            if (this.queue.length > 0) {
                await new Promise(resolve => 
                    setTimeout(resolve, this.minInterval)
                );
            }
        }
        
        this.processing = false;
    }
}

const auditQueue = new AuditQueue();
auditQueue.add(123);
auditQueue.add(456);

Extending the REST API

Adding Custom Endpoints

Extend GEO AI’s REST API with custom endpoints:
add_action( 'rest_api_init', function() {
    register_rest_route( 'geoai/v1', '/custom-endpoint', array(
        'methods'  => 'POST',
        'callback' => 'my_custom_endpoint_handler',
        'permission_callback' => function() {
            return current_user_can( 'edit_posts' );
        },
        'args' => array(
            'post_id' => array(
                'required' => true,
                'type' => 'integer',
                'sanitize_callback' => 'absint',
            ),
        ),
    ) );
} );

function my_custom_endpoint_handler( $request ) {
    $post_id = $request->get_param( 'post_id' );
    
    // Your logic here
    
    return new WP_REST_Response( array(
        'success' => true,
        'data' => array( 'post_id' => $post_id ),
    ), 200 );
}

Filtering Audit Results

Modify audit results before they’re returned:
add_filter( 'geoai_audit_result', function( $result, $post_id ) {
    // Add custom scoring criteria
    $result['scores']['custom_metric'] = 95;
    
    // Add custom issues
    $result['issues'][] = array(
        'id' => 'custom_issue',
        'severity' => 'low',
        'msg' => 'Custom validation failed',
        'quickFix' => null,
    );
    
    return $result;
}, 10, 2 );

Usage Examples

Bulk Audit Posts

async function auditAllPosts(postIds) {
    const results = [];
    
    for (const postId of postIds) {
        try {
            const response = await wp.apiFetch({
                path: '/geoai/v1/audit',
                method: 'POST',
                data: { post_id: postId }
            });
            
            results.push({
                postId,
                success: true,
                score: response.data.scores.total
            });
        } catch (error) {
            results.push({
                postId,
                success: false,
                error: error.message
            });
        }
        
        // Wait 4 seconds between requests
        await new Promise(resolve => setTimeout(resolve, 4000));
    }
    
    return results;
}

// Usage
const results = await auditAllPosts([123, 456, 789]);
console.table(results);

Building a Custom Dashboard

import { useEffect, useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';

function AuditDashboard({ postId }) {
    const [audit, setAudit] = useState(null);
    const [loading, setLoading] = useState(false);
    
    const runAudit = async () => {
        setLoading(true);
        
        try {
            const response = await apiFetch({
                path: '/geoai/v1/audit',
                method: 'POST',
                data: { post_id: postId }
            });
            
            if (response.success) {
                setAudit(response.data);
            }
        } catch (error) {
            console.error('Audit failed:', error);
        } finally {
            setLoading(false);
        }
    };
    
    return (
        <div className="audit-dashboard">
            <button onClick={runAudit} disabled={loading}>
                {loading ? 'Running...' : 'Run Audit'}
            </button>
            
            {audit && (
                <div className="audit-results">
                    <h3>Overall Score: {audit.scores.total}</h3>
                    <ul>
                        {audit.issues.map((issue, i) => (
                            <li key={i}>
                                <strong>{issue.severity}:</strong> {issue.msg}
                            </li>
                        ))}
                    </ul>
                </div>
            )}
        </div>
    );
}

Testing

Using WP-CLI

Test endpoints from the command line:
# Run audit
wp eval 'echo json_encode(
    wp_remote_post(
        rest_url("geoai/v1/audit"),
        array("body" => json_encode(array("post_id" => 123)))
    )
);'

# Apply quick fix
wp eval 'echo json_encode(
    wp_remote_post(
        rest_url("geoai/v1/quick-fix"),
        array(
            "body" => json_encode(array(
                "post_id" => 123,
                "fix_id" => "insert_answer_card"
            ))
        )
    )
);'

PHPUnit Tests

class GeoAI_REST_Test extends WP_UnitTestCase {
    public function test_audit_endpoint_requires_auth() {
        $request = new WP_REST_Request( 'POST', '/geoai/v1/audit' );
        $request->set_param( 'post_id', 1 );
        
        $response = rest_do_request( $request );
        
        $this->assertEquals( 401, $response->get_status() );
    }
    
    public function test_audit_endpoint_returns_scores() {
        wp_set_current_user( $this->factory->user->create( array(
            'role' => 'editor',
        ) ) );
        
        $post_id = $this->factory->post->create();
        
        $request = new WP_REST_Request( 'POST', '/geoai/v1/audit' );
        $request->set_param( 'post_id', $post_id );
        
        $response = rest_do_request( $request );
        $data = $response->get_data();
        
        $this->assertEquals( 200, $response->get_status() );
        $this->assertTrue( $data['success'] );
        $this->assertArrayHasKey( 'scores', $data['data'] );
    }
}

Next Steps

Extending GEO AI

Learn about hooks, filters, and custom analyzers

Contributing

Contribute improvements to the REST API

Build docs developers (and LLMs) love