# K12 Schools Email Scraper (`scrapers-hub/k12-schools-email-scraper`) Actor

K12 Schools Email Scraper finds schools via Google Maps then crawls their sites for contact emails - returning name, address, phone, website, rating, coordinates and every email found. 🏫 EdTech outreach and district lead generation.

- **URL**: https://apify.com/scrapers-hub/k12-schools-email-scraper.md
- **Developed by:** [Scrapers Hub](https://apify.com/scrapers-hub) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.99 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## 🏫 K12 Schools Email Scraper – School Contact Data, Verified Emails & Phone Numbers

The **K12 Schools Email Scraper** finds K-12 schools in the locations you specify, visits each school's website, and extracts contact data — email addresses, phone numbers and social media profiles — alongside the school's name, full address, coordinates, rating and place identifier. Every row it writes combines a location record with the contact details actually found on that school's own site.

Education-sector outreach lives or dies on contact quality. District lists go stale, generic directories sell the same recycled CSV to everyone, and manually opening a few hundred school websites to copy the office email is not a plan. This K-12 schools email scraper automates that loop: search once, crawl the sites, optionally validate every address against DNS and MX records, and get back a contact list you can hand straight to your CRM or email platform.

***

### 📊 What Data Can You Extract with This K12 Schools Email Scraper?

The scraper writes one flat record per contact discovered. The fields group naturally into six categories.

| Category | Fields | What it gives you |
|---|---|---|
| 🏫 School identity | `name`, `place_id`, `platform`, `url`, `website` | The school name, its place identifier, the source platform, and the site that was crawled |
| 📍 Location data | `street_address`, `city`, `state`, `zip`, `country_code`, `full_address`, `lat`, `long` | A complete postal address plus latitude and longitude for mapping and territory assignment |
| 📧 Email contacts | `email`, `email_found`, `emails`, `scraped_emails`, `_detailed_emails` | The email addresses discovered on the school's website, both flattened and in list form |
| ✅ Email validation | `syntax_valid`, `dns_valid`, `mx_valid`, `smtp_valid`, `is_disposable`, `is_role_based`, `confidence_score`, `validation_errors` | Layered deliverability signals so you can filter before you send |
| ☎️ Other contact channels | `phone`, `scraped_phones`, `scraped_social_media` | Phone numbers and social profile links found on the same pages |
| ⭐ Reputation and crawl telemetry | `avg_rating`, `total_reviews`, `domain`, `job_id`, `status`, `emails_found`, `pages_scraped`, `scrape_status`, `error`, `scrape_error` | Public rating signals plus per-domain crawl statistics and failure reasons |

The most valuable field in practice is `confidence_score`. Combined with `is_role_based` and `mx_valid`, it lets you split a raw scrape into a high-confidence direct-contact list and a lower-confidence generic-inbox list — which is exactly the distinction that determines whether an education outreach campaign gets replies or bounces.

***

### 🌟 Key Features of the K12 Schools Email Scraper

| Feature | Description |
|---|---|
| 🗺️ Location-driven discovery | Search by term and location list, so you can sweep a city, a county or a set of states in one run |
| 🌐 Deep website crawling | Each school's site is crawled up to `maxPagesPerSite` pages, prioritising contact, about, staff and directory pages where school emails actually live |
| 🔍 Obfuscated email detection | Recognises addresses written as `name [at] domain [dot] org` and similar anti-scraping patterns, not just plain `mailto:` links |
| ✅ Multi-layer validation | Optional syntax, DNS, MX and SMTP checks feed a `confidence_score` alongside disposable and role-based flags |
| ☎️ Phone and social extraction | Phone numbers and social media profile links are pulled from the same crawl at no extra configuration cost |
| 📍 Geocoded records | `lat` and `long` on every row let you map coverage, assign territories or filter by radius |
| 🎯 Per-location or global quotas | `scrapeMaxBusinessesPerLocation` decides whether `maxBusinesses` is a per-location target or a total across the whole run |
| 📧 Email-first output | The scraper keeps crawling until it has met your target of schools that actually have an email, rather than filling the quota with blanks |
| 🔄 Automatic proxy rotation | Requests are routed through rotating proxies automatically, so no proxy setup is required from you |

***

### 🚀 Why Choose This K12 Schools Email Scraper?

**Contact data from the source, not a resold list.** Every email in the output was found on the school's own website during the run. That means it reflects the site as it is today, including addresses that were updated after the last commercial database refresh.

**Validation is built in, not a separate pipeline.** Turn on `validateEmails` and each address comes back with `syntax_valid`, `dns_valid`, `mx_valid`, `smtp_valid`, `is_disposable`, `is_role_based` and a numeric `confidence_score`. You can filter to deliverable addresses before an address ever reaches your sending platform.

**Full location context on every row.** Because each record carries `full_address`, `city`, `state`, `zip`, `lat` and `long` alongside the email, the output is immediately usable for territory planning, regional segmentation and CRM import without a separate geocoding step.

**Crawl telemetry makes gaps explainable.** `pages_scraped`, `emails_found`, `scrape_status` and `scrape_error` tell you why a particular school produced nothing — a dead site, a blocked crawl or genuinely no published contact address. That is the difference between a list you trust and one you second-guess.

***

### 📥 Input

```json
{
  "googleMapsSearchTerm": "elementary school",
  "googleMapsLocation": ["Austin, TX", "Round Rock, TX"],
  "maxBusinesses": 100,
  "maxPagesPerSite": 20,
  "scrapeMaxBusinessesPerLocation": false,
  "validateEmails": true
}
```

#### 🔧 K12 Schools Email Scraper Input Fields

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `googleMapsSearchTerm` | string | ✅ Yes | — | The search term used to find schools, e.g. `elementary school`, `middle school`, `private high school` |
| `googleMapsLocation` | array | ✅ Yes | `[]` | One or more locations to search in. Each entry is processed in turn |
| `maxBusinesses` | integer | No | `20` | How many schools with emails to collect. Interpreted per location or globally depending on `scrapeMaxBusinessesPerLocation` |
| `maxPagesPerSite` | integer | No | `20` | Maximum number of pages to crawl on each school's website while hunting for contact details |
| `scrapeMaxBusinessesPerLocation` | boolean | No | `false` | When `true`, `maxBusinesses` applies to each location separately; when `false` it is a global limit across the run |
| `validateEmails` | boolean | No | `false` | Run syntax, DNS, MX and SMTP validation on each discovered address and populate the confidence fields |
| `proxyConfiguration` | object | No | `{"useApifyProxy": true}` | Proxy settings for the run. Recommended for large-scale scraping to avoid IP blocks and rate limits |

The run stops early and logs an error if `googleMapsSearchTerm` or `googleMapsLocation` is missing — both are required for the scraper to know what to look for and where.

#### 💡 Input Examples

**Small validation run before committing to a big scrape**

```json
{
  "googleMapsSearchTerm": "public elementary school",
  "googleMapsLocation": ["Portland, OR"],
  "maxBusinesses": 10,
  "maxPagesPerSite": 10,
  "validateEmails": true
}
```

**Multi-city sweep with an equal quota per city**

```json
{
  "googleMapsSearchTerm": "high school",
  "googleMapsLocation": ["Denver, CO", "Boulder, CO", "Fort Collins, CO"],
  "maxBusinesses": 50,
  "scrapeMaxBusinessesPerLocation": true,
  "validateEmails": true
}
```

**Fast, wide harvest without validation**

```json
{
  "googleMapsSearchTerm": "charter school",
  "googleMapsLocation": ["Phoenix, AZ"],
  "maxBusinesses": 300,
  "maxPagesPerSite": 8,
  "validateEmails": false
}
```

***

### 📤 Output

A representative record combining school location data with a validated contact:

```json
{
  "name": "Riverside Elementary School",
  "place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4",
  "platform": "google_maps",
  "website": "https://riverside-elementary.example.org",
  "url": "https://riverside-elementary.example.org/contact",
  "domain": "riverside-elementary.example.org",
  "street_address": "1420 Riverside Drive",
  "city": "Austin",
  "state": "TX",
  "zip": "78704",
  "country_code": "US",
  "full_address": "1420 Riverside Drive, Austin, TX 78704, USA",
  "lat": 30.2451,
  "long": -97.7425,
  "phone": "+1 512-555-0142",
  "avg_rating": 4.3,
  "total_reviews": 87,
  "email_found": "office@riverside-elementary.example.org",
  "email": "office@riverside-elementary.example.org",
  "syntax_valid": true,
  "dns_valid": true,
  "mx_valid": true,
  "smtp_valid": false,
  "is_disposable": false,
  "is_role_based": true,
  "confidence_score": 78.5,
  "validation_errors": [],
  "scraped_phones": ["+1 512-555-0142"],
  "scraped_social_media": ["https://www.facebook.com/riversideelementary"],
  "emails_found": 3,
  "pages_scraped": 12,
  "scrape_status": "completed"
}
```

#### 🧾 K12 Schools Email Output Fields — School and Contact Data

| Field | Type | Description |
|---|---|---|
| `name` | string | School name |
| `place_id` | string | Identifier of the place |
| `platform` | string | Platform the record came from |
| `website` | string | The school's website address |
| `url` | string | Canonical URL of the scraped item |
| `street_address` | string | Street address |
| `city` | string | City |
| `state` | string | State or region |
| `zip` | string | ZIP code |
| `country_code` | string | ISO country code |
| `full_address` | string | Complete formatted address |
| `lat` | number | Latitude coordinate |
| `long` | number | Longitude coordinate |
| `phone` | string | Phone number found for the school |
| `avg_rating` | number | Average public rating |
| `total_reviews` | integer | Total number of public reviews |
| `email` | string | Email address found |
| `email_found` | string | The email address this row represents |
| `emails` | array | Email addresses found for the school |
| `scraped_emails` | array | Emails collected during the website crawl |
| `_detailed_emails` | array | Per-email detail collected during the crawl |
| `scraped_phones` | array | Phone numbers collected during the website crawl |
| `scraped_social_media` | array | Social media profile links collected during the crawl |

#### 🧾 K12 Schools Email Output Fields — Validation and Crawl Telemetry

| Field | Type | Description |
|---|---|---|
| `syntax_valid` | boolean | Whether the address passes syntax validation |
| `dns_valid` | boolean | Whether the domain resolves in DNS |
| `mx_valid` | boolean | Whether the domain publishes MX records |
| `smtp_valid` | boolean | Whether the SMTP-level check passed |
| `is_disposable` | boolean | Whether the address belongs to a disposable email provider |
| `is_role_based` | boolean | Whether the address is a role account such as `info@` or `office@` |
| `confidence_score` | number | Composite confidence score for the address |
| `validation_errors` | array | Validation problems recorded for the address |
| `job_id` | string | Identifier of the crawl job for that domain |
| `status` | string | Status of the crawl job |
| `domain` | string | The domain that was crawled |
| `emails_found` | integer | Number of emails found on that domain |
| `pages_scraped` | integer | Number of pages crawled on that domain |
| `scrape_status` | string | Outcome of the site crawl, e.g. `completed` or `no_website` |
| `error` | string | Error message, if the record failed to process |
| `scrape_error` | string | Error raised during the website crawl |

***

### 💻 How to Use the K12 Schools Email Scraper (Step by Step)

#### Step 1: Decide what kind of school you are targeting

`googleMapsSearchTerm` is a free-text search term, and the phrasing matters more than people expect. "Elementary school" and "public elementary school" return overlapping but different sets. If you are building a full K-12 list for an area, it is usually better to run several targeted scrapes — elementary, middle, high, charter, private — than one broad "school" query that pulls in driving schools, dance schools and tutoring centres.

#### Step 2: Build your location list

`googleMapsLocation` accepts an array, so a single run can cover many places. Use whatever granularity matches your outreach plan: city names for metro-level campaigns, county or region names for broader sweeps. Each location is processed in sequence and the log announces which one it is working on, so you can watch progress in real time.

#### Step 3: Choose how the quota is applied

This is the setting people most often get wrong. With `scrapeMaxBusinessesPerLocation` left at `false`, `maxBusinesses` is a total for the whole run — five cities with a limit of 100 gives you 100 schools, weighted toward whichever city was processed first. Set it to `true` and each of those five cities gets its own target of 100. Use `true` whenever you need balanced geographic coverage.

#### Step 4: Set the crawl depth per school website

`maxPagesPerSite` defaults to 20. School websites bury contact details in staff directories, department pages and PDF-heavy "about us" sections, so a deeper crawl generally finds more addresses — but it also takes longer per school. Ten pages is a reasonable compromise for a quick harvest; twenty or more when contact completeness matters more than run time.

#### Step 5: Turn on email validation

`validateEmails` defaults to `false` because validation adds time. For any list you intend to actually send to, turn it on. You get `syntax_valid`, `dns_valid`, `mx_valid` and `smtp_valid` as independent booleans, plus `is_disposable`, `is_role_based`, a numeric `confidence_score` and any `validation_errors`. Filtering on `mx_valid` alone removes a large share of addresses that would otherwise hard-bounce.

#### Step 6: Run and monitor the log

Start the run. The log reports the location being processed, the target count, and a per-school line showing the name and website as each one is crawled. Because the scraper keeps going until it has met your target of schools *with* emails, it will crawl more listings than your `maxBusinesses` number — that is expected behaviour, not a bug.

#### Step 7: Filter and export the results

Open the Dataset tab when the run finishes. For an outreach-ready list, filter to rows where `email_found` is non-empty and, if you validated, where `mx_valid` is `true` and `is_disposable` is `false`. Split `is_role_based` into its own segment — role accounts like `office@` and `info@` behave differently in campaigns to named staff addresses. Export as CSV for your email platform or JSON for programmatic use.

***

### 🔌 API Access & Integrations

Trigger the K12 schools email scraper over HTTP and get the dataset items back in one call:

```bash
curl -X POST "https://api.apify.com/v2/acts/scrapers-hub~k12-schools-email-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "googleMapsSearchTerm": "elementary school",
    "googleMapsLocation": ["Austin, TX"],
    "maxBusinesses": 50,
    "maxPagesPerSite": 15,
    "validateEmails": true
  }'
```

With the Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")

run = client.actor("scrapers-hub/k12-schools-email-scraper").call(run_input={
    "googleMapsSearchTerm": "high school",
    "googleMapsLocation": ["Denver, CO", "Boulder, CO"],
    "maxBusinesses": 100,
    "scrapeMaxBusinessesPerLocation": True,
    "validateEmails": True,
})

deliverable = []
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item.get("email_found") and item.get("mx_valid") and not item.get("is_disposable"):
        deliverable.append({
            "school": item.get("name"),
            "email": item["email_found"],
            "city": item.get("city"),
            "state": item.get("state"),
            "score": item.get("confidence_score"),
        })

print(f"{len(deliverable)} deliverable school contacts")
```

Results can be pushed straight into Zapier, Make, Google Sheets or Slack, or delivered to your own CRM endpoint with an Apify webhook on run completion.

***

### 💡 Best Use Cases for K12 Schools Contact Data

#### 📚 EdTech Sales Prospecting

EdTech vendors need the right school, the right region and a working inbox. Filter on `state` and `city` to match your sales territories, use `confidence_score` and `mx_valid` to prioritise deliverable addresses, and treat `avg_rating` and `total_reviews` as a rough proxy for school engagement. `place_id` gives you a stable key for deduplicating against an existing CRM.

#### 🏢 School Supplies and Services Outreach

Suppliers of furniture, catering, uniforms, transport and maintenance services can build regional target lists in a single run. `full_address`, `lat` and `long` support radius-based territory assignment, while `phone` and `scraped_phones` give a second channel for accounts where email does not land.

#### 📊 Education Market Research

Researchers and policy analysts can map school density, contactability and public reputation across regions. Aggregating `avg_rating` and `total_reviews` by `city` or `zip` gives a rating landscape; aggregating `emails_found` and `scrape_status` shows how digitally reachable schools are in different areas, which is itself a meaningful finding.

#### 🤝 Non-Profit and Programme Partnerships

Organisations running reading programmes, STEM initiatives, sports schemes or grant offers need direct contact with school offices. The combination of `email_found`, `is_role_based` and `scraped_social_media` gives you the office inbox for formal approaches and the social channel for warm-up contact.

#### 🗺️ Territory Planning and Field Sales Routing

With `lat` and `long` on every row, contact data becomes mappable. Cluster schools geographically to plan field visits, calculate drive-time territories, or find gaps where you have no coverage. `street_address` and `zip` support the same analysis at postcode level for reporting.

#### 📢 District-Level Campaign Building

Grouping records by `domain` reveals which schools sit on shared district infrastructure. That is often the fastest route to identifying district-wide decision makers rather than pitching each school individually, and `emails_found` per domain hints at how much contact surface each district exposes.

#### 🧹 CRM Hygiene and List Enrichment

Already have a school list? Run the scraper over the same locations and use `place_id`, `website` and `full_address` to match records, then overwrite stale emails with freshly scraped, validated ones. The `validation_errors` array tells you exactly why an old address failed, which is useful evidence when justifying a list cleanup.

***

### ⚙️ Tips for Better K12 Schools Email Scraping Results

- **Run one school type per scrape.** Separate runs for elementary, middle, high, charter and private schools produce cleaner, more complete lists than a single generic "school" query, which drags in unrelated businesses.
- **Enable `scrapeMaxBusinessesPerLocation` for multi-city runs.** Without it, the first location in your list can consume the entire quota and later cities return nothing.
- **Increase `maxPagesPerSite` when contact rates look low.** School sites often hide addresses in staff directory subpages. Going from 10 to 20 pages typically lifts `emails_found` noticeably.
- **Validate before you send, not after.** Turning on `validateEmails` costs run time but saves your sending reputation. A hard bounce rate driven by dead district addresses is much more expensive than a longer scrape.
- **Segment role-based addresses separately.** `is_role_based` flags `office@`, `info@` and `admin@` style inboxes. They are legitimate school contacts but respond to different messaging than a named staff address.
- **Start with a 10-school test run.** Confirm the search term returns actual K-12 schools in your target area before spending a large run on a query that is pulling in tutoring centres.

***

### 🛠️ Troubleshooting

**The run ended immediately with a missing-input error.**
Both `googleMapsSearchTerm` and `googleMapsLocation` are required. If either is empty the scraper logs an error and exits without scraping. Provide a search term string and at least one location in the array.

**I got fewer schools than `maxBusinesses`.**
The scraper only saves schools where an email was actually found, and it stops when it runs out of listings for the location. Widen the location, use a broader search term, or raise `maxPagesPerSite` so more sites yield a contact.

**Many rows have `scrape_status` set to `no_website`.**
Those schools have no website listed on their public profile, so there is nothing to crawl for emails. They are still written to the dataset with their location and phone data, which is often useful even without an email.

**Emails come back with `mx_valid` false.**
The domain does not publish mail exchange records, so it cannot receive email at that address. These are almost always dead or misconfigured domains — filter them out before importing into any sending platform.

**Only one of my locations produced results.**
Check `scrapeMaxBusinessesPerLocation`. When it is `false`, `maxBusinesses` is a global cap and the first location can absorb it entirely. Set it to `true` for even coverage across every location in the list.

***

### ❓ Frequently Asked Questions About K12 Schools Email Scraping

**What does the K12 Schools Email Scraper actually collect?**
It finds schools matching your search term in your chosen locations, crawls each school's website, and returns email addresses, phone numbers and social links together with the school's name, full address, coordinates, rating and place identifier.

**Where do the email addresses come from?**
They are extracted from the school's own public website during the crawl — contact pages, about pages, staff directories and similar. Nothing is taken from a purchased database or a private source.

**Can it find emails written to avoid scrapers?**
Yes. The extractor recognises common obfuscation patterns such as `name [at] domain [dot] org` in addition to plain text addresses and `mailto:` links.

**How does email validation work in this scraper?**
With `validateEmails` enabled, each address is checked for syntax, DNS resolution, MX records and SMTP acceptance. Those results feed `confidence_score`, and separate flags mark disposable providers and role-based inboxes.

**What is the difference between `email`, `email_found` and `scraped_emails`?**
`scraped_emails` is the list of everything found on that school's site. `email_found` and `email` identify the specific address a given output row represents, since a school with several published addresses produces several rows.

**Can I control how deep the scraper goes into each school website?**
Yes, with `maxPagesPerSite`. It defaults to 20 pages per domain. Lower it for speed, raise it when you need maximum contact coverage.

**Does `maxBusinesses` apply per location or overall?**
It depends on `scrapeMaxBusinessesPerLocation`. When `false` (the default) it is a global limit for the whole run. When `true`, each location in `googleMapsLocation` gets its own target.

**Do I need to configure a proxy?**
No. Proxy rotation is handled automatically by the actor, so there is nothing for you to set up.

**Are phone numbers and social profiles included?**
Yes. Phone extraction and social media extraction run as part of every crawl, populating `phone`, `scraped_phones` and `scraped_social_media` without any extra configuration.

**Can I use this K12 schools email scraper outside the United States?**
The location field is free text, so you can search other countries. `country_code` is returned on each record. Results depend on how well schools in that region are represented on the underlying source and how their websites publish contact details.

**How do I get only the deliverable contacts?**
Run with `validateEmails` on, then filter to rows where `mx_valid` is `true`, `is_disposable` is `false`, and `confidence_score` clears whatever threshold your campaign requires.

**Why do some schools appear more than once in the output?**
Each row represents one email address. A school publishing an office address, a principal's address and an admissions address will appear as three rows sharing the same `name`, `place_id` and `full_address`.

**Can I schedule this to keep a school contact list current?**
Yes. Apify's Scheduler can re-run the same input monthly or quarterly. Compare new results against your stored list using `place_id` to spot new schools and changed addresses.

**How do I export the school contact data?**
From the Apify Console you can export the dataset as CSV, JSON, Excel or XML. Via the API, `run-sync-get-dataset-items` returns the items directly in the response body.

**Is scraping school contact information legal?**
The scraper collects only publicly published information from school websites. How you use it is your responsibility — commercial outreach to school addresses is subject to marketing and privacy law in your jurisdiction, and you should confirm your legal basis before sending.

***

### 🆘 Support & Feedback

Run into a bug, an unexpected result, or a location that returns nothing? Open a ticket in the **Issues** tab on the actor page and include the run ID and your input so the case can be reproduced.

Need a custom build — additional output fields, a different discovery source, tighter validation rules, or direct delivery into your CRM? Email **scraperhubapi@gmail.com** and describe the requirement.

If the K12 schools email scraper saves you time, please leave a rating and a short review on the actor page. Feedback directly shapes what gets improved.

***

### ⚖️ Disclaimer

This K12 schools email scraper collects only publicly available information — school listings and the contact details schools themselves publish on their public websites. It does not access private systems, authenticated portals, student records or any non-public source.

You are responsible for how you use this actor and the data it produces. That includes complying with the terms of service of the sites visited, respecting any published contact policies, and following the marketing and anti-spam rules that apply where you and your recipients are located.

Email addresses, phone numbers and staff names are personal data under GDPR, CCPA and comparable privacy regimes. If you store or process this output you are acting as the data controller: you need a lawful basis for processing, you must honour access and deletion requests, and you should not retain contact data beyond the purpose you collected it for. Take particular care with any data relating to schools, since the education context can attract additional protections.

If you believe data collected through this actor should be removed, contact **scraperhubapi@gmail.com** with the details and the request will be handled.

# Actor input Schema

## `googleMapsSearchTerm` (type: `string`):

Enter the business type or niche for email scraper (e.g., 'coffee shops', 'dentists').

## `googleMapsLocation` (type: `array`):

Target geographic location for the email scraper (e.g., 'Miami, Florida').

## `maxBusinesses` (type: `integer`):

Target number of businesses to find (1-1000). The scraper will stop when this target is reached.

## `scrapeMaxBusinessesPerLocation` (type: `boolean`):

If enabled, the scraper will collect up to `maxBusinesses` results per location. If disabled, it combines all locations up to a single total limit.

## `proxyConfiguration` (type: `object`):

Proxy settings for scraping. Recommended for large-scale scraping.

## Actor input object example

```json
{
  "googleMapsSearchTerm": "K12 Schools",
  "googleMapsLocation": [
    "New York"
  ],
  "maxBusinesses": 5,
  "scrapeMaxBusinessesPerLocation": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

Records scraped by K12 Schools Email Scraper, stored in the run's default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "googleMapsSearchTerm": "K12 Schools",
    "googleMapsLocation": [
        "New York"
    ],
    "maxBusinesses": 5,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapers-hub/k12-schools-email-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "googleMapsSearchTerm": "K12 Schools",
    "googleMapsLocation": ["New York"],
    "maxBusinesses": 5,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapers-hub/k12-schools-email-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "googleMapsSearchTerm": "K12 Schools",
  "googleMapsLocation": [
    "New York"
  ],
  "maxBusinesses": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapers-hub/k12-schools-email-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapers-hub/k12-schools-email-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/8gHcdvJqSR2t0sJaD/builds/XmxfBlkxAFtWG8qW6/openapi.json
