{
  "openapi": "3.0.1",
  "info": {
    "title": "SiteVerdict — Local Business Leads (Google Places API + OSM)",
    "description": "Find local businesses by category and place (e.g. plumbers in Bristol) from OpenStreetMap (free, no key) or the official Google Places API with your own key. Phones, websites, ratings, hours. Grid search beyond 60 results, cost cap, no scraping.",
    "version": "1.0",
    "x-build-id": "3RAS3OtfECvhz8gnn"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/siteverdict~siteverdict-local-business-leads/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-siteverdict-siteverdict-local-business-leads",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/siteverdict~siteverdict-local-business-leads/runs": {
      "post": {
        "operationId": "runs-sync-siteverdict-siteverdict-local-business-leads",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/siteverdict~siteverdict-local-business-leads/run-sync": {
      "post": {
        "operationId": "run-sync-siteverdict-siteverdict-local-business-leads",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "queries": {
            "title": "Searches",
            "type": "array",
            "description": "One search per line in the form \"<what> in <where>\", e.g. \"plumbers in Bristol\", \"cafes near PL1 1AA\", \"dentists in Leeds\". The place is geocoded with OpenStreetMap Nominatim (or Google, see Geocoder). Without \"in <where>\" the search uses the area below (coordinates / bounding box / polygon).",
            "items": {
              "type": "string"
            }
          },
          "categories": {
            "title": "Categories (optional, combined with Locations)",
            "type": "array",
            "description": "Alternative to Searches: every category is searched in every location, e.g. [plumbers, electricians] x [Bristol, Bath].",
            "items": {
              "type": "string"
            }
          },
          "locations": {
            "title": "Locations (optional)",
            "type": "array",
            "description": "City, town, postcode or address, one per line. Used with Categories.",
            "items": {
              "type": "string"
            }
          },
          "source": {
            "title": "Data source",
            "enum": [
              "osm",
              "google",
              "both"
            ],
            "type": "string",
            "description": "OpenStreetMap: free open data (ODbL), no key, coverage varies by area. Google Places API: needs your own Google Maps Platform API key (Google bills you per request). Both: runs both and keeps each record's source.",
            "default": "osm"
          },
          "maxResultsPerQuery": {
            "title": "Max businesses per search",
            "minimum": 1,
            "maximum": 100000,
            "type": "integer",
            "description": "Stop each search after this many businesses are saved.",
            "default": 200
          },
          "maxResultsTotal": {
            "title": "Max businesses in total",
            "minimum": 1,
            "maximum": 1000000,
            "type": "integer",
            "description": "Safety cap for the whole run (you also control spend with the run's maximum total charge).",
            "default": 5000
          },
          "countryCode": {
            "title": "Country (ISO code)",
            "type": "string",
            "description": "Two-letter country code, e.g. \"gb\", \"us\", \"in\". Limits geocoding to that country, sets Google's region and formats phone numbers. Leave empty for worldwide."
          },
          "lat": {
            "title": "Centre latitude",
            "type": "string",
            "description": "Search around a point instead of a named place (decimal degrees, e.g. 50.3714)."
          },
          "lng": {
            "title": "Centre longitude",
            "type": "string",
            "description": "Decimal degrees, e.g. -4.1427."
          },
          "radiusMeters": {
            "title": "Radius (metres)",
            "minimum": 50,
            "maximum": 50000,
            "type": "integer",
            "description": "Radius around the centre point. Max 50,000.",
            "default": 2000
          },
          "boundingBox": {
            "title": "Bounding box",
            "type": "string",
            "description": "\"south,west,north,east\" in decimal degrees, e.g. \"50.360,-4.160,50.380,-4.120\"."
          },
          "polygon": {
            "title": "Polygon (GeoJSON)",
            "type": "object",
            "description": "A GeoJSON Polygon or Feature (coordinates in [lng, lat] order). For Google, the polygon only decides which grid tiles are searched: Google's terms forbid point-in-polygon analysis of Places coordinates, so Google results near the edge are not clipped."
          },
          "minRadiusMeters": {
            "title": "Minimum radius for small places (metres)",
            "minimum": 100,
            "maximum": 50000,
            "type": "integer",
            "description": "When a location is tiny (a postcode or address), search this radius around it.",
            "default": 2000
          },
          "tileSizeKm": {
            "title": "Google grid tile size (km)",
            "minimum": 0.2,
            "maximum": 50,
            "type": "number",
            "description": "Large areas are split into tiles of about this size; tiles that hit Google's 60-results-per-search cap are split again (quadtree). Smaller tiles = more complete results but more Google requests.",
            "default": 2
          },
          "maxTilesPerQuery": {
            "title": "Max tiles per search",
            "minimum": 1,
            "maximum": 2000,
            "type": "integer",
            "description": "Upper bound on areas searched per search including re-splits (half for the initial grid, half for splitting). Main lever on Google cost: each tile costs 1-3 Google requests.",
            "default": 40
          },
          "maxSplitDepth": {
            "title": "Max split depth",
            "minimum": 0,
            "maximum": 8,
            "type": "integer",
            "description": "How many times a saturated tile may be split into 4.",
            "default": 3
          },
          "osmTileSizeKm": {
            "title": "OpenStreetMap tile size (km)",
            "minimum": 1,
            "maximum": 50,
            "type": "number",
            "description": "OSM areas larger than 150 km² are queried in tiles of this size to keep each Overpass request light.",
            "default": 10
          },
          "googleApiKey": {
            "title": "Google Maps Platform API key",
            "type": "string",
            "description": "Your own key with \"Places API (New)\" enabled (see README for a 5-minute setup with budget alerts). Stored encrypted by Apify, never logged or written to the output. Without a key, Google searches run as a cost estimate only."
          },
          "googleFieldPreset": {
            "title": "Google fields (decides Google's price tier)",
            "enum": [
              "ids",
              "basic",
              "contact",
              "atmosphere"
            ],
            "type": "string",
            "description": "IDs only: place IDs (free SKU, storable). Basic: name, address, location, types, Maps link, status (Pro SKU). Contact: + phone, website, rating, review count, price level, hours (Enterprise SKU). Atmosphere: + editorial summary (Enterprise + Atmosphere SKU).",
            "default": "contact"
          },
          "googleBudgetUsd": {
            "title": "Google budget cap (USD, list price)",
            "minimum": 0,
            "maximum": 100000,
            "type": "number",
            "description": "The run stops cleanly before Google requests would exceed this amount at Google's list prices (before your free monthly usage). A cost estimate is saved as GOOGLE_COST_ESTIMATE.",
            "default": 5
          },
          "googleDryRun": {
            "title": "Dry run (estimate Google cost only)",
            "type": "boolean",
            "description": "Plan the grid and estimate Google cost without calling Google.",
            "default": false
          },
          "googleSearchMode": {
            "title": "Google search type",
            "enum": [
              "text",
              "nearby"
            ],
            "type": "string",
            "description": "Text Search (default): keyword search, up to 60 per tile. Nearby Search: by Google place type only, up to 20 per tile, needs \"Google place type\".",
            "default": "text"
          },
          "googleIncludedType": {
            "title": "Google place type (optional)",
            "type": "string",
            "description": "A Google Table A type such as \"plumber\", \"cafe\", \"dentist\". Biases Text Search (or restricts it with strict filtering); required for Nearby Search."
          },
          "googleStrictTypeFiltering": {
            "title": "Only that place type",
            "type": "boolean",
            "description": "With a Google place type: return only places of exactly that type.",
            "default": false
          },
          "googleIncludeServiceAreaBusinesses": {
            "title": "Include service-area businesses",
            "type": "boolean",
            "description": "Include businesses with no public address (e.g. mobile tradespeople). Text Search only.",
            "default": false
          },
          "googlePlaceIds": {
            "title": "Refresh stored place IDs (Place Details)",
            "type": "array",
            "description": "Place IDs you stored earlier (Google allows storing place IDs). Each costs one Place Details request at the tier of the chosen fields.",
            "items": {
              "type": "string"
            }
          },
          "googleExtraFields": {
            "title": "Extra Google fields (advanced)",
            "type": "array",
            "description": "Additional Place fields, e.g. \"shortFormattedAddress\", \"priceRange\". The most expensive field decides the SKU for every request.",
            "items": {
              "type": "string"
            }
          },
          "includeReviews": {
            "title": "Include Google reviews (up to 5)",
            "type": "boolean",
            "description": "Adds the up-to-5 reviews Google returns. Switches requests to the Enterprise + Atmosphere SKU ($40 per 1,000). Off by default.",
            "default": false
          },
          "includeReviewerNames": {
            "title": "Include reviewer names (personal data)",
            "type": "boolean",
            "description": "Reviewer names and profile links are personal data under UK GDPR. Off by default. If you display reviews, Google requires you to credit the author.",
            "default": false
          },
          "geocoder": {
            "title": "Geocoder",
            "enum": [
              "nominatim",
              "google"
            ],
            "type": "string",
            "description": "How place names become search areas. Nominatim (OpenStreetMap, free, 1 request/second). Google uses one Text Search Pro request per place name (billed to your key).",
            "default": "nominatim"
          },
          "googleMaxConcurrency": {
            "title": "Google parallel requests",
            "minimum": 1,
            "maximum": 10,
            "type": "integer",
            "description": "Requests in flight at once. Quota errors are retried with exponential backoff.",
            "default": 3
          },
          "languageCode": {
            "title": "Language",
            "type": "string",
            "description": "Language for Google results, e.g. \"en\", \"en-GB\", \"fr\".",
            "default": "en"
          },
          "googlePriceOverrides": {
            "title": "Google price overrides (advanced)",
            "type": "object",
            "description": "USD per 1,000 requests if Google's prices change or you have a volume discount, e.g. {\"textSearch.enterprise\": 30}. Keys: textSearch|nearbySearch|details . idsOnly|essentials|pro|enterprise|atmosphere."
          },
          "minRating": {
            "title": "Minimum rating",
            "minimum": 0,
            "maximum": 5,
            "type": "number",
            "description": "Google only (OpenStreetMap has no ratings, so OSM results would all be excluded). Also sent to Google as a server-side filter."
          },
          "minReviews": {
            "title": "Minimum number of reviews",
            "minimum": 0,
            "type": "integer",
            "description": "Google only (needs the Contact preset or higher)."
          },
          "hasWebsite": {
            "title": "Only businesses with a website",
            "type": "boolean",
            "description": "Keep only businesses that list a website.",
            "default": false
          },
          "ownWebsiteOnly": {
            "title": "...and the website is their own",
            "type": "boolean",
            "description": "With the option above: ignore Facebook, Instagram, Yell, Linktree etc. as websites.",
            "default": false
          },
          "noWebsiteOnly": {
            "title": "Only businesses WITHOUT their own website",
            "type": "boolean",
            "description": "Web-design prospecting: keep businesses with no website or only a social/listing page.",
            "default": false
          },
          "hasPhone": {
            "title": "Only businesses with a phone number",
            "type": "boolean",
            "description": "Keep only businesses that list a phone number.",
            "default": false
          },
          "openNow": {
            "title": "Open now",
            "type": "boolean",
            "description": "Google: only places open at request time. Not applied to OpenStreetMap.",
            "default": false
          },
          "operationalOnly": {
            "title": "Skip closed businesses",
            "type": "boolean",
            "description": "Drop places marked temporarily/permanently closed (Google businessStatus; OSM disused/closed tags).",
            "default": true
          },
          "osmTags": {
            "title": "Custom OpenStreetMap tags (advanced)",
            "type": "array",
            "description": "Override the category mapping with OSM tags, e.g. \"craft=plumber\", \"shop=bakery\", \"office\" (any value). See wiki.openstreetmap.org/wiki/Map_features.",
            "items": {
              "type": "string"
            }
          },
          "includeEmails": {
            "title": "Include e-mail addresses from OSM",
            "type": "boolean",
            "description": "Some OSM entries list a contact e-mail. Off by default: for sole traders an e-mail is personal data, and UK PECR restricts unsolicited marketing e-mails to individuals.",
            "default": false
          },
          "overpassUrl": {
            "title": "Overpass API endpoint",
            "type": "string",
            "description": "Default: the public overpass-api.de instance (fair use: well under 10,000 requests/day). For heavy use, run your own Overpass server or use a commercial instance.",
            "default": "https://overpass-api.de/api/interpreter"
          },
          "nominatimUrl": {
            "title": "Nominatim endpoint",
            "type": "string",
            "description": "Default: the public nominatim.openstreetmap.org (max 1 request/second, no bulk geocoding). Use your own instance for large jobs.",
            "default": "https://nominatim.openstreetmap.org"
          },
          "osmContact": {
            "title": "Contact for OSM servers (optional)",
            "type": "string",
            "description": "E-mail or URL added to the User-Agent sent to Overpass/Nominatim, as their usage policies ask."
          },
          "agencyLeadFinderExport": {
            "title": "Export for SiteVerdict Agency Lead Finder",
            "type": "boolean",
            "description": "Also save the businesses as key-value record AGENCY_LEAD_FINDER_INPUT in the Agency Lead Finder's \"businesses\" input format (one entry per website domain; businesses without a website are kept as no-website leads). You can also give that Actor this run's ID or dataset ID directly.",
            "default": false
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_UNBLOCKER_UNITS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}