# Local SEO Data — Full API Reference > Complete reference for the API endpoints, with request and response shapes. For quick tool selection, see https://localseodata.com/llms.txt ## Authentication All requests require a Bearer token: ``` Authorization: Bearer YOUR_API_KEY ``` Get a key at https://localseodata.com/signup (100 free credits, no credit card). Sandbox keys (`sk_test_*`) return realistic mock data and charge zero credits. ## Response Envelope Every successful response follows this shape: ```json { "status": "success", "request_id": "req_abc123", "credits_used": 1, "credits_remaining": 49, "cached": false, "data": { ... } } ``` Response headers: `X-Credits-Used`, `X-Credits-Remaining`, `X-Request-ID` Cached responses return `credits_used: 0` and `cached: true`. ## Error Shapes ```json // 400 - Invalid parameters { "status": "error", "request_id": "req_abc123", "error": { "code": "invalid_params", "message": "keyword is required" } } // 402 - Insufficient credits { "status": "error", "request_id": "req_abc123", "error": { "code": "insufficient_credits", "message": "Insufficient credits", "required": 10, "current": 3 } } // 429 - Rate limited { "status": "error", "request_id": "req_abc123", "error": { "code": "rate_limit_exceeded", "message": "Rate limit exceeded" } } // Check Retry-After header for seconds to wait // 502 - No data returned { "status": "error", "request_id": "req_abc123", "error": { "code": "no_results", "message": "No results found for this query" } } ``` ## Data Source Notes All ranking and SERP data (local-pack, organic, maps, local-finder, ai-overview, ai-mode, geogrid/scan) uses non-personalized Google search. Results will differ from what a user sees in their browser because Google personalizes by location history, search history, logged-in account, and device. This is standard across SEO tools but worth noting when comparing against manual searches. Review counts and ratings may lag Google's live display by a few hours. AI visibility results (ai/mentions, ai/llm-response, ai/scraper) are point-in-time snapshots -- LLM outputs change frequently. Use ai/sample when the question is how often something is said rather than what was said once. --- ## SERP & Rankings ### POST /v1/serp/local-pack **Use when:** checking if a business appears in Google's top 3 local results for a keyword. **Cost:** 2 credits Request: ```json { "keyword": "plumber", "location": "Buffalo, NY" } ``` Optional: `device` ("desktop"|"mobile"), `depth` (1-60, default 20) Response `data`: ```json { "results": [ { "rank": 1, "name": "Acme Plumbing", "cid": "12345678901", "rating": 4.7, "reviews_count": 142, "address": "123 Main St, Buffalo, NY", "phone": "(555) 123-4567", "hours": "Open · Closes 5 PM", "website": "https://acmeplumbing.com" } ], "total_results": 3, "keyword": "plumber", "location": "Buffalo, NY" } ``` --- ### POST /v1/serp/organic **Use when:** need the complete SERP: organic results + local pack + ads + PAA + AI overview in one call. **Cost:** 2 credits Request: ```json { "keyword": "best plumber near me", "location": "Buffalo, NY" } ``` Response `data`: ```json { "organic_results": [ { "rank": 1, "title": "Best Plumber Near Me - Top Result", "url": "https://example.com/plumber", "snippet": "Find the best plumber in Buffalo..." } ], "local_pack": [ { "rank": 1, "name": "Acme Plumbing", "cid": "123", "rating": 4.7, "reviews_count": 142 } ], "ads": [ { "position": "top", "title": "Best Plumber Near You - Ad", "url": "https://ad.com", "description": "Top-rated service." } ], "people_also_ask": [ "How much does a plumber cost?", "What is the best plumber near me?" ], "ai_overview": "According to local results, plumbers in Buffalo typically offer competitive pricing...", "lsa_ads": [], "knowledge_panel": null } ``` --- ### POST /v1/serp/lsa **Use when:** checking Google Guaranteed/Screened ads for service categories (home services, legal, etc.). **Cost:** 2 credits Request: ```json { "keyword": "plumber", "location": "Buffalo, NY" } ``` Response `data`: ```json { "ads": [ { "rank": 1, "business_name": "Acme Plumbing", "rating": 4.8, "reviews_count": 98, "badge": "Google Guaranteed", "years_in_business": 12, "phone": "(555) 123-4567", "services": ["Plumber", "Emergency Service"] } ], "total_ads": 3, "keyword": "plumber", "location": "Buffalo, NY" } ``` --- ### POST /v1/serp/maps **Use when:** need more than 3 local results with GPS coordinates. Returns up to 60 listings. **Cost:** 2 credits Request: ```json { "keyword": "plumber", "location": "Buffalo, NY" } ``` Optional: `limit` (1-60, default 20) Response `data`: ```json { "results": [ { "rank": 1, "name": "Acme Plumbing", "place_id": "ChIJ_abc123", "rating": 4.7, "reviews_count": 142, "address": "123 Main St, Buffalo, NY", "phone": "(555) 123-4567", "website": "https://acmeplumbing.com", "hours": "Mon-Fri 8AM-6PM", "latitude": 42.886, "longitude": -78.878, "categories": ["Plumber"] } ], "total_results": 20, "keyword": "plumber", "keyword_used": "plumber", "geo_fallback_applied": false, "location": "Buffalo, NY", "location_resolved": "Buffalo,New York,United States" } ``` If the first search returns nothing, the API retries once with the city appended to the keyword (`geo_fallback_applied: true`, `keyword_used` shows the retried keyword). Unresolvable locations return a validation error instead of silently searching country-wide. --- ### POST /v1/serp/local-finder **Use when:** need local listings with Google CID identifiers (instead of GPS coordinates from maps). **Cost:** 2 credits Request: ```json { "keyword": "plumber", "location": "Buffalo, NY" } ``` Optional: `limit` (1-60, default 20), `min_rating` (1-5) Response `data`: ```json { "results": [ { "rank": 1, "name": "Acme Plumbing", "rating": 4.7, "reviews_count": 142, "address": "123 Main St, Buffalo, NY", "phone": "(555) 123-4567", "hours": "Mon-Fri 8AM-6PM", "website": "https://acmeplumbing.com", "cid": "12345678901" } ], "total_results": 20, "keyword": "plumber", "location": "Buffalo, NY" } ``` --- ### POST /v1/serp/ai-overview **Use when:** checking if Google shows an AI Overview for a keyword and what sources it cites. **Cost:** 2 credits Request: ```json { "keyword": "best plumber in Buffalo", "location": "Buffalo, NY" } ``` Response `data`: ```json { "has_ai_overview": true, "asynchronous_ai_overview": false, "summary_text": "Based on local reviews and ratings, plumbers in Buffalo include several highly-rated options...", "cited_sources": [ { "title": "Top Plumbers - Yelp", "url": "https://yelp.com/example", "domain": "yelp.com" } ], "keyword": "best plumber in Buffalo", "location": "Buffalo, NY" } ``` --- ### POST /v1/serp/ai-mode **Use when:** getting Google AI Mode (SGE) generated response text and its reference links. **Cost:** 2 credits Request: ```json { "keyword": "best plumber in Buffalo", "location": "Buffalo, NY" } ``` Response `data`: ```json { "ai_response": "Here are some options for plumbers in Buffalo. Based on reviews and availability...", "references": [ { "title": "Local Business Directory", "url": "https://directory.example.com", "domain": "directory.example.com" } ], "shopping_items": [], "keyword": "best plumber in Buffalo", "location": "Buffalo, NY" } ``` --- ## Business Data ### POST /v1/business/profile **Use when:** pulling one business's complete GBP: NAP, hours, categories, photos, rating, and attributes. **Cost:** 5 credits Request: ```json { "business_name": "Acme Pizza", "location": "Buffalo, NY" } ``` Optional: `place_id` (Google place ID for exact match) Response `data`: ```json { "matched": true, "name": "Acme Pizza", "address": "123 Main St, Buffalo, NY", "phone": "(555) 123-4567", "website": "https://acmepizza.com", "hours": { "monday": { "open": "09:00", "close": "17:00" }, "tuesday": { "open": "09:00", "close": "17:00" }, "saturday": { "open": "10:00", "close": "14:00" }, "sunday": null }, "categories": ["Pizza Restaurant", "Italian Restaurant"], "photos_count": 24, "rating": 4.6, "reviews_count": 287, "description": "A beloved local establishment serving the community...", "attributes": ["Wheelchair accessible", "Free Wi-Fi", "Outdoor seating"], "cid": "12345678901", "place_id": "ChIJ_abc123", "verified": true, "suggestions": null } ``` **No match fallback:** When no exact match is found, `matched` is `false` and `suggestions` contains up to 5 candidates from Google Maps. Each suggestion has `name`, `address`, `rating`, `reviews_count`, `place_id`, `phone`, and `categories`. Retry with a suggestion's `place_id` for an exact lookup. --- ### POST /v1/profile/health **Use when:** auditing what's missing or weak on a GBP. Returns a completeness score with specific fixes. **Cost:** 5 credits Request: ```json { "business_name": "Acme Pizza", "location": "Buffalo, NY" } ``` Optional: `place_id` Response `data`: ```json { "completeness_score": 86, "verified": true, "photos_count": 24, "missing_fields": ["description"], "incomplete_fields": [], "recommendations": [ "Add a business description" ] } ``` --- ### POST /v1/reviews/google **Use when:** reading actual review text and owner replies for sentiment analysis or reply auditing. **Cost:** 1 credit per 10 reviews Request: ```json { "business_name": "Acme Pizza", "location": "Buffalo, NY" } ``` Optional: `place_id`, `limit` (default 10), `sort` ("newest"|"highest"|"lowest"), `offset` Response `data`: ```json { "reviews": [ { "text": "Great experience! Would highly recommend.", "rating": 5, "date": "2026-05-01T00:00:00.000Z", "author_name": "John D.", "author_url": "https://maps.google.com/contrib/123456", "owner_reply": "Thank you for your kind words!", "owner_reply_date": "2026-05-02T00:00:00.000Z", "owner_reply_time_ago": "6 months ago" } ], "total_reviews": 287, "average_rating": 4.6 } ``` --- ### POST /v1/reviews/velocity **Use when:** analyzing review growth rate, rating changes, reply rate, and sentiment themes over time. **Cost:** 6 credits. May take 10-30 seconds. Request: ```json { "business_name": "Acme Pizza", "location": "Buffalo, NY" } ``` Optional: `place_id`, `months` (default 3) Response `data`: ```json { "reviews_per_month": 8.3, "rating_trend": "improving", "current_rating": 4.6, "period_rating": 4.4, "reply_rate": 0.45, "sentiment_themes": { "positive": ["fast service", "friendly staff", "fair pricing"], "negative": ["long wait times", "parking difficult"] }, "review_count_by_month": [ { "month": "2026-05", "count": 9, "avg_rating": 4.7 }, { "month": "2026-04", "count": 7, "avg_rating": 4.5 } ] } ``` --- ### POST /v1/reviews/multi-platform **Use when:** comparing ratings across Google + Trustpilot in one call. **Cost:** 6 credits. May take 10-30 seconds. Request: ```json { "business_name": "Acme Pizza", "location": "Buffalo, NY" } ``` Optional: `place_id` Response `data`: ```json { "platforms": { "google": { "rating": 4.6, "count": 287, "recent_trend": "stable" }, "trustpilot": { "rating": 4.2, "count": 45, "recent_trend": "improving" } }, "combined_rating": 4.5, "total_reviews": 332 } ``` --- ### POST /v1/business/qa **Use when:** checking Q&A on a GBP: what questions are asked and whether they have answers. **Cost:** 5 credits Request: ```json { "business_name": "Acme Pizza", "location": "Buffalo, NY" } ``` Optional: `place_id` Response `data`: ```json { "questions": [ { "question_text": "Do you offer delivery?", "asked_by": "Local User", "date": "2026-04-11T00:00:00.000Z", "answers": [ { "text": "Yes, we offer delivery within a 5-mile radius!", "author": "Business Owner", "date": "2026-04-12T00:00:00.000Z", "upvotes": 5 } ] } ], "total_questions": 2 } ``` --- ### POST /v1/business/listings **Use when:** bulk discovery of businesses in a category + location (not single business lookup). **Cost:** 10 credits per 50 results Request: ```json { "category": "Pizza Restaurant", "location": "Buffalo, NY" } ``` Optional: `limit` (1-200, default 50) Response `data`: ```json { "businesses": [ { "name": "Acme Pizza", "rating": 4.6, "reviews_count": 287, "address": "123 Main St, Buffalo, NY", "phone": "(555) 123-4567", "place_id": "ChIJ_abc123", "categories": ["Pizza Restaurant"] } ], "total_available": 342 } ``` --- ## Keyword Research ### POST /v1/keywords/search-volume **Use when:** you have keywords and need monthly volume, CPC, and competition data. Up to 1000 keywords per call. **Cost:** 72 credits flat Request: ```json { "keywords": ["plumber buffalo", "emergency plumber near me"], "location": "Buffalo, NY" } ``` Optional: `language` (default "en") Response `data`: ```json { "keywords": [ { "keyword": "plumber buffalo", "search_volume": 2400, "cpc": 42.50, "competition": 0.73, "competition_level": "HIGH", "monthly_searches": [ { "year": 2026, "month": 5, "search_volume": 2600 }, { "year": 2026, "month": 4, "search_volume": 2200 } ] } ], "total_count": 2, "location": "Buffalo, NY", "location_used": "Buffalo,New York,United States", "volume_scope": "local", "data_source": "google_ads", "language": "en" } ``` Volumes are city/state-scoped when local data exists (`volume_scope: "local"`). When there is no local data, the API falls back to country-level numbers and says so (`volume_scope: "country"`, `data_source: "labs"`, `location_used` shows the actual scope). A `null` search_volume means no data — not zero searches. Unresolvable locations return a validation error instead of silently using national numbers. When the requested location has sibling entities in the same state (e.g. "Hamilton" and "Hamilton Township", NJ — distinct entities whose volumes can differ 3-5x), the response includes `location_alternatives: [{name, code, type}]`. Check it before treating a small number as the whole market; re-query with the alternative name for its volumes. --- ### POST /v1/keywords/suggestions **Use when:** generating new keyword ideas from a seed keyword. **Cost:** 92 credits Keyword ideas come from a country-level keyword database; volumes and CPC are then localized via Google Ads for city/state locations (`volume_scope: "local"`, `data_source: "google_ads"`), with a labeled country-level fallback when no local data exists (`volume_scope: "country"`, `data_source: "labs"`). Responses include `location_used` and, when the place has sibling entities (townships/boroughs), `location_alternatives`. `keyword_difficulty` remains national. A `null` search_volume means no data — not zero searches. Request: ```json { "keyword": "plumber", "location": "Buffalo, NY" } ``` Optional: `limit` (default 20), `include_seed_keyword` (default true) Response `data`: ```json { "seed_keyword": { "keyword": "plumber", "search_volume": 3200, "keyword_difficulty": 45, "cpc": 38.00, "serp_features": ["local_pack", "people_also_ask"] }, "suggestions": [ { "keyword": "plumber near me", "search_volume": 1800, "keyword_difficulty": 52, "cpc": 45.00, "serp_features": ["local_pack", "people_also_ask"] } ], "total_count": 8, "location": "Buffalo, NY" } ``` --- ### POST /v1/keywords/overview **Use when:** you have keywords and want difficulty and search intent alongside volume. **Cost:** 10 credits for one keyword, scaling with list size (20 for 100, 116 for 1,000) Request: ```json { "keywords": ["roof repair", "emergency roofer"], "location": "Austin, TX" } ``` Optional: `language` (ISO 639-1 code or name; defaults to the market's language) Response `data`: ```json { "keywords": [ { "keyword": "roof repair", "search_volume": 1600, "cpc": 38.50, "competition": 0.72, "competition_level": "HIGH", "monthly_searches": [{ "year": 2026, "month": 8, "search_volume": 1900 }], "keyword_difficulty": 42, "search_intent": "transactional", "secondary_intents": ["commercial"], "avg_backlinks": { "backlinks": 1840, "dofollow": 1502, "referring_pages": 967, "referring_domains": 212, "referring_main_domains": 188, "rank": 284, "main_domain_rank": 612 } } ], "total_count": 1, "location": "Austin, TX", "location_used": "United States", "volume_scope": "country", "data_source": "labs", "language": "en" } ``` Volumes are country-level (`volume_scope: "country"`) — use `/v1/keywords/search-volume` when the city/state number is what matters. A keyword the database has nothing on comes back with null metrics rather than being dropped from the list. `search_intent` is one of informational, navigational, commercial, transactional. `avg_backlinks` is the averaged link profile of the pages currently ranking: `rank` and `main_domain_rank` are 0-1000 authority scores. ### POST /v1/keywords/for-site **Use when:** reverse-engineering what keywords a competitor domain ranks for. **Cost:** 20 credits Request: ```json { "domain": "competitor.com", "location": "Buffalo, NY" } ``` Optional: `limit` (default 20) Response `data`: ```json { "keywords": [ { "keyword": "competitor service", "search_volume": 1200, "cpc": 35.00, "competition": 0.65 } ], "total_count": 8, "domain": "competitor.com", "location": "Buffalo, NY" } ``` --- ### POST /v1/keywords/related **Use when:** expanding a keyword list with semantically related terms. **Cost:** 72 credits Request: ```json { "keywords": ["plumber"], "location": "Buffalo, NY" } ``` Optional: `limit` (default 10) Response `data`: ```json { "keywords": [ { "keyword": "plumber near me", "search_volume": 900, "cpc": 28.00, "competition": 0.55, "competition_level": "MEDIUM" } ], "total_count": 10, "seed_keywords": ["plumber"], "location": "Buffalo, NY" } ``` Results are sorted by volume. A `null` search_volume means no data — not zero searches. --- ### POST /v1/keywords/trends **Use when:** checking if a keyword is rising, stable, or declining over 3-60 months. **Cost:** 9 credits Request: ```json { "keywords": ["plumber near me", "emergency plumber"], "location": "Buffalo, NY" } ``` Optional: `period` ("3m"|"6m"|"12m"|"5y", default "12m") Response `data`: ```json { "trends": [ { "keyword": "plumber near me", "data_points": [ { "month": "2025-06", "interest": 45 }, { "month": "2025-07", "interest": 52 } ], "peak_month": "2026-01", "trend_direction": "rising" } ], "total_count": 2, "keywords": ["plumber near me", "emergency plumber"], "location": "Buffalo, NY", "period": "12m" } ``` --- ### POST /v1/keywords/local-opportunities **Use when:** finding the best local keywords to target based on difficulty vs current rank. **Cost:** 4 credits Request: ```json { "category": "plumber", "location": "Buffalo, NY" } ``` Optional: `business_name`, `place_id` Response `data`: ```json { "keywords": [ { "keyword": "plumber near me", "estimated_difficulty": 45, "your_rank": 7, "search_volume": 2400, "top_competitor": null, "competitor_rank": null } ] } ``` --- ## AI Optimization ### POST /v1/ai/keyword-data **Use when:** measuring how often keywords are searched in ChatGPT and other LLMs. **Cost:** 9-16 credits by keyword count Request: ```json { "keywords": ["best plumber", "emergency plumber"] } ``` Optional: `location` Response `data`: ```json { "total_keywords": 2, "keywords": [ { "keyword": "best plumber", "ai_search_volume": 1800, "monthly_searches": [ { "year": 2026, "month": 5, "ai_search_volume": 2100 }, { "year": 2026, "month": 4, "ai_search_volume": 1600 } ] } ] } ``` --- ### POST /v1/ai/mentions **Use when:** finding where a keyword appears in ChatGPT and Google AI outputs. **Cost:** 88 credits, up to 160 at limit 100 Request: ```json { "keyword": "best plumber in Buffalo" } ``` Optional: `location`, `platforms` (["chat_gpt","google"]), `limit` (default 10) Response `data`: ```json { "ai_search_volume": 1500, "total_mentions": 8, "mentions": [ { "question": "What is the best plumber?", "answer_snippet": "Based on reviews and ratings, popular options include...", "sources_cited": ["https://yelp.com/example", "https://business.com"], "platform": "chat_gpt", "date": "2026-05-01 00:00:00 +00:00" } ] } ``` --- ### POST /v1/ai/top-sources **Use when:** discovering which domains AI models cite most for a topic. **Cost:** 81 credits Request: ```json { "keyword": "plumber near me" } ``` Optional: `location`, `platforms`, `limit` (default 10) Response `data`: ```json { "keyword": "plumber near me", "top_domains": [ { "domain": "yelp.com", "mentions": 23, "ai_search_volume": 1800 }, { "domain": "homeadvisor.com", "mentions": 15, "ai_search_volume": 1200 } ] } ``` --- ### POST /v1/ai/top-pages **Use when:** finding specific pages (not just domains) that AI models reference most. **Cost:** 81 credits Request: ```json { "keyword": "plumber near me" } ``` Optional: `location`, `platforms`, `limit` (default 10) Response `data`: ```json { "keyword": "plumber near me", "top_pages": [ { "url": "https://yelp.com/biz/best-plumber", "domain": "yelp.com", "mentions": 12, "ai_search_volume": 1500 } ] } ``` --- ### POST /v1/ai/visibility **Use when:** measuring a domain's overall AI citation score across platforms. **Cost:** 81 credits Request: ```json { "domain": "acmeplumbing.com", "keywords": ["plumber buffalo", "emergency plumber"] } ``` Response `data`: ```json { "domain": "acmeplumbing.com", "total_mentions": 15, "total_impressions": 8500, "ai_search_volume": 4200, "platform_breakdown": { "chat_gpt": { "mentions": 10, "ai_search_volume": 2800, "impressions": 5500 }, "google": { "mentions": 5, "ai_search_volume": 1400, "impressions": 3000 } }, "top_sources": [ { "domain": "yelp.com", "mentions": 6, "impressions": 1800 } ] } ``` --- ### POST /v1/ai/compare **Use when:** comparing multiple domains' AI visibility side by side. **Cost:** 81 credits per domain compared (2 domains = 162) Request: ```json { "domains": ["acmeplumbing.com", "competitor.com"], "keywords": ["plumber buffalo", "emergency plumber"] } ``` Response `data`: ```json { "keywords": ["plumber buffalo", "emergency plumber"], "by_domain": [ { "domain": "acmeplumbing.com", "total_mentions": 12, "total_impressions": 5000, "ai_search_volume": 3200 }, { "domain": "competitor.com", "total_mentions": 8, "total_impressions": 3500, "ai_search_volume": 2100 } ] } ``` --- ### POST /v1/ai/llm-response **Use when:** getting what a specific LLM (ChatGPT, Claude, Gemini, Perplexity) says about a query. **Cost:** 8 credits by default. The optional `model` parameter is priced by tier: 8 standard, 20 premium (Opus-class), 40 frontier (Fable-class). Request: ```json { "prompt": "best plumber in Buffalo NY", "platform": "chat_gpt" } ``` Platform options: "chat_gpt", "claude", "gemini", "perplexity" Response `data`: ```json { "platform": "chat_gpt", "prompt": "best plumber in Buffalo NY", "response_text": "Based on available information, here are recommendations for plumbers in Buffalo...", "model": "gpt-4.1-mini", "sources": [ { "url": "https://source.com/article", "title": "Best Plumbers in Buffalo", "domain": "source.com" } ], "fan_out_queries": [ "best plumber in Buffalo NY near me", "best plumber in Buffalo NY reviews" ] } ``` --- ### POST /v1/ai/scraper **Use when:** capturing ChatGPT/Gemini search results. Budget alternative to llm_response at 4 credits. **Cost:** 4 credits **Location:** Gemini supports city-level (e.g. "Portland, OR"). ChatGPT resolves to country level. Request: ```json { "keyword": "best plumber in Buffalo", "platform": "gemini", "location": "Buffalo, NY" } ``` Platform options: "chat_gpt", "gemini" Response `data`: ```json { "keyword": "best plumber in Buffalo", "platform": "gemini", "model": "gemini-2.5-flash", "markdown": "Based on reviews, here are the best options...\n\n1. **Business A** - Highly rated\n2. **Business B** - Great reviews", "search_results": [ { "title": "Result title", "url": "https://result.com", "domain": "result.com", "description": "Result description..." } ], "sources": [ { "title": "Source title", "url": "https://source.com", "domain": "source.com", "snippet": "Expert guide..." } ], "fan_out_queries": ["best plumber in Buffalo near me", "best plumber reviews"], "brand_entities": [ { "title": "Brand Name", "category": "local_business" } ] } ``` --- ### POST /v1/ai/sample **Use when:** the question is how OFTEN an AI names a business, not whether it did once. A model composes its answer per request, so one call is a sample and not a fact. **Cost:** `samples` x the per-run price -- 4 credits a run on chat_gpt and gemini, 2 on google_ai_mode. No batch discount. **Async:** the POST returns a job; poll `poll_url`. The runs execute in parallel, so expect ~60s on google_ai_mode and ~120s on the capture endpoints, not samples x that. Request: ```json { "keyword": "best plumber in Buffalo", "platform": "chat_gpt", "samples": 10, "location": "Buffalo, NY" } ``` Platform options: "chat_gpt", "gemini", "google_ai_mode" (google_ai_mode requires a location) Response `data` once complete: ```json { "keyword": "best plumber in Buffalo", "platform": "chat_gpt", "samples_requested": 10, "samples_returned": 10, "samples_failed": 0, "businesses": [ { "name": "Bolt Plumbing", "matched_on": "domain", "cid": "", "domain": "boltplumbing.com", "appearances": 8, "appearance_rate": 0.8, "best_rank": 1, "worst_rank": 4, "mean_rank": 2.13, "runs_seen": [1, 2, 3, 5, 6, 8, 9, 10] } ], "runs": [ { "run": 1, "ok": true, "businesses": [], "response_text": "For plumbing in Buffalo..." } ] } ``` `appearance_rate` is out of `samples_returned`, not `samples_requested`: a run that did not complete is a measurement not taken, so it is refunded rather than counted as an absence. `mean_rank` averages only the runs that named the business -- named 2nd in 3 of 10 runs is `mean_rank` 2 and `appearance_rate` 0.3, which are different problems. Results are never cached. --- ### POST /v1/ai/match **Use when:** deciding whether a business an AI named is the business being tracked. **Cost:** 5 credits when the target is resolved against the business graph, 1 when you supply a cid, domain, website or phone yourself. Matching on the domain alone misses roughly a third of appearances -- that is how often ChatGPT names a business with an empty website. Matching on the name alone fails on AI Mode, where Google labels cards editorially: the same business came back as "The Classic Staple" in one answer and under its trading name in the next. Request: ```json { "target": { "business_name": "Cellino Plumbing", "location": "Buffalo, NY" }, "businesses": [ { "rank": 1, "name": "Roto-Rooter", "domain": "rotorooter.com" }, { "rank": 2, "name": "The Classic Staple", "phone": "716-631-8080" } ] } ``` Pass `businesses` for one answer, or a `/v1/ai/sample` `runs` array for a rate across the sample. Never both. Extra fields on a business object are ignored, so an AI endpoint's array goes through untouched. Response `data`: ```json { "target": { "business_name": "Cellino Plumbing", "resolved_from": "business_profile", "cid": "12345678901234567", "website": "https://www.cellino.com", "phone": "+1 716-631-8080", "address": "3 Coventry Green, Buffalo, NY 14221" }, "matched": true, "confidence": 0.9, "matched_on": "phone", "rank": 2, "match": { "rank": 2, "name": "The Classic Staple", "phone": "716-631-8080" }, "candidates": [ { "name": "The Classic Staple", "score": 0.9, "matched_on": "phone", "rank": 2 } ] } ``` `matched_on` is `cid` (1.0), `domain` (0.9), `phone` (0.9), `name_address` (0.8), `name` (0.4-0.65) or `none`. Filter on it rather than on `confidence` when identifier-grade certainty is needed: a name hit and a domain hit are different kinds of answer, not the same answer at different strengths. --- ## Backlinks & Site ### POST /v1/backlinks/summary **Use when:** getting a domain's backlink count, referring domains, spam score, and authority. **Cost:** 23 credits Request: ```json { "domain": "acmeplumbing.com" } ``` Response `data`: ```json { "domain": "acmeplumbing.com", "rank": 450, "backlinks_total": 2340, "referring_domains": 187, "referring_domains_nofollow": 23, "broken_backlinks": 4, "spam_score": 8, "backlinks_by_type": { "anchor": 1800, "image": 120, "redirect": 45 } } ``` --- ### POST /v1/backlinks/gap **Use when:** finding sites that link to competitors but not to you. Link building opportunities. **Cost:** 23 credits Request: ```json { "your_domain": "acmeplumbing.com", "competitor_domains": ["competitor1.com", "competitor2.com"] } ``` Response `data`: ```json { "your_domain": "acmeplumbing.com", "opportunities": [ { "referring_domain": "directory1.com", "domain_authority": 65, "links_to_competitors": [ { "domain": "competitor1.com", "backlinks": 3 } ], "links_to_you": false } ], "total_opportunities": 47 } ``` --- ### POST /v1/site/page-audit **Use when:** auditing a single page's on-page SEO: title, meta, headings, Core Web Vitals, schema, mobile. **Cost:** 1 credits Request: ```json { "url": "https://acmeplumbing.com" } ``` Response `data`: ```json { "seo_score": 72, "title": "Acme Plumbing - Buffalo, NY", "meta_description": "A local business providing quality service...", "h1": ["Welcome to Acme Plumbing"], "word_count": 1450, "load_time_ms": 2300, "core_web_vitals": { "lcp": 2400, "cls": 0.08, "inp": null, "source": "lab", "note": "Lab measurements from a single crawl. INP needs real-user (field) data and is not measured." }, "issues": [ { "severity": "warning", "issue": "Images missing alt text (3 of 8)" }, { "severity": "info", "issue": "No FAQ schema markup detected" } ], "schema_markup": ["LocalBusiness"], "mobile_friendly": true } ``` --- ## Competitive Intelligence ### POST /v1/ads/competitor **Use when:** seeing what Google Ads a competitor domain is running. **Cost:** 2 credits Request: ```json { "domain": "competitor.com" } ``` Optional: `limit` (default 10) Response `data`: ```json { "advertiser": "competitor.com LLC", "total_ads": 23, "ads": [ { "title": "Ad 1 - competitor.com", "format": "text", "platforms": ["google_search", "google_maps"], "first_shown": "2025-11-15", "last_shown": "2026-05-10" } ] } ``` --- ### POST /v1/brand/mentions **Use when:** monitoring where a brand is mentioned online with sentiment analysis. **Cost:** 23 credits Request: ```json { "business_name": "Acme Plumbing" } ``` Optional: `location`, `limit` (default 10) Response `data`: ```json { "total_mentions": 28, "sentiment_breakdown": { "positive": 18, "neutral": 7, "negative": 3 }, "mentions": [ { "title": "Article mentioning Acme Plumbing", "url": "https://news.com/article", "domain": "news.com", "domain_authority": 55, "sentiment": "positive", "snippet": "Acme Plumbing was mentioned in this article about local services.", "date": "2026-05-01" } ] } ``` --- ## Composite Reports ### POST /v1/audit/local **Use when:** running a comprehensive local SEO check: SERP + profile + reviews + competitors. Expensive but thorough. **Cost:** 50 credits. Async job, takes 15-45 seconds. Request: ```json { "business_name": "Acme Plumbing", "location": "Buffalo, NY", "keyword": "plumber" } ``` Optional: `place_id` Response `data`: ```json { "local_pack_position": 2, "organic_positions": [ { "keyword": "plumber", "rank": 5 } ], "rating": 4.6, "reviews_count": 287, "profile_completeness": 72, "review_velocity": 8.3, "competitors": [ { "name": "Competitor Plumbing", "rank": 1, "rating": 4.8, "reviews_count": 412, "profile_completeness": 85 } ], "recommendations": [ "Improve profile completeness -- add missing business description", "Increase review response rate to above 50%", "Add more photos (aim for 20+)" ] } ``` --- ### POST /v1/audit/reputation **Use when:** analyzing cross-platform review reputation with sentiment. Takes 10-30 seconds. **Cost:** 30 credits Request: ```json { "business_name": "Acme Plumbing", "location": "Buffalo, NY" } ``` Optional: `place_id` Response `data`: ```json { "reputation_score": 78, "platforms": { "google": { "rating": 4.6, "count": 287, "trend": "stable" } }, "sentiment": { "positive": ["great", "friendly"], "negative": ["slow"] }, "response_rate": 0.45, "recommendations": [ "Respond to at least 50% of reviews", "Encourage satisfied customers to leave reviews" ] } ``` --- ### POST /v1/report/competitor-gap **Use when:** comparing your business vs local competitors on rankings, reviews, and profile completeness. **Cost:** 10 credits + 2 per competitor analyzed (default 5 competitors = 20 credits) Request: ```json { "business_name": "Acme Plumbing", "location": "Buffalo, NY", "keyword": "plumber" } ``` Optional: `place_id`, `competitors` (number, default 5) Response `data`: ```json { "your_business": { "name": "Acme Plumbing", "rank": 2, "rating": 4.6, "reviews_count": 287, "profile_completeness": null }, "competitors": [ { "name": "Competitor Plumbing", "rank": 1, "rating": 4.8, "reviews_count": 412, "profile_completeness": null, "advantages": ["More reviews", "Better photos"] } ], "your_gaps": [ "Fewer reviews than top competitor", "Profile completeness below average" ] } ``` --- ### POST /v1/score/local-authority **Use when:** calculating a composite 0-100 local authority score with component breakdown. **Cost:** 10 credits Request: ```json { "business_name": "Acme Plumbing", "location": "Buffalo, NY", "keyword": "plumber" } ``` Optional: `place_id` Response `data`: ```json { "authority_score": 67, "breakdown": { "ranking": { "score": 18, "max": 25, "detail": "Ranks #2 in local pack" }, "reviews": { "score": 22, "max": 30, "detail": "4.6 stars, 287 reviews" }, "profile": { "score": 17, "max": 25, "detail": "72% complete" }, "citations": { "score": 10, "max": 20, "detail": "Estimated based on profile signals" } }, "percentile": "top 15% in your category for this location" } ``` --- ## Audits ### POST /v1/audit/citation **Use when:** checking NAP (Name, Address, Phone) consistency across business directories. **Cost:** 50 credits Request: ```json { "business_name": "Acme Plumbing", "location": "Buffalo, NY" } ``` Optional: `place_id`, `phone`, `address`, `directories` `directories` controls the scan: a count from 1 to 50 (default 20, taken in priority order) or an explicit array of directory names, e.g. `["yelp", "bbb", "houzz"]`. Names match the values returned in `details[].directory`. Cost is 2.5 credits per directory scanned (default 20 = 50 credits, all 50 = 125). Every directory you select is searched, listed or not, so `directories_checked` always equals what you asked for. `found` is how many of them actually list the business, and is usually much smaller. Response `data`: ```json { "consistency_score": 78, "directories_checked": 20, "found": 15, "consistent": 10, "inconsistent": 3, "unverified": 2, "not_found": 5, "details": [ { "directory": "Yelp", "status": "consistent", "name_match": true, "address_match": true, "phone_match": true }, { "directory": "Yellow Pages", "status": "inconsistent", "name_match": true, "address_match": false, "phone_match": true, "expected_address": "123 Main St, Buffalo, NY" }, { "directory": "Superpages", "status": "unverified", "name_match": true, "address_match": true, "phone_match": null } ] } ``` Match fields are three-state: `true` (verified), `false` (conflicting value found), `null` (listing found but the field isn't visible in search data). `consistency_score` is a partial-credit average over found listings and is `null` when nothing was found. --- ## Geogrid Rank Scans ### POST /v1/geogrid/scan **Use when:** creating a geographic rank map showing where a business ranks across a grid area. Best for visualizing local rank coverage. **Cost:** 2 credits per grid point. The default 5x5 is 50 credits; setting grid_size costs 18 (3x3), 98 (7x7) or 162 (9x9). Points that can't be checked are refunded. Async job, usually takes 25-40 seconds at any size. Request: ```json { "business": "Acme Plumbing", "keyword": "plumber", "location": "Buffalo, NY", "grid_size": "5x5", "radius_miles": 3 } ``` Required: `keyword`; `business` or `place_id`; `location` or `center`. Optional: - `place_id`: exact listing match (preferred over `business`, which matches by name) - `center`: `{"lat": 42.886, "lng": -78.878}`, overrides `location`; use the business's own coordinates for tracking over time - `grid_size`: "3x3", "5x5" (default), "7x7", "9x9"; larger grids cost more - `radius_miles`: center to edge, default 3 - `zoom`: "auto" (14, default) or 3-21 - `include_listings`: true adds the top 20 organic listings at every point (same price, larger response) - `callback_url`: public https URL to POST the finished scan to Response `data`: ```json { "grid": [ [3, 1, 2, 0, 5], [2, 1, 1, 3, 0], [4, 2, 1, 2, 3], [0, 3, 2, 4, 5], [0, 0, 4, 3, 0] ], "grid_points": [ { "row": 0, "col": 0, "lat": 42.929478, "lng": -78.937339, "rank": 3, "status": "ok" }, { "row": 0, "col": 1, "lat": 42.929478, "lng": -78.90767, "rank": 1, "status": "ok" } ], "average_rank": 2.7, "atrp": 7.1, "solv": 56, "found_in": 19, "failed_points": 0, "total_points": 25, "credits_refunded": 0, "center": { "lat": 42.886, "lng": -78.878 }, "grid_size": "5x5", "radius_miles": 3, "keyword": "plumber", "business_name": "Acme Plumbing", "zoom": 14, "zoom_mode": "auto", "cached": false, "completed_at": "2026-05-11T12:00:00.000Z" } ``` `grid` is a 2D array indexed [row][col]. 0 means not found in the top 20 (or a failed point; check `status` in `grid_points`). `grid_points` is a flat array with lat/lng for plotting on a map. Ranks count organic listings only; sponsored listings are skipped. `average_rank` (ARP) averages the points where the business was found; `atrp` averages every answered point with not-found as 21; `solv` is the % of answered points in the top 3. Failed points are left out of all three and refunded at 2 credits each. ### POST /v1/geogrid/map **Use when:** a client or an agent needs to see a finished geogrid scan, not read it. Draws the scan on a street map, with the business, keyword and headline numbers on top, and returns a PNG or WebP. **Cost:** 2 credits. Refunded if the map cannot be drawn. A scan that is still running returns 409 job_not_complete and costs nothing. Request: ```json { "job_id": "geo_a1b2c3d4e5", "size": 800, "style": "light" } ``` Required: `job_id` of a completed scan on your account. Optional: - `size`: 600, 800 (default) or 1000 - `scale`: 2 (default) draws twice size in pixels for sharing; 1 draws size in pixels for chat and email - `format`: "png" (default) or "webp" (about a fifth of the size) - Send `Accept: image/png` or `image/webp` to get the image itself instead of JSON (image/webp also picks WebP when format isn't set) - `style`: "light" (default) or "dark" - `compare`: false to leave out the change since your previous scan of the same keyword, business, grid, zoom, radius and centre (default true) - `compare_to`: job_id of a specific earlier scan to compare against instead, e.g. the first scan of an engagement. It must be your own completed, older scan of the same keyword, business, grid size, zoom, radius and centre; otherwise 409 with nothing charged. It applies even with compare: false Response `data`: ```json { "job_id": "geo_a1b2c3d4e5", "image_base64": "iVBORw0KGgoAAAANSUhEUgAA...", "mime_type": "image/png", "width": 1600, "height": 1600, "style": "light", "format": "png", "business_name": "Joe's Plumbing", "keyword": "plumber", "atrp": 10.1, "found": 24, "points": 25, "compared_to": { "job_id": "geo_9z8y7x6w5v", "date": "2026-10-01T12:04:00.000Z", "atrp": 12.4 }, "attribution": "© OpenMapTiles © OpenStreetMap contributors" } ``` Circles are numbered with the rank and coloured green (1-3), lime (4-7), orange (8-10) or red (11 and below); grey circles are points where the business was not found (–) or that couldn't be checked (?). `atrp` is the average rank on the map, with not found counted as 21; the scan's `average_rank` counts found points only. When an earlier scan of the same grid exists, each point shows how many places it moved and `compared_to` names that scan. Keep the attribution with the image. The map is drawn per request and not stored. Returns 503 not_configured, with nothing charged, where maps are not turned on. --- ## Location Utilities ### GET /v1/locations/search **Use when:** resolving city names to the exact format needed by other tools. Call this first when unsure of format. **Cost:** Free (0 credits) Request: `GET /v1/locations/search?q=cherry+hill&state=NJ` Optional: `state` (name or abbreviation), `country` (name or US/USA/UK), `limit` (1-50, default 10) Response `data`: ```json { "locations": [ { "name": "Cherry Hill Township,New Jersey,United States", "code": 1022070, "type": "City", "country": "United States", "state": "New Jersey", "parent_code": 21147 } ], "total_count": 1, "truncated": false } ``` Prefer a bare place name + `state` filter: many municipalities exist twice as distinct entities (e.g. "Hamilton" AND "Hamilton Township" in NJ) with very different search volumes, and the state filter surfaces all of them. `type` is the location type (City, County, State, DMA Region, ...). --- ## Account ### GET /v1/account/balance **Use when:** checking available credits before starting work. Always call this first. **Cost:** Free (0 credits) Response `data`: ```json { "credits_remaining": 47, "daily_spending_cap": null, "plan": "starter", "environment": "live" } ``` --- ## Links - Documentation: https://localseodata.com/docs - Quick reference: https://localseodata.com/llms.txt - MCP setup: https://localseodata.com/llms-install.md - Pricing: https://localseodata.com/pricing - Sign up: https://localseodata.com/signup