# EDJOIN Jobs Scraper - California School & Teacher Jobs (`maxqim/edjoin-jobs-scraper`) Actor

Scrape K-12 and community college job postings from EDJOIN (edjoin.org): teacher, classified and administrator jobs with district, county, salary range, posting and closing dates and full description. Fast JSON-based. Pay per result.

- **URL**: https://apify.com/maxqim/edjoin-jobs-scraper.md
- **Developed by:** [miao qi](https://apify.com/maxqim) (community)
- **Categories:** Jobs
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 1,000 job listings

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## EDJOIN Jobs Scraper: California Teacher & School Jobs

Scrape job postings from **EDJOIN** (edjoin.org), the main job board for California public schools, school districts, county offices of education, charter schools and community colleges (plus some districts in other states).

Get **position, district, city, county, job category, full/part time, salary range, posting date, closing date** and, optionally, the **full job description and district address**, as clean JSON / CSV / Excel.

- Fast: reads the same JSON data the EDJOIN search page loads, 100 postings per request
- Salary ranges parsed into numbers (`salaryMin`, `salaryMax`, `salaryPeriod`)
- Filters: keywords, location, counties, job category, posted within N days
- No login, no browser. Pay only for the postings you get

### What data do you get?

| Field | Example |
| --- | --- |
| `postingId`, `title`, `url` | 2234530, "Regional Special Education Specialist" |
| `employer` | "KIPP SoCal Public Schools" (district / school / college) |
| `city`, `county`, `state` | Los Angeles, Los Angeles, California |
| `jobCategory` | "Teacher - Special Education", "Teacher Assistant / Aide / Paraprof.", "Classified", ... |
| `fullTimePartTime` | Full Time / Part Time |
| `salaryText`, `salaryMin`, `salaryMax`, `salaryPeriod` | "$79,200 - $99,000 Annually" |
| `postedDate`, `closingDate`, `closingRule` | 2026-10-27, 2026-12-01, "By Date" / "Until Filled" |
| `onlineApplication`, `numberOfOpenings`, `isSummerSchool` | |
| With **Open posting pages** on: `descriptionText`, `employerOverview`, `employerAddress`, `postalCode`, `employerLogoUrl`, `employerEdjoinUrl`, `employmentType`, `workHours` | |

No applicant data and no personal contact details are collected.

### Use cases

- **Teacher and staff salary benchmarking** across California districts and counties
- **EdTech, staffing agencies and substitute-teacher services**: find districts that are hiring now (lead lists by county and job category)
- **Job alert bots and niche job boards** for teachers, special-education staff, counselors, school psychologists, administrators
- **Workforce research**: shortages by subject (special education, math, science) and region, tracked daily
- **AI agents**: structured, up-to-date school job data

### How to use

1. Enter **keywords** (e.g. `math teacher`, `special education`, `school psychologist`, `custodian`) or leave empty for all postings.
2. Optionally set **location**, **counties**, **job category contains** and **posted within days**.
3. Turn on **Open posting pages** if you need the full description.
4. Set **Max postings** and click Start.

Tip: schedule a daily run with `postedWithinDays: 1` to get only new postings.

#### Input example

```json
{
  "keywords": "special education",
  "counties": ["Los Angeles", "Orange", "San Diego"],
  "jobCategoryContains": ["Teacher"],
  "postedWithinDays": 7,
  "includeDetails": true,
  "maxItems": 300
}
```

#### Output example

```json
{
  "postingId": 2234530,
  "title": "Regional Special Education Specialist",
  "url": "https://www.edjoin.org/Home/JobPosting/2234530",
  "employer": "KIPP SoCal Public Schools",
  "city": "Los Angeles",
  "county": "Los Angeles",
  "state": "California",
  "jobCategory": "Special Education",
  "fullTimePartTime": "Full Time",
  "salaryText": "$79,200 - $99,000 Annually",
  "salaryMin": 79200.0,
  "salaryMax": 99000.0,
  "salaryPeriod": "Annually",
  "postedDate": "2026-10-27",
  "closingDate": "2026-12-01",
  "closingRule": "By Date",
  "onlineApplication": false,
  "descriptionText": "Job Summary\nKey Responsibilities\n\nSchool-Based Support & Coaching ...",
  "employerEdjoinUrl": "https://www.edjoin.org/kippsocal",
  "employerAddress": "3601 E. First St., Los Angeles, California, 90063",
  "postalCode": "90063",
  "employmentType": ["FULL_TIME"],
  "workHours": "immediate opening",
  "scrapedAt": "2026-10-28T05:13:59+00:00"
}
```

More real records in `sample_output.json`.

### Pricing

Pay per result: **$1.50 per 1,000 postings** (event `job`), same price with or without detail pages. Set **Max postings** or a maximum cost per run and the Actor stops exactly there.

### FAQ

**Does it cover states other than California?** EDJOIN is mostly California, but some districts in other states post there too; those are included (see `state`).

**Why is `closingDate` empty?** The posting is open "Until Filled" (`closingRule`).

**Some postings fail to open with detail pages on.** Postings removed between the search and the detail request return "410 Gone"; the Actor keeps the listing data and moves on.

**Is it legal?** The Actor reads publicly available job postings at a polite rate without logging in and does not collect personal data. You are responsible for how you use the data.

# Actor input Schema

## `keywords` (type: `string`):

Same as the EDJOIN search box, e.g. "math teacher", "special education", "custodian", "principal". Leave empty for all postings.

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

City, county or ZIP code as you would type it on EDJOIN, e.g. "Sacramento", "Los Angeles".

## `counties` (type: `array`):

Keep only postings in these counties, e.g. "Riverside", "San Diego".

## `jobCategoryContains` (type: `array`):

Keep only postings whose EDJOIN category contains one of these texts, e.g. "Teacher", "Classified", "Administrator", "Substitute".

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

Only postings from the last N days (0 = any). Great for daily monitoring runs.

## `includeDetails` (type: `boolean`):

Adds full description/requirements, employer overview, employer address and logo. One extra request per posting.

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

Stop after this many postings (0 = no limit). You pay per posting saved.

## `maxConcurrency` (type: `integer`):

Parallel detail requests.

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

Usually not needed.

## Actor input object example

```json
{
  "keywords": "special education",
  "postedWithinDays": 0,
  "includeDetails": false,
  "maxItems": 200,
  "maxConcurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All scraped job listings, one item per job.

# 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 = {
    "keywords": "special education"
};

// Run the Actor and wait for it to finish
const run = await client.actor("maxqim/edjoin-jobs-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 = { "keywords": "special education" }

# Run the Actor and wait for it to finish
run = client.actor("maxqim/edjoin-jobs-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 '{
  "keywords": "special education"
}' |
apify call maxqim/edjoin-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maxqim/edjoin-jobs-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/WzAdBVV67T5gtSQ5h/builds/JkOwX5lBdDagarmfQ/openapi.json
