Local SEO platform
A Laravel SaaS for local SEO and Google Business Profile management that a US marketing agency runs on. I was its lead engineer.
- Role
- Lead engineer: architecture, back end, front end, infrastructure, releases
- Timeline
- September 2025 to October 2026
- Stack
- Laravel, PHP, MySQL, Next.js, Google Business Profile API, OpenAI, Claude, Gemini, DigitalOcean
- Client
- A US local-marketing agency, name withheld

The situation
An agency fulfilling local SEO by hand. Every client's Google profile needed posts, photos, rank checks and citations, and every one of those was a person doing it in a browser: hours a week per client, results the client had to take on faith, and no way to grow without hiring.
Everything shipped straight to production, so the release process had to be as careful as the code.
What I built
- An AI post writer with hard content gates: the keyword once in the first sentence, no quotes, at most one post a day per profile, a "Learn more" that always points at the client's service page. The gates never switch off.
- AI-generated photos in the client's brand, plus a photo import at setup so new profiles start full.
- Publishing through Google's API, with a "check it's live" verification step and a remove-from-Google action for posts that must come down.
- Map-grid rank heatmaps on DataForSEO, running about 1,100 scans a month, with a 21,000-cell weekly batch sized to finish in about 45 minutes instead of 3 hours.
- Citation pages built and tracked per client.
- Recurring client reports with a review gate before anything goes out, delivered to Slack on a schedule.
- An onboarding wizard, invite-only 14-day trials with no card, subscription packages, renewal reminders, and agency and wholesale accounts.
- An in-product AI agent with about 190 actions. Anything destructive or public needs its own confirmation, with a monthly action budget and a circuit breaker. Covered by PHPUnit and Playwright.
- All AI writing moved from OpenAI to Claude with automatic fallback, so a failed call never loses a post, plus per-call cost metering, a retry ceiling and a daily spend alert.
- A WordPress plugin on about 245 client sites that publishes posts and pages and applies SEO fixes across Elementor, Divi, Gutenberg, Beaver Builder and the Classic editor.
What was hard
Throughput
Everything ran through a single queue, so live scans took minutes and post writing fought with them for workers. I measured where the time went, split it into pools of 8 writers and 8 scanners with 96 concurrent API calls, raised PHP-FPM to 16 workers and MySQL to 150 connections, and had the server resized to 4 vCPU and 8 GB to match. Scans now take seconds.
Shipping without staging
A git-based release path: branch, pull request, merge, write a rollback pointer, check out, recache config atomically, reload. Every release gets a rollback ref before it goes live. In September that process carried 19 production releases without a single rollback.
Not trusting third parties
One syndication target's "is it live" check was flaky. Instead of shipping a claim I couldn't prove, I disabled that target and scheduled a retry. A customer report must never say something happened when it might not have.