{
  "openapi": "3.0.1",
  "info": {
    "title": "Website Email Extractor for Google Maps Leads & Domain Lists",
    "description": "Website email extractor and contact details scraper for bulk domain lists: best email per site with MX check, phones and social links. Also adds emails to Google Maps results (does not scrape Maps itself). $2/1,000 sites; nothing found = free.",
    "version": "0.1",
    "x-build-id": "by4SM3a53CFXGjQJn"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/tidytools~website-contact-extractor/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-tidytools-website-contact-extractor",
        "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/tidytools~website-contact-extractor/runs": {
      "post": {
        "operationId": "runs-sync-tidytools-website-contact-extractor",
        "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/tidytools~website-contact-extractor/run-sync": {
      "post": {
        "operationId": "run-sync-tidytools-website-contact-extractor",
        "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": {
          "domains": {
            "title": "Websites or company names",
            "type": "array",
            "description": "One website per line: acme.com, www.acme.com or a full URL; a column copied from a spreadsheet works. A company name works too with its city, address or phone on the same line (Joe's Pizza, New York). A path (acme.com/contact) is read as an extra page. Each website is one row, charged once (www variants and duplicates count once); blank, invalid and unverified lines are free. Up to 50,000 per run. JSON: {\"domains\":[\"acme.com\",\"Joe's Pizza, New York\"]}. Alternatives: startUrls or datasetId.",
            "items": {
              "type": "string"
            }
          },
          "startUrls": {
            "title": "Website URLs (optional)",
            "type": "array",
            "description": "The same as \"Websites or company names\", as a URL list; you can also upload or link a text/CSV file of websites. Bare domains belong in the list above (this editor rejects them).",
            "items": {
              "type": "object",
              "required": [
                "url"
              ],
              "properties": {
                "url": {
                  "type": "string",
                  "title": "URL of a web page",
                  "format": "uri"
                }
              }
            }
          },
          "findWebsitesForNames": {
            "title": "Find websites for company names",
            "type": "boolean",
            "description": "Boolean, true by default. With {\"findWebsitesForNames\":true,\"domains\":[\"Joe's Pizza, 7 Carmine St, New York, NY\"]}, a company-name line is looked up as a website. Put its city, address or phone on the same line, or paste tab/comma-separated columns with a Name,City,Phone header; names alone are ambiguous. Only verified websites are used. No verified website gives a free company_not_found row; false makes name lines free invalid rows.",
            "default": true
          },
          "namesCountry": {
            "title": "Country of the company names (optional)",
            "type": "string",
            "description": "2-letter country code (e.g. DE, GB, JP) so that company names are first tried on that country's domains (.de, .co.uk, .co.jp) before .com. Empty = .com, .io, .co."
          },
          "datasetId": {
            "title": "Google Maps / other source dataset ID (optional)",
            "type": "string",
            "description": "First run a Google Maps scraper (e.g. compass/crawler-google-places) or another directory scraper, then select that run's dataset here. This Actor reads websites listed in the dataset; it does not scrape Google Maps. For Maps results, it finds the website field automatically and keeps each place's name, address, phone, Maps link and placeId (placeName, placeAddress, placePhone, mapsUrl, placeId) so emails merge back into your export."
          },
          "datasetField": {
            "title": "Dataset field",
            "type": "string",
            "description": "Field (or \"rows\" column) with the website or domain, e.g. \"website\" (dot paths such as \"company.url\" work). Empty = found automatically: website, then url, domain, link... A field that only holds Google Maps links (the \"url\" of a Maps place) is skipped, so Google Maps results need no setting."
          },
          "datasetKeepFields": {
            "title": "Dataset fields to keep",
            "type": "array",
            "description": "Extra fields copied from each dataset item into the output's \"source\" object, e.g. totalScore, reviewsCount. Google Maps results already get flat place columns (placeName, placeAddress, placePhone, mapsUrl, placeId, placeCategory, placeCity, placeCountryCode) without this setting.",
            "items": {
              "type": "string"
            }
          },
          "rows": {
            "title": "Rows from a spreadsheet or CRM (API / n8n / Make)",
            "type": "array",
            "description": "Instead of a dataset: the rows of your sheet as JSON objects, e.g. [{\"row_number\": 2, \"Website\": \"acme.com\", \"Email\": \"\"}]. The website column is found automatically (Website, url, domain...) or set it in \"Dataset field\". Every row keeps its identifier (rowId) and gets its own output row, so you can write the result back to the same row."
          },
          "mapsData": {
            "title": "Pasted Google Maps JSON or CSV (optional)",
            "type": "string",
            "description": "Alternative to rows or datasetId: paste a Maps JSON array, an object with items, or CSV with a website column. A datasetId takes precedence over sample pasted data."
          },
          "mapsPlaceRows": {
            "title": "Google Maps place view (optional)",
            "type": "boolean",
            "description": "Return CRM-ready place columns and preserve each original Maps item in source. Default off leaves this Actor's existing output unchanged.",
            "default": false
          },
          "rowIdField": {
            "title": "Row identifier field",
            "type": "string",
            "description": "Field that identifies each row of the dataset or \"rows\" (returned as rowId). Empty = the first of rowId, row_number, placeId, id; otherwise the row position. Branches of a chain that share one website are read once but keep their own rowId."
          },
          "targetPersonNameField": {
            "title": "Person name column (optional)",
            "type": "string",
            "description": "Column in the source dataset or rows containing a person's full name, e.g. contactName. Empty = find personName, contactName or fullName automatically; a company Name column is never treated as a person. Marks a unique text pattern in an email already published on the website; it does not guess an address or verify who owns it."
          },
          "emailField": {
            "title": "Email column",
            "type": "string",
            "description": "Field with the email you already have (Email, emails, primaryEmail... are found automatically). Used by \"Only rows without an email\" and for \"fill\": the found email is only offered for rows where this field is empty, so your own values are never overwritten."
          },
          "phoneField": {
            "title": "Phone column",
            "type": "string",
            "description": "Field with the phone you already have (found automatically). The found phone is only put in \"fill\" when it is empty."
          },
          "onlyMissingEmail": {
            "title": "Only rows without an email",
            "type": "boolean",
            "description": "With a dataset or \"rows\": rows that already have an email are not read and not charged (they get one row with emailStatus already_had_email).",
            "default": false
          },
          "requiredContact": {
            "title": "Which results are charged?",
            "enum": [
              "any",
              "email"
            ],
            "type": "string",
            "description": "Choose which results are charged. \"any\" (default): a website with at least one email, phone number or social profile. \"email\": only websites where at least one email address is found are charged; websites with only a phone number or social profile are still returned, but free. The price per website is the same in both modes.",
            "default": "any"
          },
          "roleEmailsOnly": {
            "title": "Role mailboxes only (no personal emails)",
            "type": "boolean",
            "description": "Off (default): every business email published on the website is returned, including addresses that look like a named person (anna.meier@...). On: only shared role mailboxes (info@, contact@, sales@, support@, office@...) are returned and used for billing; personal-looking addresses are dropped before output.",
            "default": false
          },
          "dedupeStoreName": {
            "title": "Skip websites read in earlier runs (dedupe name)",
            "type": "string",
            "description": "Any name, e.g. \"my-lead-list\". Websites (by domain) read in an earlier run with the same name are not read or charged again: the earlier result is returned with fromPreviousRun. Kept in a key-value store \"contacts-<name>\" in your Apify account. Failed websites are not kept, so they are tried again. Empty = off."
          },
          "dedupeMaxAgeDays": {
            "title": "Reuse earlier results for (days)",
            "minimum": 0,
            "maximum": 3650,
            "type": "integer",
            "description": "With a dedupe name: results older than this are read again. 0 = no age limit.",
            "default": 90
          },
          "outputMode": {
            "title": "Output format",
            "enum": [
              "sites",
              "emailRows"
            ],
            "type": "string",
            "description": "\"One row per email\" splits each website into one row per email found, with the company name, primary phone, social profiles, the page where the email was found and whether it is on the website's own domain. The price is the same: each website is charged once (only its first row shows charged: true). A website with a phone or social profile but no email gives one row with email empty.",
            "default": "sites"
          },
          "maxPagesPerSite": {
            "title": "Pages per website",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "The home page plus likely contact pages (contact, imprint/Impressum, about, team, support, legal). When those show no email, pages left over go to other pages likely to name a mailbox (policies, catering, jobs, locations...). The price per website is the same however many pages are read. The prefilled example uses 1 (home page only) for a quick first run; the default is 8.",
            "default": 8
          },
          "excludePaths": {
            "title": "Pages not to read (paths)",
            "type": "array",
            "description": "Optional JSON array of site paths beginning with /, e.g. {\"excludePaths\":[\"/blog\",\"/privacy\"]}; enter one per line in the form. /blog excludes /blog/... but not /blogger; matching ignores case and query strings. Home pages and URLs you explicitly give are still read. Empty means no exclusion. A non-path entry causes a free invalid_input row and stops reading.",
            "items": {
              "type": "string"
            }
          },
          "maxConcurrency": {
            "title": "Parallel websites",
            "minimum": 1,
            "maximum": 20,
            "type": "integer",
            "description": "Global maximum websites processed at the same time (each reads up to 2 pages at once). Websites under the same registrable domain are additionally limited to one at a time; other domains can run in parallel.",
            "default": 10
          },
          "siteTimeoutSecs": {
            "title": "Time limit per website (seconds)",
            "minimum": 20,
            "maximum": 900,
            "type": "integer",
            "description": "After this time no further pages of a website are started and the pages read so far are returned (skipped pages are listed in timedOutPages). If even the home page is not read by then, the website fails (not charged).",
            "default": 120
          },
          "httpVia": {
            "title": "Plain HTTP requests from",
            "enum": [
              "auto",
              "backend",
              "direct",
              "vm"
            ],
            "type": "string",
            "description": "Some sites block requests from data centers. Auto retries from a second network before falling back to a real browser. If those routes are refused, Auto also tries our second server (Oracle, different IP) before a real browser.",
            "default": "auto"
          },
          "warpRetry": {
            "title": "Retry refused websites through a second network",
            "type": "boolean",
            "description": "On (default; used with Auto and Our servers only): when a website refuses every request (HTTP 403 or a refusal of automated clients, not a CAPTCHA page), its home page is asked once more through our Cloudflare WARP network with a standard browser User-Agent (still ending with our TidyTools token). A recovered website is a normal row at the normal price; if it refuses again or shows a CAPTCHA or human-verification page, the row stays blocked and free. CAPTCHAs are never solved.",
            "default": true
          },
          "proxyConfiguration": {
            "title": "Proxy (optional)",
            "type": "object",
            "description": "Only used for requests sent from Apify's network. Apify proxy usage is billed to your Apify account."
          },
          "respectRobotsTxt": {
            "title": "Skip pages robots.txt disallows",
            "type": "boolean",
            "description": "Off (default): the websites you list are read even where their robots.txt disallows automated crawlers, as when you open them yourself. Every request still identifies itself as TidyTools; we never log in (sign-in pages are reported as login_required, free), never solve CAPTCHAs and respect Retry-After. On: pages robots.txt disallows for our crawler (TidyToolsContacts) are skipped, and a website whose home page is disallowed is a free robots_disallowed row.",
            "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
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}