local-seo
every-app/open-seo
Audit Google Business Profiles, benchmark against local competitors, and map visibility across your service area.
What is local-seo?
Diagnose why a business does or doesn't rank in Google Maps and the local pack near its customers. Use this skill when rankings depend on physical location or service area; for national organic work, use competitor-analysis or keyword-research instead.
- Locate and audit a Google Business Profile against top local competitors
- Compare category, review count, hours completeness, photos, and claimed status
- Visualize Maps ranking across a service area with a rank grid by direction
- Analyze review volume, recency, and owner-reply rates
- Identify the highest-impact fix: category and claim problems outrank posting cadence
How to install local-seo
npx skills add https://github.com/every-app/open-seo --skill local-seo- A Google Business Profile (claimed or unclaimed) or business name and location
- One to three customer-search keywords (e.g., 'emergency plumber', not brand name)
- Business coordinates (latitude/longitude) or ability to derive them from a search result
How to use local-seo
- 1.Call get_project_context to ground the work in existing business overview
- 2.If business_overview is empty, infer the business type and location from the website and confirm with the user in one question
- 3.Search for the business using search_local_businesses with the name and a wide radius to get its cid and coordinates
- 4.Run get_local_serp_results for your main keyword near the business coordinate to identify top competitors
- 5.Compare your listing against the top 2 competitors using get_business_profile for missing details (hours, categories, photos)
- 6.Call get_business_reviews for your business and the strongest competitor to analyze review volume and recency
- 7.Run get_local_rank_grid (start with 3x3) to see where you rank across the service area and who wins at each point
- 8.Add get_google_business_questions and get_business_updates if profile basics are already competitive
Use cases
- A plumber wants to know why they don't appear in the local pack for 'emergency plumber' in their service area
- A multi-location dental practice needs to benchmark each office's profile strength against competitors
- A restaurant owner discovers their listing is unclaimed and wants to see the ranking impact of claiming it
- A home-services business needs to understand which keywords and locations drive visibility
- Local business owners and marketers
- SEO professionals managing location-based rankings
- Multi-location franchise or chain operators
- Service-area businesses (plumbing, HVAC, dental, etc.)
local-seo FAQ
Run one search_local_businesses call to build a snapshot table for the whole chain. For 5 or fewer locations, deep-dive all of them. For more than 5, present the snapshot and ask the user which 1–3 locations to analyze in detail.
Each point in the grid is one paid SERP call. A 3x3 grid costs 9 calls; a 5x5 costs 25. Always tell the user the cost before running a large grid or multiple grids.
Use search_local_businesses with the name as the query and a wide radius. The result rows include coordinates and cid, which you can then use for all downstream calls.
No. Never recommend review gating, fake reviews, or keyword-stuffed business names. Focus on legitimate fixes: claiming the profile, correcting category, improving hours completeness, and encouraging genuine reviews.
Read it against that point's resultsCount. A full result set means the business is outranked; a sparse SERP means few results were returned there, not proof of invisibility.
Full instructions (SKILL.md)
Source of truth, from every-app/open-seo.
name: local-seo description: "Audit a Google Business Profile, compare it to local competitors, and map Maps visibility around a location."
OpenSEO Local SEO
Goal
Work out why a business does or does not show up in Google Maps and the local pack near its customers, and what to fix first.
Use this when rankings depend on a physical location or service area. For national organic work, use competitor-analysis or keyword-research.
Required inputs
projectId- The business: name, or a
cid/placeId(most reliable) - Its coordinate (latitude/longitude) — derive it from a
search_local_businesses/get_local_serp_resultsrow; only ask the user when derivation is ambiguous - One to three keywords customers actually search (e.g. "emergency plumber", not the brand name)
Project context
The project-context tools are free and shared with the app and other agents.
- Call
get_project_contextfirst and ground the work in it — what the business does and where it operates decides which keywords and radius matter. - This skill needs
business_overview. If it is empty, run a minimal inline setup: infer what the business does and its location from the site and confirm it with the user in one question, write it back withupdate_project_context, then continue. Never front-load the full interview; suggestseo-project-setupat the end for the rest. - Before spending credits, check the research log. If the same research ran within the last 30 days, reuse that result and say so instead of re-buying it.
- On finish, write back what is durable with
update_project_context— local competitors that have a website viaaddCompetitors(competitor rows are keyed by domain, so skip listings without one), a correctedbusiness_overview— and append a research log entry:{ appendResearchLog: { summary: "Local SEO: <business> near <area>. Verdict: <conclusion>" } }.
Deliver as a report
Deliver through the seo-report skill, saving with skill: "local-seo". If that skill is not available, say so and stop before writing HTML.
OpenSEO MCP tools
search_local_businesses: nearby listings, filterable byminRating,minReviews, andisClaimed— useisClaimed: falseto find unclaimed listings when prospecting. One call with the brand name asqueryand a wide radius returns category, rating, review count, claimed status, coordinates, andcidfor every location of a chain — usually enough that per-locationget_business_profilecalls are unnecessary.get_local_serp_results: the Maps/Local Finder result set near a coordinate. The rows carrycidandplace_id— collect them once and reuse them everywhere below.get_business_profile: the full profile for one business (hours, rating breakdown) when thesearch_local_businessesrow isn't enough.get_business_reviews: reviews with ratings, text, and whether the owner replied. Queued: aprocessingresponse returns ataskId— call again with it after 30-60 seconds, at no extra cost.get_local_rank_grid: rank at every point of a grid around a coordinate, with each point's result count and #1 business. 3x3 is nine searches; only go to 5x5 when the service area is genuinely wide.get_google_business_questions: Q&A on the profile (acceptscid/placeId).get_business_updates: posts published on the profile, with dates.list_business_categories: valid category slugs forsearch_local_businesses.
Workflow
- Find the business. Given only a name or website,
search_local_businesses(name asquery, wide radius) locates the listing and yields itscidand coordinate. If it returns several locations, the business is a chain — see multi-location below. - Run
get_local_serp_resultsfor the main keyword near the business coordinate. Record the top 3-5 competitors'cid/place_idand the user's own row. - Compare the user's listing against the top two competitors: primary category, additional categories, review count, hours completeness, photo count, claimed status.
search_local_businessesrows usually carry all of this; useget_business_profilefor what they lack. - Sanity-check each listing's website link (
url/contact_urlin the rows): it should deep-link to that location's page on the project domain, not a homepage or a stale domain. For broader on-page work, hand off torun_site_audit. - Call
get_business_reviewsfor the user and the strongest competitor. Look at review volume, recency, average rating, and how many reviews got an owner reply. - Run
get_local_rank_gridfor the main keyword. Use the grid to separate "ranks at the storefront only" from "ranks across the service area", and each point'stopResultto name who wins where the target doesn't. - Add
get_google_business_questionsandget_business_updateswhen the profile basics are already competitive and the gap is engagement rather than setup. - Turn the evidence into a prioritized list. Category and claim problems outrank posting cadence every time.
Multi-location businesses
Always build the profile snapshot table for the whole chain — one search_local_businesses call covers it. The per-location deep-dives (reviews, grid, posts, Q&A) are where cost scales:
- 5 locations or fewer: deep-dive them all.
- More than 5: present the snapshot table, then ask the user (AskUserQuestion) which 1-3 locations to deep-dive. Pick sensible defaults to recommend — e.g. the weakest profile in the densest market.
Output format
h1: the business name.
If a report template applies (see seo-report), its sections and tone replace this list.
Sections in this order:
- Snapshot — one or two opening sentences, then a table of category, rating, review count, and claimed status. One row per location for a chain.
- The one fix — one finding. Category and claim problems outrank posting cadence every time.
- Head to head — a table of signal, this business, the best competitor, and the gap. Cover categories, reviews (count, recency, owner replies), hours and profile completeness, and the listing's website link.
- Maps coverage — what the grid shows, where visibility drops off, and who wins there. A bar chart of ranks by direction reads faster than a paragraph; label every value.
- Q&A and posting — only when the basics are already competitive.
- What to do next — an ordered list.
- How this report was made — opens with the skill link line from
seo-report, pointing athttps://openseo.so/docs/skills/local-seo("OpenSEO Local SEO skill"), then which tools returned what, plus a note reading each missing grid rank against that point'sresultsCountrather than calling it invisibility.
Guardrails
- Do not run a 5x5 grid, or grids for several keywords, without telling the user the cost first — every point is a paid SERP call.
- Match businesses by
cidorplace_idwhen you have one. Name matching collides with chains and similarly named businesses. - A missing rank at a grid point means the business wasn't among the results returned there. Read it with that point's
resultsCount: a full result set means outranked; a near-empty one means a sparse SERP, not proof of invisibility. - Do not infer local-pack strength from national organic metrics.
- Never recommend review gating, fake reviews, or keyword-stuffed business names.
- A grid centered on the wrong place is worse than no grid — confirm the coordinate matches the storefront before spending grid credits.
Related skills
More from every-app/open-seo and the wider catalog.

maintain-greptile-rules
Agent skill from every-app/open-seo.

merge-ready
Agent skill from every-app/open-seo.

openseo-release-notes
Agent skill from every-app/open-seo.

openseo-review-web-content
Agent skill from every-app/open-seo.

papercuts
Agent skill from every-app/open-seo.

seo-audit
Audit a website's SEO and identify the highest-impact changes most likely to grow organic traffic that converts.