# GreatSchools School Ratings Scraper (`automation-lab/greatschools-school-profiles-ratings`) Actor

Extract public GreatSchools school profiles, ratings, academic indicators, enrollment, demographics, addresses, and review summaries from school or location URLs.

- **URL**: https://apify.com/automation-lab/greatschools-school-profiles-ratings.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Education
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.28 / 1,000 school profile extracteds

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?

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

## GreatSchools School Ratings Scraper

Export public GreatSchools school ratings and profile context as structured JSON. Give the Actor a school profile URL or a GreatSchools city/location school-list URL and receive normalized school identity, ratings, academic indicators, enrollment, demographics, address, and review-summary fields.

The Actor is designed for recurring school-market and location analysis. It uses public, logged-out pages and does not require a GreatSchools account.

### What does this GreatSchools school ratings scraper collect?

For every school profile, the Actor can collect:

- GreatSchools school ID and NCES school ID
- school name, type, and grade range
- overall GreatSchools rating
- component ratings such as student progress and test scores
- published subject proficiency and state comparison values
- enrollment and public demographic shares
- street address, city, state, postal code, coordinates, and phone
- aggregate community review rating and count
- latest review text exposed in public structured profile data
- canonical profile URL and extraction timestamp

Fields remain `null` or arrays remain empty when GreatSchools does not publish that metric for a school.

### Who is it for?

- **Education researchers** compare published school indicators across a market.
- **Real-estate analysts** add school context to location and neighborhood datasets.
- **Relocation teams** prepare repeatable school comparisons for supplied locations.
- **Data teams** replace manual profile copying with a scheduled JSON dataset.
- **Developers** feed normalized school records into spreadsheets, warehouses, and dashboards.

### Why use this Actor?

GreatSchools pages contain several structured surfaces. This Actor combines them into one stable record rather than returning raw HTML. Location pages are expanded into individual profiles, duplicate schools are removed, and `maxItems` is enforced on useful school records.

The implementation uses direct HTML requests, so it avoids browser overhead and does not expose an automatic paid-proxy fallback.

### Getting started

1. Open the Actor input page.
2. Add one or more public `greatschools.org` school profile or location URLs.
3. Set **Maximum school profiles**.
4. Click **Start**.
5. Open the **School profiles** dataset view.
6. Export JSON, CSV, Excel, XML, or another supported dataset format.

### Input parameters

| Field | Type | Required | Default | Purpose |
| --- | --- | --- | --- | --- |
| `startUrls` | array | yes | — | GreatSchools profile URLs or city/location school-list URLs. |
| `maxItems` | integer | no | `25` | Maximum unique school profiles to save, from 1 to 10,000. |

Only HTTPS GreatSchools URLs are accepted. A URL on another domain fails closed instead of being fetched.

### Profile URL example

```json
{
  "startUrls": [
    {
      "url": "https://www.greatschools.org/california/san-francisco/6435-King-Starr-Elementary-School/"
    }
  ],
  "maxItems": 1
}
```

Use this mode when you already know the exact schools to enrich.

### Location URL example

```json
{
  "startUrls": [
    {
      "url": "https://www.greatschools.org/california/san-francisco/schools/"
    }
  ],
  "maxItems": 10
}
```

The Actor reads the location result page, discovers canonical school links, and fetches profiles until the limit is reached.

### Output example

```json
{
  "schoolId": 6435,
  "ncesSchoolId": "063441005671",
  "name": "King (Starr) Elementary School",
  "schoolType": "public",
  "grades": "K-5",
  "enrollment": 333,
  "greatSchoolsRating": 6,
  "ratingIndicators": [
    { "name": "Student Progress Rating", "rating": 4 },
    { "name": "Test Score Rating", "rating": 8 }
  ],
  "subjectScores": [
    { "subject": "English", "schoolPercentProficient": 60.87, "statePercentProficient": 48.81 }
  ],
  "reviewRating": 4.36,
  "reviewCount": 28,
  "address": {
    "street": "1215 Carolina Street",
    "city": "San Francisco",
    "state": "CA",
    "postalCode": "94107"
  },
  "latitude": 37.753468,
  "longitude": -122.399117,
  "sourceUrl": "https://www.greatschools.org/california/san-francisco/6435-King-Starr-Elementary-School/",
  "scrapedAt": "2026-09-10T12:00:00.000Z"
}
```

The live record can contain additional arrays documented in the dataset schema.

### How much does it cost to extract GreatSchools school profiles?

Pricing is pay per event:

- a **$0.005 start fee** per run;
- **$0.0088 per school profile on the Bronze plan**, with lower unit prices on higher plans.

At Bronze pricing, 1 profile costs about **$0.0138**, 10 profiles about **$0.093**, and 100 profiles about **$0.885**, including one start event. Apify displays the exact applicable tier before a run. Failed or rejected pages are not charged as profile items.

### Scheduling school-market analysis

Create an Apify Schedule with the same location URLs and item limit. Each run creates a fresh dataset. Export or compare snapshots in your own warehouse to identify changes in published ratings, enrollment context, or review aggregates.

The Actor does not itself calculate historical differences or send alerts. Those are downstream workflow steps.

### Integrations

Dataset results can be connected to:

- Google Sheets for location comparison tables
- webhooks for completion-driven pipelines
- Make or Zapier for low-code routing
- BigQuery, Snowflake, or another data warehouse
- Python and JavaScript analysis notebooks
- Apify API and MCP clients

### Run with the Apify API using cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~greatschools-school-profiles-ratings/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"startUrls":[{"url":"https://www.greatschools.org/california/san-francisco/schools/"}],"maxItems":10}'
```

Keep tokens in environment variables or a secrets manager.

### Run with JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/greatschools-school-profiles-ratings').call({
  startUrls: [{ url: 'https://www.greatschools.org/california/san-francisco/schools/' }],
  maxItems: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Run with Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("automation-lab/greatschools-school-profiles-ratings").call(
    run_input={
        "startUrls": [{"url": "https://www.greatschools.org/california/san-francisco/schools/"}],
        "maxItems": 10,
    }
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use through Apify MCP

Add this Actor to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/greatschools-school-profiles-ratings"
```

Claude Desktop, Cursor, and VS Code setup:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/greatschools-school-profiles-ratings"
    }
  }
}
```

Example prompts:

- “Extract up to 10 school profiles from this GreatSchools San Francisco location URL.”
- “Get the rating, enrollment, grades, address, academic indicators, and review aggregate for this school URL.”

### Reliability and retries

Requests have a 30-second timeout and bounded retries for network failures, HTTP 429, and temporary server errors. A 404, foreign domain, challenge page, non-HTML response, or page without a recognizable school/list payload produces an explicit error instead of a fabricated record.

For best reliability, keep individual runs bounded and schedule recurring collections at a respectful frequency.

### Limits and data freshness

- Results reflect what the public page exposed when the Actor ran.
- GreatSchools does not publish every metric for every school.
- A location page can expose a bounded result set; the Actor does not claim nationwide exhaustive crawling from one URL.
- Review output is a summary field and aggregate, not a complete review-history product.
- The source can change its HTML or embedded data structures.
- The Actor supports public US GreatSchools URLs and does not log into accounts.

### Legality and responsible use

GreatSchools data may be subject to source terms and intellectual-property rights. Use the Actor only for lawful purposes, collect only what you need, respect applicable privacy and database rules, and avoid using a single rating as the sole basis for high-impact decisions.

School ratings and community reviews are context, not a complete measure of school quality. Validate important conclusions against official education sources.

### Troubleshooting

**Why did the run reject my URL?**

Confirm that it is a complete public URL on `greatschools.org`. Search-engine result URLs and third-party redirects are not accepted.

**Why is a field null or an array empty?**

The school may not have that metric, or GreatSchools may not expose it on the current public profile. Compare the source URL shown in the record.

**Why did a location URL save fewer items than `maxItems`?**

The source page may expose fewer unique school profiles. `maxItems` is a ceiling, not a promised count.

**What should I do after a challenge or temporary error?**

Wait before retrying. The Actor already performs bounded retries for transient responses; repeated immediate runs may make rate limiting worse.

### FAQ

**Does this use the discontinued GreatSchools API?**

No. It converts public logged-out GreatSchools pages into API-like dataset records.

**Can I pass several profile and location URLs together?**

Yes. Duplicate profile URLs and school IDs are removed within a run.

**Does it collect every review?**

No. This Actor focuses on school profiles and rating context. It includes the aggregate review rating/count and a public review summary when exposed.

**Can I export CSV or Excel?**

Yes. Use the dataset export controls or dataset API after the run completes.

**Does it monitor changes automatically?**

Use an Apify Schedule to rerun the same input. Store and compare snapshots in your downstream system.

### Related automation-lab Actors

Combine these results with other public location datasets from the [automation-lab Apify profile](https://apify.com/automation-lab). This Actor intentionally focuses on GreatSchools school profiles instead of mixing unrelated property or business records into one dataset.

# Actor input Schema

## `startUrls` (type: `array`):

Public greatschools.org school profile URLs or city/location school-list URLs. Location pages are expanded into school profiles.

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

Stop after this many unique, successfully extracted school profiles.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.greatschools.org/california/san-francisco/6435-King-Starr-Elementary-School/"
    },
    {
      "url": "https://www.greatschools.org/california/san-francisco/schools/"
    }
  ],
  "maxItems": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset containing all extracted school profile records.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.greatschools.org/california/san-francisco/6435-King-Starr-Elementary-School/"
        },
        {
            "url": "https://www.greatschools.org/california/san-francisco/schools/"
        }
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/greatschools-school-profiles-ratings").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 = {
    "startUrls": [
        { "url": "https://www.greatschools.org/california/san-francisco/6435-King-Starr-Elementary-School/" },
        { "url": "https://www.greatschools.org/california/san-francisco/schools/" },
    ],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/greatschools-school-profiles-ratings").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 '{
  "startUrls": [
    {
      "url": "https://www.greatschools.org/california/san-francisco/6435-King-Starr-Elementary-School/"
    },
    {
      "url": "https://www.greatschools.org/california/san-francisco/schools/"
    }
  ],
  "maxItems": 10
}' |
apify call automation-lab/greatschools-school-profiles-ratings --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/greatschools-school-profiles-ratings"
        }
    }
}
```

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/iZvsgmoGvLQhIb6wT/builds/hq50jBNXEzCvFPe2A/openapi.json
