# Schoolspring Scraper (`normdata/schoolspring-scraper`) Actor

Search SchoolSpring's US K-12 job board by keyword, location or ZIP, district, or multiselect category, grade level, and job type filters, or look up job IDs. Every job includes the full description, tagged categories, the specific school, pay range, contact info, and the application link.

- **URL**: https://apify.com/normdata/schoolspring-scraper.md
- **Developed by:** [Norm Data](https://apify.com/normdata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.90 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

![Norm Data](https://i.ibb.co/rGbhM5Y8/Chat-GPT-Image-Sep-8-2026-02-20-50-PM.png)

## 🏫 SchoolSpring K-12 Jobs Scraper

Search **SchoolSpring**, a major US K-12 education job board, by keyword, location, district, and real multiselect category, grade level, and job type filters, or look up known job IDs directly. No login, no account.

Every job comes back with the full description, every category and subcategory it's tagged under, the specific school's name, pay range, the district contact's real name, phone, and email, exact school coordinates, and the real external application link, useful for education staffing, teacher job search tools, district hiring research, and edtech outreach.

### 🎯 Who uses it?

#### 📇 Education staffing agencies

Build a current list of open teaching and support positions by district, subject, and category, with a direct contact to reach out to.

#### 👩‍🏫 Teacher job search tools

Power a search or alert product on top of live, structured listings instead of scraping raw HTML yourself.

#### 📊 District hiring researchers

Compare a district's advertised pay ranges, job types, and posting volume against nearby districts.

#### 📣 Edtech sales and outreach teams

Build a list of districts currently hiring, with contact details, for targeted outreach.

### ✨ What it does

- **Search:** by keyword, location (city, state, or ZIP code), district or organization name, or a specific district's own SchoolSpring site.
- **Real multiselect filters:** pick several job types, grade levels, or categories in one run, not just one at a time.
- **Quality filters:** only jobs posted within a chosen number of days, or only jobs meeting a minimum listed pay.
- **Look up:** specific, already-known job IDs directly, one row per ID.
- **Full detail on every row:** complete job description, every category and subcategory the job is tagged under, pay range, requirements, degree and experience preferences, the specific school's name alongside the district name, the district contact's name, title, phone, fax, email, and address, every school location with exact coordinates, and the real external application link.

Missing source values are returned as `null`, never invented.

### Why this scraper

- **Real multiselect filters.** Pick several job types, grade levels, or categories at once, the source's own filters only take one value each.
- **The specific school's name**, not just the district, so a lead or a listing points at the actual building.
- **Search by ZIP code**, not just city or state text.
- **Full detail already included.** Every search result comes fully enriched, no separate lookup or toggle needed.
- **The district contact's real phone and email come standard**, not stripped out or held back.

### How it compares

| Capability | This actor | Closest competitor |
|---|:--:|:--:|
| Multiselect category, grade level, and job type in one run | **yes** | no, one value at a time |
| Every category and subcategory a job is tagged under | **yes** | single category field |
| Specific school name, separate from the district | **yes** | not shown |
| Search by ZIP code | **yes** | state/city only |
| Full detail on every search result, no extra step | **yes** | separate detail lookup |
| Minimum advertised pay filter | yes | yes |
| Posted-within-days filter | yes | yes |

### 📦 What data you get

| Entity | Useful fields |
| --- | --- |
| Job | Title, full description, employment type, positions, shift, EOE statement, external job code, and application instructions. |
| Categories | Every category and subcategory the job is tagged under. |
| Employer | The district name and, separately, the specific school this posting belongs to. |
| Pay | Display text, stated pay period, parsed min/max, and pay type. |
| Contact | Name, title, phone, fax, email, and full address. |
| Locations | Every school location for this job, each with exact latitude and longitude. |
| Links | The real external application link, plus the district's own job board and SchoolSpring site. |
| Dates | Posted, displayed, start date, application deadline, and close date. |

### 💡 Use cases

#### 📇 Build a district contact list for staffing outreach

```json
{ "mode": "search", "keyword": "special education", "jobTypeIds": [1], "maxItems": 200 }
```

#### 🏫 Search near a ZIP code, multiple grade levels at once

```json
{ "mode": "search", "location": "97124", "gradeLevelIds": [3, 5], "maxItems": 100 }
```

#### 💰 Only recent, well-paid openings

```json
{ "mode": "search", "keyword": "principal", "postedWithinDays": 14, "minPay": 70000, "maxItems": 100 }
```

#### 🔎 Resolve known job IDs directly

```json
{ "mode": "lookup", "jobIds": ["5929662", "5936800"] }
```

### ⚙️ How the input is organised

**Maximum results** sits at the very top, since it applies no matter what you're doing. Below it, the form is split into three numbered sections:

| Section | What it's for |
| --- | --- |
| **1 · Mode** | Choose whether to search by filters or look up known job IDs. |
| **2 · Search filters** | Keyword, location (city, state, or ZIP), district or organization name, a specific district's SchoolSpring subdomain, multiselect job type, grade level, and category filters, and optional posted-within-days and minimum-pay quality filters. |
| **3 · Look up** | Known SchoolSpring job IDs to resolve directly. |

> **Apify Free plan:** every run is limited to a fixed 10-row sample. Upgrade your Apify plan to run your own settings.

### 🛡️ Limits & responsible use

This Actor reads only SchoolSpring's own public job board. It never signs in and never accesses anything gated behind an account.

District contact details are public directory data the district itself publishes, not personal information scraped from elsewhere. Use them in line with SchoolSpring's terms and applicable law.

A job ID in Look up mode that doesn't resolve writes a row with an `error` field instead of failing the run.

### 📧 Contact

Need a scraper for a different site, or found something wrong with this one? norm.data.scrapers@gmail.com

### Local development

```bash
bun install
bun test
bun run src/main.ts
```

# Changelog

This Actor's version history is a separate document: https://apify.com/normdata/schoolspring-scraper/changelog.md

# Actor input Schema

## `maxItems` (type: `integer`):

Caps how many rows this run writes.

## `mode` (type: `string`):

Search filters SchoolSpring's job board into a list. Look up resolves specific known job IDs directly.

## `keyword` (type: `string`):

Match jobs whose title or description contains this text.

## `location` (type: `string`):

City, state, region, or ZIP code, e.g. "Florida", "Austin, Texas", or "97124".

## `organization` (type: `string`):

Match jobs from a specific school district or organization name.

## `domainName` (type: `string`):

Scope the search to one district's own SchoolSpring site, e.g. "framingham" for framingham.schoolspring.com. Leave empty to search all districts nationwide.

## `jobTypeIds` (type: `array`):

Select one or more employment types. SchoolSpring's own filters only accept one value per search, so selecting several here runs one query per type and merges the results.

## `gradeLevelIds` (type: `array`):

Select one or more grade levels. Selecting several runs one query per level and merges the results, same as job type.

## `categoryIds` (type: `array`):

Select one or more job categories. Selecting several runs one query per category and merges the results, same as job type.

## `postedWithinDays` (type: `integer`):

Only jobs posted or last displayed within this many days. Leave empty for no limit.

## `minPay` (type: `number`):

Only jobs whose listed pay meets or exceeds this number. Districts list pay as either hourly or annual, so a high value here only matches annual salaries. Jobs with no numeric pay listed are excluded. Leave empty for no limit.

## `jobIds` (type: `array`):

Known SchoolSpring job IDs to resolve directly. Unmatched IDs come back as an error row.

## Actor input object example

```json
{
  "maxItems": 10,
  "mode": "search",
  "keyword": "teacher"
}
```

# Actor output Schema

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

One dataset row per job matched by search, or per job ID looked up, with title, employer, location, and (when enrichment is on) pay range, full description, requirements, district contact info, exact coordinates, and the real external application link.

# 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 = {
    "maxItems": 10,
    "mode": "search",
    "keyword": "teacher"
};

// Run the Actor and wait for it to finish
const run = await client.actor("normdata/schoolspring-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 = {
    "maxItems": 10,
    "mode": "search",
    "keyword": "teacher",
}

# Run the Actor and wait for it to finish
run = client.actor("normdata/schoolspring-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 '{
  "maxItems": 10,
  "mode": "search",
  "keyword": "teacher"
}' |
apify call normdata/schoolspring-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,normdata/schoolspring-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/nMe57FhmTcoPT4EJ2/builds/uvGa6ig8cO1wZfg1P/openapi.json
