# Superprof Scraper - Tutors, Lessons, Prices & Ratings (`abotapi/superprof-tutor-scraper`) Actor

Scrape Superprof tutors by subject and location, or paste tutor URLs. One rich record per tutor: name, photo, price, rating, reviews count, subjects, city, geo, response time, lesson type and more. Filter by price, rating and lesson type.

- **URL**: https://apify.com/abotapi/superprof-tutor-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** Jobs, Lead generation, E-commerce
- **Features:** Limited permissions
- **Stats:** 9 total users, 1 monthly users, 88.6% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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

## Superprof Scraper: Tutors, Prices, Ratings & Lesson Details

Superprof Scraper turns Superprof into a clean tutor data feed. Search by subject and location, or paste results links, and get one structured record per tutor: name, hourly price plus pack rates, average rating and review count, subjects taught, city, lesson format (webcam or face to face), response time, badges and more. A single pasted tutor link returns that tutor's profile-page fields only, since it never comes from a search result. Export to JSON, CSV or Excel, or read results straight through the API.

### Why This Scraper?

- **One rich record per tutor** covering price, 5h/10h pack rates, rating, review count, badge, response time, lesson format, subjects taught and profile photo.
- **Two ways to find tutors:** search by subject and location, or paste tutor pages and results links directly.
- **Filters that actually narrow the search:** hourly price, minimum rating, lesson format, webcam availability and free first lesson apply in search mode and to a pasted results link alike.
- **Optional per-tutor enrichment** pulls city, subjects taught and the full profile description straight from each tutor's own page, billed only for tutors that are actually returned.
- **Resume a capped or interrupted run** with one pasted run id, and the run survives a platform restart automatically with no duplicate tutors and no double charges.
- **Incremental mode for recurring monitoring:** schedule the same search and later runs return only new and updated tutors by default (reappeared tutors need an earlier run that completed a full scan with Emit expired tutors on).
- **A refused run fails loudly** instead of quietly completing with an empty, billed dataset that looks the same as a genuinely empty search.

### Use Cases

- **Tutoring marketplaces and comparison sites:** build a searchable directory of tutors with live pricing, ratings and availability.
- **Price and market research:** track average hourly rates and pack pricing by subject and city over time.
- **Lead generation for tutoring businesses:** find tutors in a subject and location with profile links ready to review.
- **Recurring monitoring:** schedule the actor to catch new tutors, price changes and tutors who stop listing.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live listing.

| Field | Example |
| --- | --- |
| `id_annonce` | `"10000001"` |
| `teacherName` | `"Jane Doe"` |
| `title` | `"Personalized math lessons for all levels"` |
| `url` | `"https://www.superprof.com/sample-tutor-headline.html"` |
| `price` / `price_1H` | `30` / `30` |
| `teacherCity` / `city` | `"New York"` / `"New York"` |
| `country` | `"US"` |
| `lat` / `lng` | `40.712800` / `-74.006000` |
| `subjects` | `["Mathematics", "Algebra", "Statistics"]` |
| `badge` / `is_ambassador` | `"Ambassador"` / `true` |
| `stat_stars` / `stat_count_reviews` | `5` / `120` |
| `offer_lesson_first_is_free` / `offer_lesson_first_lesson_duration` | `true` / `"0hr30"` |
| `price_5H` / `price_10H` / `price_webcam` | `100` / `200` / `30` |
| `webcam` / `faceToFace` | `true` / `false` |
| `responseTimeDesc` / `response_time_sec` | `"Responds within 1 hour"` / `3600` |
| `levels` | `["All Levels"]` |
| `languages` / `native_language` | `["English"]` / `"English"` |
| `status` | `"Ambassador"` |
| `stat_count_recommendations` | `15` |
| `interview` / `has_interview` | `[{"question": "1) When did you start tutoring?", "answer": "..."}]` / `true` |
| `reviews` (up to **Reviews per tutor**, default 50, max 50; needs profile fetch, no extra charge) | `[{"author": "Sam", "text": "Great tutor, very patient."}]` |
| `gallery` | `["https://example.com/photo.jpg"]` |
| `teacherPhoto` | `"https://example.com/photo.jpg"` |
| `search_url` | `"https://www.superprof.com/s/mathematics,New-York,,,,.html"` |

Every record also carries the raw source leaves the fields above summarize (`teacherRating`, `rating_score`/`rating_count`/`rating_label`, `offers`, `teacherId`, `key`, `sex`, `is_superprof`, `is_verified_member`, `has_valid_diploma`, `has_video`/`video`, `experience`, `teaching`, `venue_can_move`/`venue_can_receive`, `searchMode`, `sourceUrl`, `scrapedAt` and more) so nothing the source exposes is lost. `subjects`, `country`, pack rates (`price_5H`, `price_10H`, `price_webcam`), `levels`, `languages`, `native_language`, `experience`, `teaching`, `interview`/`has_interview`, `stat_count_recommendations` and the review list are filled from the tutor's own profile page and are only present when **Fetch full tutor profiles** is on (it is, by default); `city`, `gallery` and `status` fall back to search-result values when it's off. A single pasted tutor link always reads the profile page regardless of that toggle, but returns only profile-page fields; the search-result-only fields (name, rating, badge, response time, webcam/in-person flags) are empty for a pasted tutor link, since it never comes from a search result. `contact_label` is read from the contact button on the tutor page. `contact_url` is the tutor's "contact this tutor" link taken from the search result, so it is filled for tutors found by a search and empty for a single pasted tutor link. A few other contact/CV fields (`emails`, `cv`, `department`, `creation_date`) are always present on the record shape but are not exposed on the pages we read, so they read as empty on every run. In incremental mode, records also carry `changeType`, `changedFields`, `firstSeenAt` and `lastSeenAt`; an `EXPIRED` row repeats the tutor's last known field values with `changeType` set to `"EXPIRED"`.

### How to Use

1. Pick a **mode**: `search` (by subject and location) or `url` (paste tutor or results links).
2. In search mode, list one or more **subjects** and **locations**; in url mode, paste the links. Set the price, rating, lesson-format, webcam and free-first-lesson filters you want; they apply in both modes, except to a single pasted tutor page.
3. Set **Max items** (and, if you want a page-count safety net, **Max pages**) to control run size and cost, then click **Start**.
4. Download the dataset as JSON, CSV or Excel, or read it through the API.

**Search a subject in a location:**

```json
{
  "mode": "search",
  "subjects": ["Mathematics"],
  "locations": ["New York"],
  "maxItems": 20
}
```

**Filtered search for online lessons:**

```json
{
  "mode": "search",
  "subjects": ["Guitar"],
  "locations": ["London"],
  "lessonType": "online",
  "minRating": 4,
  "maxPrice": 40,
  "onlyFirstLessonFree": true,
  "maxItems": 50
}
```

**Scrape specific URLs:**

```json
{
  "mode": "url",
  "urls": [
    "https://www.superprof.com/s/mathematics,United-States,,,,.html",
    "https://www.superprof.com/sample-tutor-headline.html"
  ],
  "fetchDetails": true
}
```

**Monitor a search on a schedule and get only what changed:**

```json
{
  "mode": "search",
  "subjects": ["Mathematics"],
  "locations": ["New York"],
  "incrementalMode": true,
  "emitExpired": true,
  "maxItems": 0
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/superprof-tutor-scraper").call(run_input={
    "mode": "search", "subjects": ["Mathematics"], "locations": ["New York"], "maxItems": 20
})
for tutor in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(tutor["teacherName"], tutor["price"])
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/superprof-tutor-scraper').call({
  mode: 'search', subjects: ['Mathematics'], locations: ['New York'], maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the **Integrations** tab.

#### How Max items and Max pages shape a run

**Max items** is the single overall cap on tutors collected across the whole run (`0` means unlimited); when several subjects and locations are combined, the cap is split evenly across each subject/location pair so one search cannot use up the whole budget before the others start. **Max pages** is a separate, optional safety ceiling on how many result pages a single search walks; it never caps the number of tutors returned, only how far one search is allowed to page before stopping.

#### Resume and recurring updates

Two different features, both optional:

- **Resume** (`resumeFromRunId`): continue ONE specific interrupted or capped run. Paste a previous run id or dataset id and this run returns only tutors it did not already collect. The run also checkpoints its own progress automatically, so a platform restart resumes it without any input from you, with no duplicate tutors and no double charges.
- **Incremental mode** (`incrementalMode`): for scheduling this actor again and again against the SAME search or URL set. The actor remembers its own state; no run id to paste. Every tutor is classified:

  - `NEW`: never tracked before.
  - `UPDATED`: a comparable field changed since last time; `changedFields` names exactly which.
  - `UNCHANGED`: nothing changed. Suppressed and not billed by default; turn on **Emit unchanged tutors** to also get (and be billed for) these rows.
  - `REAPPEARED`: was marked `EXPIRED` in a previous run and is back.
  - `EXPIRED`: was tracked before and is no longer found. Only ever produced after a run that proved a complete, uncapped scan of the tracked search or URL set, with no Resume used; if Max items or Max pages cut the run short, or Resume seeded it, expiry detection is skipped for that run and last run's state is kept as is. Off by default; turn on **Emit expired tutors** to also get (and be billed for) these rows.

  What is compared: every output field of the tutor (price, rating, availability flags, profile text, subjects, gallery, reviews). What is excluded: `scrapedAt`, `distance` (depends on the proxy location) and `resultTier` (the search's own ranking), plus any field names you list in **Ignore fields when detecting changes**; excluded fields stay in the output and only stop triggering `UPDATED`. Whole numbers compare equal whether written `25` or `25.0`.

  By default (both toggles off) a recurring run returns only `NEW` and `UPDATED` tutors. `REAPPEARED` only appears once an earlier run had **Emit expired tutors** on and completed a full, uncapped, non-resumed scan and marked a tutor `EXPIRED`. State is scoped automatically to the search/URL and filter combination you use; **State key** lets you name a campaign explicitly, or deliberately share state across differently configured runs.

### Send results into your apps (MCP connectors)

Connect one or more MCP servers to push the scraped tutors straight into your tools after a run finishes. Set `mcpConnectors` to the servers you want (Notion, Linear, Airtable, Apify). For Notion, also set `notionParentPageUrl` to the page under which a results database is created, and use `maxNotifyListings` to cap how many tutors are sent. The export is a side channel: it never changes or blocks the dataset output.

1. Authorize a connector once under **Apify > Settings > Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Export to your apps (MCP)"** input field. (If the picker is empty, you haven't authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where records should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | select | `search` | `search` for subject/location searches, `url` to scrape pasted URLs. |
| `subjects` | array | no default (prefill: `["Mathematics"]`) | Subjects to search, e.g. "Mathematics", "Guitar". Search mode only. |
| `locations` | array | no default (prefill: `["United States"]`) | Places to search, e.g. "New York". Empty means country-wide. Search mode only. |
| `urls` | array | a country results URL, a city results URL, an Online results URL and one tutor page URL | Tutor page URLs or results URLs on www.superprof.com. URL mode only. If you leave the field out entirely, these four sample URLs are scraped (previously nothing was); pass an empty list to scrape nothing. Other Superprof country sites and other hosts are skipped with a warning. |
| `lessonType` | select | `any` | Restrict to `any`, `online` or `face_to_face`. Applies in search mode and to a pasted results URL; does not narrow a single pasted tutor page. |
| `minPrice` | integer | no default | Only tutors charging at least this much per hour. |
| `maxPrice` | integer | no default | Only tutors charging at most this much per hour. |
| `minRating` | integer | no default | Only tutors with an average rating at or above this value (1 to 5). |
| `onlyWebcam` | boolean | `false` | Only tutors who offer lessons by webcam. |
| `onlyFirstLessonFree` | boolean | `false` | Only tutors who offer a free first lesson. |
| `sortBy` | select | `relevance` | Result order: `relevance` or `distance`. |
| `fetchDetails` | boolean | `true` | Adds city, subjects taught and the full description from the tutor's own profile page. |
| `reviewLimit` | integer | `50` | Written reviews returned per tutor (needs `fetchDetails`). Values above 50 are lowered to 50; `0` returns none. In incremental mode a non-default limit is tracked as its own baseline (its own state key), so changing it does not report unchanged tutors as updated; the default of 50 keeps your existing baseline. |
| `maxPages` | integer | `0` | Safety ceiling on result pages per search. `0` is unlimited; does not cap items. |
| `maxItems` | integer | `20` | Maximum tutors collected across the whole run. `0` is unlimited. |
| `resumeFromRunId` | string | no default | Paste a previous run id (or its dataset id) to continue that run: tutors already collected there are skipped. |
| `incrementalMode` | boolean | `false` | Turn on for recurring/scheduled monitoring of the same search or URL set. |
| `stateKey` | string | no default | Optional. Name a monitoring campaign, or deliberately share state across differently configured runs. |
| `emitUnchanged` | boolean | `false` | Incremental mode only. Also return (and bill) tutors unchanged since the last run. |
| `emitExpired` | boolean | `false` | Incremental mode only. Also return (and bill) tutors no longer found, after a complete scan. |
| `ignoreFieldsForChanges` | array | `[]` | Incremental mode only. Top-level field names to leave out of change detection, on top of the built-in exclusions. |
| `proxy` | object | `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}` | Connection settings. |
| `mcpConnectors` | array | no default | Optional MCP servers (Notion, Linear, Airtable, Apify) to receive the results. |
| `notionParentPageUrl` | string | no default | Optional Notion page URL under which a results database is created. |
| `maxNotifyListings` | integer | no default | Optional cap on how many tutors are pushed to the connected apps. |

In the Console dataset you can switch between four views: Overview, Detail, Reviews and Changes (the incremental status and changed fields).

### Output Example

> Sample shape: values are illustrative placeholders, not from a live listing.

```json
{
  "id_annonce": "10000001",
  "teacherName": "Jane Doe",
  "title": "Personalized math lessons for all levels",
  "url": "https://www.superprof.com/sample-tutor-headline.html",
  "landing_url": "https://www.superprof.com/s/mathematics,United-States,,,,.html",
  "teacherPhoto": "https://example.com/photo.jpg",
  "price": 30,
  "price_1H": 30,
  "prices": { "hourly": 30, "firstHour": 30, "currency_hint": null },
  "teacherCity": "New York",
  "city": "New York",
  "country": "US",
  "lat": 40.7128,
  "lng": -74.006,
  "subjects": ["Mathematics", "Algebra", "Statistics"],
  "subjects_raw": ["Mathematics", "Algebra", "Statistics"],
  "badge": "Ambassador",
  "is_ambassador": true,
  "is_available": true,
  "search_url": "https://www.superprof.com/s/mathematics,New-York,,,,.html",
  "sex": "female",
  "is_superprof": true,
  "is_verified_member": true,
  "stat_count_reviews": 120,
  "stat_stars": 5,
  "response_time_sec": 3600,
  "offer_lesson_first_is_free": true,
  "offer_lesson_first_lesson_duration": "0hr30",
  "stat_count_recommendations": 15,
  "status": "Ambassador",
  "price_5H": 100,
  "price_10H": 200,
  "price_webcam": 30,
  "price_with_travel": null,
  "offers": [
    { "type": "hourly", "price": 30 },
    { "type": "pack_5h", "price": 100 },
    { "type": "pack_10h", "price": 200 },
    { "type": "webcam", "price": 30 }
  ],
  "levels": ["All Levels"],
  "has_all_levels": true,
  "native_language": "English",
  "languages": ["English"],
  "experience": "Sample tutor bio placeholder.",
  "teaching": "Sample lesson approach placeholder.",
  "explications": "Sample lesson approach placeholder.",
  "interview": [{ "question": "1) When did you start tutoring?", "answer": "Sample answer placeholder." }],
  "has_interview": true,
  "video": null,
  "has_video": false,
  "gallery": ["https://example.com/photo.jpg"],
  "reviews": [{ "author": "Sam", "text": "Great tutor, very patient." }],
  "has_valid_diploma": true,
  "venue_can_move": false,
  "venue_can_receive": false,
  "venue_can_receive_individually": false,
  "venue_can_receive_several_students": false,
  "venue_max_distance": null,
  "creation_date": null,
  "update_date": "2026-02-23T09:41:38Z",
  "contact_label": "Contact",
  "contact_url": null,
  "emails": [],
  "cv": null,
  "department": null,
  "teacherId": "20000001",
  "key": "a1b2c3d4",
  "webcam": true,
  "faceToFace": false,
  "responseTimeDesc": "Responds within 1 hour",
  "description": "Sample tutor bio placeholder.",
  "searchMode": "search",
  "sourceUrl": "https://www.superprof.com/sample-tutor-headline.html",
  "scrapedAt": "2026-01-01T00:00:00+00:00"
}
```

### Plan Requirement

Superprof protects its pages with two different walls: a datacenter connection meets an AWS WAF "Human Verification" captcha, and a residential connection meets a DataDome interstitial. Neither connection type gets through on its own, so the actor clears the check internally and then reads the pages. There is nothing for you to configure, but a run is not guaranteed to succeed, and pages can still be refused in waves.

- Keep the default Residential proxy group. If you select a non-residential proxy group, the run logs a boxed warning that the selected group may be refused by some pages and that you can try another proxy group in the Proxy configuration input.
- If the Apify residential connection is refused (free plans do not include residential proxies), the run logs a boxed warning, continues on a backup connection, and says so in the log when that backup serves data. Some pages may still be unavailable.
- If every connection is refused, the run fails with a message that your plan does not include proxy access. If residential was unavailable and the backup could not load the pages, it fails saying so. Any other run that loads no page fails with "Could not load any results page", so a failed connection is never mistaken for an empty search. A pasted tutor link that cannot be loaded is skipped with a warning and is neither returned nor billed; if no page at all loads, the run fails.

### FAQ

#### How much does it cost?

You pay per tutor record returned, plus a small per-run start fee, with the per-tutor profile enrichment (the `detail-enrichment` event, one unit per tutor) billed only when it is switched on and a tutor is actually returned. It is charged as each tutor record is saved, so an interrupted run is billed only for the tutors already delivered. The **Pricing** tab shows the current rates. Use **Max items** to cap the cost of any run.

#### Is it legal to scrape Superprof?

This actor collects only publicly available tutor listing data. You are responsible for how you use it: follow Superprof's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Contact details and personal profile content may be subject to additional privacy obligations in your jurisdiction.

#### Can I get only new or changed tutors on a schedule?

Yes. Schedule the actor from the **Schedules** tab and turn on **Incremental mode**. Each run then returns only new and updated tutors, and unchanged ones are not billed unless you opt in. Reappeared and expired tutors only appear once an earlier run had **Emit expired tutors** on and completed a full scan.

#### Why do minPrice, maxPrice and the other filters apply even in URL mode?

A pasted results URL is re-queried through the same search feed as search mode, so every filter you set (price, rating, lesson format, webcam, free first lesson) is sent along with it and narrows the results exactly as it would in search mode. The one exception is a single pasted tutor page: it always returns that one tutor, unfiltered.

#### Why did my run fail instead of returning an empty dataset?

If the connection is refused before any page can be read, or every request during the run is rejected with nothing getting through, the run stops with a clear error message instead of completing with zero tutors, so "nothing matched" is never confused with "nothing could be read". A genuinely empty search (the connection worked, but no tutors matched) still succeeds normally with an empty dataset and a status message explaining why.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more jobs data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🏷️ <a href="https://apify.com/abotapi/kijiji-scraper"><b>Kijiji.ca Scraper</b></a><br>Scrape Kijiji.ca listings across property, vehicles, jobs, electronics, furniture...</td><td>🏷️ <a href="https://apify.com/abotapi/finn-no-jobs-scraper"><b>FINN.no Jobs Scraper</b></a><br>Scrape active FINN.no job listings by search filters or URL. Extract 30+ fields including...</td></tr>
<tr><td>🛒 <a href="https://apify.com/abotapi/flippa-com-scraper"><b>Flippa Scraper</b></a><br>Scrape Flippa listings with full enrichment, including revenue, profit, valuation...</td><td>💼 <a href="https://apify.com/abotapi/cwjobs-scraper"><b>Cwjobs UK</b></a><br>Scrape tech and IT jobs from CWJobs.co.uk into clean, structured data. Search by keyword...</td></tr>
<tr><td>📇 <a href="https://apify.com/abotapi/thomasnet-supplier-scraper"><b>ThomasNet Scraper</b></a><br>Scrape ThomasNet suppliers and manufacturers by keyword, category, location...</td><td>🛒 <a href="https://apify.com/abotapi/product-reviews-australia-scraper"><b>ProductReview.com.au Scraper</b></a><br>Scrape ProductReview.com.au products, businesses and customer reviews. Search by...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/superprof-tutor-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

Choose how to find tutors. 'search' runs subject and location searches with filters. 'url' scrapes the tutor pages or results URLs you paste.

## `subjects` (type: `array`):

One or more subjects to search, e.g. 'Mathematics', 'Guitar', 'Spanish'. Each subject is searched independently against each location.

## `locations` (type: `array`):

One or more places to search, e.g. 'New York', 'London', 'United States'. Leave empty for a country-wide search. Each location is paired with each subject.

## `urls` (type: `array`):

Multi-URL mode: paste Superprof tutor page URLs (e.g. https://www.superprof.com/<tutor-page>.html) or results URLs. The fields in the Filters and sort section still apply to a pasted results URL; a pasted tutor page returns that one tutor. A results URL collects across its available pages up to Max items. Only www.superprof.com is supported; other Superprof country sites and other hosts are skipped with a warning.

## `lessonType` (type: `string`):

Restrict to a lesson format. Applies in BOTH search mode and URL mode: a pasted results URL is re-queried through the same search feed, so the filter is sent with it. It does not narrow a pasted tutor page, which returns that one tutor.

## `minPrice` (type: `integer`):

Only return tutors charging at least this much per hour (in the local currency of the search location). Applies in BOTH search mode and URL mode: a pasted results URL is re-queried through the same search feed, so the filter is sent with it. It does not narrow a pasted tutor page, which returns that one tutor.

## `maxPrice` (type: `integer`):

Only return tutors charging at most this much per hour. Applies in BOTH search mode and URL mode: a pasted results URL is re-queried through the same search feed, so the filter is sent with it. It does not narrow a pasted tutor page, which returns that one tutor.

## `minRating` (type: `integer`):

Only return tutors with an average rating at or above this value (1 to 5). Applies in BOTH search mode and URL mode: a pasted results URL is re-queried through the same search feed, so the filter is sent with it. It does not narrow a pasted tutor page, which returns that one tutor.

## `onlyWebcam` (type: `boolean`):

When on, only tutors who offer lessons by webcam are returned. Applies in BOTH search mode and URL mode: a pasted results URL is re-queried through the same search feed, so the filter is sent with it. It does not narrow a pasted tutor page, which returns that one tutor.

## `onlyFirstLessonFree` (type: `boolean`):

When on, only tutors who offer a free first lesson are returned. Applies in BOTH search mode and URL mode: a pasted results URL is re-queried through the same search feed, so the filter is sent with it. It does not narrow a pasted tutor page, which returns that one tutor.

## `sortBy` (type: `string`):

Order of the results returned by the search. Applies in BOTH search mode and URL mode: a pasted results URL is re-queried through the same search feed, so the filter is sent with it. It does not narrow a pasted tutor page, which returns that one tutor.

## `fetchDetails` (type: `boolean`):

When on, each tutor is enriched from its profile page with city, subjects taught and the full description. When off, only the rich results fields are returned (no per-tutor fetch).

## `reviewLimit` (type: `integer`):

Maximum number of written reviews returned per tutor profile (needs Fetch full tutor profiles). Values above 50 are lowered to 50. Set 0 to return no reviews.

## `maxPages` (type: `integer`):

Safety ceiling on how many result pages to walk per search. Empty or 0 (default) means unlimited -- the walk continues until the source's own page count is reached. This does NOT cap the number of items; use Max items for that.

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

Maximum number of tutors to collect across the whole run. The single overall limit. Set 0 for unlimited.

## `proxy` (type: `object`):

Connection settings. The default enables Apify Proxy and works on every plan; leaving it as is gives the most reliable results.

## `resumeFromRunId` (type: `string`):

Optional. Paste a previous run id (or its dataset id) to continue that run: tutors already collected there are skipped, so this run returns only new tutors (a delta). For recurring daily monitoring of the same search, use Incremental mode below instead.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring. The first run returns all matching tutors as NEW. Later runs normally return only NEW, UPDATED, and REAPPEARED tutors. UPDATED means a tutor's own data changed: price, rating, availability flags, profile text, subjects, gallery or reviews. Excluded from the comparison, so they never trigger UPDATED on their own: scrapedAt, distance (it depends on the proxy location) and resultTier (the search's own ranking), plus any fields you list in "Ignore fields when detecting changes". Whole numbers compare equal whether written 25 or 25.0. Turn on "Emit unchanged" or "Emit expired" only when you also want those tutors returned (and billed). State is kept separately for each search/URL and filter setup; use State key when you want to name or deliberately share a monitoring campaign. To continue one specific interrupted run instead, use Resume from a previous run above.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or to deliberately share state across differently-configured runs. Leave empty to let the actor derive a key automatically from the search/URL and filter settings -- different searches then never mix state with each other.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return tutors that have not changed since the last run, marked UNCHANGED. This returns -- and bills -- extra rows you already have, so leave it off unless you specifically want the full snapshot every run.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return tutors that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search or URL set -- not when Max items or Max pages capped it, or when Resume was used. This returns -- and bills -- extra synthetic rows, so leave it off unless you need expiry tracking.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Top-level field names to exclude from UPDATED change detection, in addition to the built-in exclusions (scrapedAt, distance, resultTier). Use it when a field is noisy in your setup, for example a field that changes without the tutor really changing. Ignored fields stay in the output; they just never make a tutor UPDATED.

## `mcpConnectors` (type: `array`):

Optional. Connect MCP servers (Notion, Linear, Airtable, Apify) to push the scraped tutors straight into your tools after the run.

## `notionParentPageUrl` (type: `string`):

Optional. When a Notion MCP connector is set, the URL of the Notion page under which a results database is created.

## `maxNotifyListings` (type: `integer`):

Optional. Cap on how many tutors are pushed to the connected MCP apps.

## Actor input object example

```json
{
  "mode": "search",
  "subjects": [
    "Mathematics"
  ],
  "locations": [
    "United States"
  ],
  "urls": [
    "https://www.superprof.com/s/mathematics,United-States,,,,.html",
    "https://www.superprof.com/s/mathematics,New-York,,,,.html",
    "https://www.superprof.com/s/mathematics,Online,,,,.html",
    "https://www.superprof.com/empathy-driven-intuitive-math-taught-engineer-with-years-experience-stop-memorizing-formulas-dive-into-the-essence-with.html"
  ],
  "lessonType": "any",
  "onlyWebcam": false,
  "onlyFirstLessonFree": false,
  "sortBy": "relevance",
  "fetchDetails": true,
  "reviewLimit": 50,
  "maxPages": 0,
  "maxItems": 20,
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `detail` (type: `string`):

No description

## `reviews` (type: `string`):

No description

## `changes` (type: `string`):

No description

# 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 = {
    "mode": "search",
    "subjects": [
        "Mathematics"
    ],
    "locations": [
        "United States"
    ],
    "urls": [
        "https://www.superprof.com/s/mathematics,United-States,,,,.html",
        "https://www.superprof.com/s/mathematics,New-York,,,,.html",
        "https://www.superprof.com/s/mathematics,Online,,,,.html",
        "https://www.superprof.com/empathy-driven-intuitive-math-taught-engineer-with-years-experience-stop-memorizing-formulas-dive-into-the-essence-with.html"
    ],
    "proxy": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "ignoreFieldsForChanges": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/superprof-tutor-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 = {
    "mode": "search",
    "subjects": ["Mathematics"],
    "locations": ["United States"],
    "urls": [
        "https://www.superprof.com/s/mathematics,United-States,,,,.html",
        "https://www.superprof.com/s/mathematics,New-York,,,,.html",
        "https://www.superprof.com/s/mathematics,Online,,,,.html",
        "https://www.superprof.com/empathy-driven-intuitive-math-taught-engineer-with-years-experience-stop-memorizing-formulas-dive-into-the-essence-with.html",
    ],
    "proxy": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "ignoreFieldsForChanges": [],
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/superprof-tutor-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 '{
  "mode": "search",
  "subjects": [
    "Mathematics"
  ],
  "locations": [
    "United States"
  ],
  "urls": [
    "https://www.superprof.com/s/mathematics,United-States,,,,.html",
    "https://www.superprof.com/s/mathematics,New-York,,,,.html",
    "https://www.superprof.com/s/mathematics,Online,,,,.html",
    "https://www.superprof.com/empathy-driven-intuitive-math-taught-engineer-with-years-experience-stop-memorizing-formulas-dive-into-the-essence-with.html"
  ],
  "proxy": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "ignoreFieldsForChanges": []
}' |
apify call abotapi/superprof-tutor-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/superprof-tutor-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/PY4ahcHv1QfF2YaU3/builds/EXeBfsLABSXLWqCzg/openapi.json
