Shazil Khan
Back to portfolioSaaS · United States

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
Rankings map screen with demo data
800+merged pull requests
27worker lanes behind a watchdog
~245sites on its WordPress plugin
~190actions in the AI agent

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.