9.2 KiB
9.2 KiB
SEO Optimizer for WordPress
Production-ready Go application that automatically optimizes WordPress blog posts for SEO using Claude AI.
Features
- Autonomous Optimization: Automatically selects and optimizes old blog posts
- Budget-Conscious: Single comprehensive Claude API call per post (~€0.05/post)
- Multi-Language Support: Alternates between English and Spanish (Polylang)
- Comprehensive SEO: Meta tags, content improvements, internal linking, FAQ blocks
- Draft Revisions: Creates draft revisions for human approval (never auto-publishes)
- Smart Selection: Respects cooldown periods and content quality criteria
- REST API: Manual triggers and status monitoring
- Production-Ready: Docker, Kubernetes, graceful shutdown, structured logging
Architecture
┌─────────────────┐
│ Scheduler │ Language rotation (EN → ES)
│ (2.5h cycle) │
└────────┬────────┘
│
v
┌─────────────────┐
│ Post Selector │ Select oldest posts (120+ days)
└────────┬────────┘ Content >= 500 words
│ Cooldown: 90 days
v
┌─────────────────┐
│ Claude AI │ Single comprehensive prompt
│ (Sonnet 4.5) │ - SEO meta optimization
└────────┬────────┘ - Content improvements
│ - Internal links (3-8)
│ - FAQ blocks
│ - Fact checking
v
┌─────────────────┐
│Content Builder │ Apply all optimizations
└────────┬────────┘ Generate Gutenberg blocks
│
v
┌─────────────────┐
│ WordPress API │ Create draft revision
└─────────────────┘ (pending approval)
Quick Start
Prerequisites
- Go 1.21+
- Docker (optional)
- WordPress with:
- Application Password enabled
- Polylang plugin
- RankMath SEO plugin
- REST API accessible
Setup
-
Clone and configure:
git clone <repository> cd WPSideKick make dev-setup -
Edit credentials:
# Edit .env with your credentials nano .envRequired:
WP_APP_PASSWORD: Generate in WordPress → Users → Profile → Application PasswordsCLAUDE_API_KEY: Get from https://console.anthropic.com/
-
Install dependencies:
make deps -
Run locally:
make run
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
WP_APP_PASSWORD |
WordPress Application Password | Required |
CLAUDE_API_KEY |
Claude API key | Required |
OPTIMIZATION_INTERVAL_HOURS |
Hours between optimizations | 2.5 |
LOG_LEVEL |
Logging level (INFO/DEBUG) | INFO |
API_PORT |
REST API port | 8080 |
WEBHOOK_URL |
Optional webhook for notifications | - |
Configuration File
Copy configs/config.example.yaml to configs/config.yaml and customize:
wordpress:
base_url: "https://your-blog.com"
username: "admin"
claude:
model: "claude-sonnet-4-20250514"
max_tokens: 4096
temperature: 0.3
scheduler:
enabled: true
interval_hours: 2.5
languages: ["en", "es"]
optimization:
min_content_length: 500
min_age_days: 30
max_age_days: 365
reoptimization_cooldown_days: 365
storage:
base_path: "./data/optimizations"
Docker Deployment
Docker Compose (Recommended)
# Build and start
make docker-build
make docker-run
# View logs
make logs
# Stop
make docker-stop
Docker Run
docker build -t seo-optimizer .
docker run -d \
--name seo-optimizer \
-p 8080:8080 \
-e WP_APP_PASSWORD="your-password" \
-e CLAUDE_API_KEY="your-key" \
-e OPTIMIZATION_INTERVAL_HOURS=2.5 \
seo-optimizer
Kubernetes Deployment
# Edit secrets in deployment.yaml first
kubectl apply -f deployments/kubernetes/deployment.yaml
# Check status
kubectl get pods -l app=seo-optimizer
kubectl logs -f deployment/seo-optimizer
# Port-forward for local access
kubectl port-forward svc/seo-optimizer 8080:8080
REST API
Endpoints
Health Check
curl http://localhost:8080/api/v1/health
# Response: {"status":"ok","version":"1.0.0"}
Manual Optimization
curl -X POST http://localhost:8080/api/v1/optimize \
-H "Content-Type: application/json" \
-d '{"post_id": 123, "language": "es"}'
# Response: {"job_id":"uuid","status":"accepted"}
Check Status
curl http://localhost:8080/api/v1/status/uuid
# Response: {"status":"completed","post_id":123,...}
Pending Reviews
curl http://localhost:8080/api/v1/pending
# Response: {"count":2,"records":[...]}
Optimization Details (by Post ID)
curl http://localhost:8080/api/v1/optimization/123
# Response: { ... optimization record ... }
Apply Draft
curl -X POST http://localhost:8080/api/v1/apply-draft \
-H "Content-Type: application/json" \
-d '{"draft_post_id": 456}'
# Response: {"status":"applied","message":"Draft applied to original post and draft deleted"}
Optimization Records (File Storage)
Optimizations are stored on disk for audit and review:
- Base path:
storage.base_path(default./data/optimizations) - Subfolders:
pending/,applied/,rejected/,skipped/ - Files:
{post_id}_{unix_timestamp}.jsoncontaining the full Claude response
How It Works
Post Selection
The optimizer selects posts based on:
- Age: Last modified between 30 and 365 days ago
- Content: >= 500 words
- Cooldown: Not optimized in last 12 months
- Status: Published only
- Order: Oldest first
Optimization Process
- SEO Meta: Optimized title (50-60 chars) and description (150-160 chars)
- Content Changes: Keyword injection, heading improvements, technical updates
- Internal Links: 3-8 relevant internal links with descriptive anchors
- FAQ Block: 4-6 questions in RankMath format (if appropriate)
- Validation: Fact-checking and broken link fixes
- Draft Creation: WordPress draft revision (requires manual approval)
Cost Tracking
- Model: Claude Sonnet 4.5
- Pricing: $3/1M input tokens, $15/1M output tokens
- Est. per post: 8K input + 2K output ≈ $0.054 (€0.05)
- Budget: €10/month ≈ 200 posts
- Interval: 2.5 hours ≈ 10 posts/day ≈ 300 posts/month
Monitoring
Logs
# Docker Compose
docker-compose logs -f
# Kubernetes
kubectl logs -f deployment/seo-optimizer
# JSON format (default)
{
"time":"2026-01-24T10:30:00Z",
"level":"INFO",
"msg":"Optimization completed",
"post_id":123,
"cost_usd":0.054,
"changes":8,
"links":5,
"duration_seconds":45.2
}
Health Check
# HTTP
curl http://localhost:8080/api/v1/health
# Docker
docker inspect --format='{{.State.Health.Status}}' seo-optimizer
Development
Build
# Local build
make build
# Production build (optimized)
make build-prod
# Run with debug logging
make run-debug
Testing
# Run all tests
make test
# With coverage
make test-coverage
# Quick tests
make test-quick
Code Quality
# Format code
make fmt
# Lint (requires golangci-lint)
make lint
# All checks
make all
Troubleshooting
WordPress Connection Issues
Problem: "WordPress API error: status 401"
Solution:
- Verify WordPress Application Password is correct
- Check WordPress REST API is accessible:
curl https://your-blog.com/wp-json/wp/v2/posts - Ensure username and password are properly set in .env
Claude API Errors
Problem: "Claude API error: status 401"
Solution:
- Verify Claude API key is correct
- Check API key has sufficient credits
- Test API key:
curl https://api.anthropic.com/v1/messages -H "x-api-key: YOUR_KEY"
No Posts Selected
Problem: "No eligible posts found for optimization"
Possible causes:
- All posts are too recent (< 120 days old)
- All posts were recently optimized (< 90 days ago)
- Posts don't meet minimum word count (500 words)
Solution:
- Adjust
max_age_daysorreoptimization_cooldown_daysin config - Check post modification dates in WordPress
Budget Exceeded
Problem: Costs exceeding €10/month
Solution:
- Increase
interval_hours(e.g., 3.0 or 4.0) - Reduce optimization frequency
- Monitor costs in logs:
grep "cost_usd" logs/*.log
Security
- No Auto-Publish: Creates draft revisions only
- Local API: No authentication (assumes local/trusted network)
- Credentials: Use environment variables, never commit secrets
- Non-Root: Docker runs as non-root user (UID 1000)
License
[Add your license here]
Support
- Issues: GitHub Issues
- Blog: https://alexandre-vazquez.com
- Contact: [Your contact info]
Roadmap
- Prometheus metrics export
- Advanced retry strategies
- Image optimization suggestions
- Schema markup generation
- Multi-site support
- Category-specific optimization strategies