{"ok":true,"name":"M3 API","api":"https://api.m3sys.org","status":"operational","generated_at":"2026-08-10T00:56:56.810Z","services":[{"service":"scrape","name":"gbp-scraper","status":"ok","health":{"ok":true,"service":"gbp-scraper"},"health_latency_ms":145,"endpoints":[{"path":"/scrape/gbp","methods":["POST"],"description":"Scrape GBP data. Two modes: (1) single profile from gbp_link, or (2) keyword search that scrapes ALL results up to max and saves each as a lead in D1.","request_body":{"gbp_link":"string (optional) — Google Maps URL of the business profile (single-scrape mode)","business":"string (optional) — business name hint for single-scrape mode","keyword":"string (optional) — search term (search mode; required if no gbp_link)","city":"string (optional) — city filter for search mode","state":"string (optional) — state filter for search mode","max":"integer (optional, default 10, 1-50; -1 for all results) — max results to scrape in search mode"},"response":"{ profile: GbpProfile } OR { keyword, city, state, max, results, leads_saved, profiles: GbpProfile[] }"},{"path":"/scrape/gbp/search","methods":["POST"],"description":"Search Google Maps by business name (+ optional location).","request_body":{"business":"string (required)","location":"string (optional)"},"response":"{ profile: GbpProfile }"},{"path":"/scrape/gbp/places","methods":["POST"],"description":"Search the Google Places API by a business name.","request_body":{"business":"string (required)"},"response":"{ profile: GbpProfile }"},{"path":"/scrape/gbp/competitors","methods":["POST"],"description":"Fetch competitor data for a business category.","request_body":{"category":"string (required)"},"response":"{ competitors: Competitor[] }"},{"path":"/scrape/gbp/qualify","methods":["POST"],"description":"Score existing scraped leads against qualification rules and store qualifying leads in the qualified_leads table.","request_body":{"max_reviews":"integer (optional, default 100) — skip leads with this many or more reviews","check_website":"boolean (optional, default false) — live-check websites for breakage"},"response":"{ qualified, disqualified, total, config, disqualify_reasons }"},{"path":"/scrape/gbp/leads","methods":["GET"],"description":"List qualified leads from the qualified_leads table.","response":"{ total, limit, offset, leads: QualifiedLead[] }"}]},{"service":"audit","name":"gbp-audit","status":"ok","health":{"ok":true,"service":"gbp-audit"},"health_latency_ms":16,"endpoints":[{"path":"/audit/gbp","methods":["POST"],"description":"Generate a full GBP audit report from a scraped profile.","request_body":{"profile":"GbpProfile (required) — profile returned by /scrape/gbp","request":"object (optional) — audit request data","competitors":"Competitor[] (optional)"},"response":"{ report: object, generated_at: ISO timestamp }"}]}],"endpoints_count":7}