Teachable Scraper - Courses, Prices & Instructors avatar

Teachable Scraper - Courses, Prices & Instructors

Pricing

from $3.80 / 1,000 results

Go to Apify Store
Teachable Scraper - Courses, Prices & Instructors

Teachable Scraper - Courses, Prices & Instructors

Extract every course from any Teachable school: title, price, original price, currency, instructor, enrolled students, rating, reviews and the section-by-section curriculum. Reads both catalogue layouts and custom domains. Paste a school URL or specific course URLs. Export JSON, CSV or Excel.

Pricing

from $3.80 / 1,000 results

Rating

0.0

(0)

Developer

SilentFlow

SilentFlow

Maintained by Community

Actor stats

1

Bookmarked

7

Total users

0

Monthly active users

3 days ago

Last modified

Share

Teachable Scraper

Turn any Teachable school into a structured course catalogue. Prices, instructors, enrolment numbers, ratings and the full lesson outline for every course a school sells. A 36 course school in about three minutes.

How it works

Three steps, no setup.

  1. Paste a school URL. Anything that shows courses in your browser works: https://yourschool.teachable.com, its /courses page, its /l/products catalogue, or a custom domain.
  2. Press Start. The Actor walks the catalogue, then opens each course to read its price, instructor, outline and reviews.
  3. Take your rows. One row per course, 31 fields each, ready to view in a table or pull as JSON, CSV or Excel.

Already know which courses you want? Give it course URLs instead and skip the catalogue entirely.

โœจ Why teams choose this over other Teachable scrapers

Ever pointed a scraper at a school and got nothing back, with no idea whether the school was empty or the tool had failed? Ever built a price list, then found half the prices missing? Ever needed the lesson outline and got only a title?

  • ๐Ÿซ Reads both kinds of Teachable catalogue. Teachable runs two catalogue templates side by side, the older /courses grid and the newer /l/products one, and a school serves whichever it was built with. This Actor reads both. On a modern catalogue that returns nothing elsewhere, it returned all 27 listings.
  • ๐Ÿ“š Gets the whole catalogue, not part of it. A school's grid links its courses in two different ways, and reading only one of them quietly drops the rest. On a 36 course school, that is the difference between 36 rows and 14. Both forms are followed, and the same course is never billed twice.
  • ๐Ÿ’ต Prices you can trust. The price comes from the pricing plans the course page publishes, with the currency, and the cheapest paid plan when a course offers several. A paid course is never reported as free, and a $49 course never arrives as $4,900.
  • ๐Ÿงญ The lesson outline, section by section. Every course carries its curriculum: section titles, their order, and the number of lessons in each. One course came back with 29 lessons across 7 sections, another with 83 across 21.
  • ๐Ÿ‘ค The person behind the course. Instructor name, bio, title and avatar, pulled from the school's own instructor block, so you can qualify a creator and not just a listing.
  • ๐Ÿ” An empty run tells you why. When a run returns nothing it says whether the school lists no courses publicly or its pages could not be read. You never have to guess which.
  • ๐ŸŒ Subdomains and custom domains both work. Plenty of schools run on their own domain. Paste whichever URL your browser shows.
  • ๐ŸŽฏ Point it at courses, not just schools. Mix school URLs and direct course URLs in the same run, in either page style.

๐ŸŽฏ What you can do with Teachable data

TeamWhat they build
PricingA benchmark of what courses in one niche actually charge, with original prices next to current ones, before setting their own
GrowthLead lists of course creators worth partnering with, filtered by student count, topic and whether they sell paid or free courses
ContentCurriculum gap analysis across rival schools: which sections everyone teaches, and the 29 lesson course that goes deeper than your 9 lesson one
MarketplaceA course directory that refreshes itself, with titles, prices, thumbnails, instructors and descriptions for every listing
AffiliateCatalogue pages built from real listings, so a retired course stops earning clicks that go nowhere
ResearchDatasets on the creator economy: price distribution, catalogue size and enrolment per school across a sample of schools
SalesQualified outreach to creators, using their own course topics, audience size and ratings as the opening line
CompetitiveA weekly snapshot of a rival school, so a new launch or a price change shows up as a diff

๐Ÿ“ฅ Input parameters

ParameterTypeDefaultDescription
schoolUrlsarrayTeachable schools to scrape. A homepage, a /courses page, a /l/products catalogue, or a custom domain
courseUrlsarrayDirect course URLs, in either page style: /p/course-name or /l/pdp/course-name
scrapeDetailsbooleantrueOpen each course for its price, instructor, outline, rating and reviews. Turn it off for a fast catalogue of titles and links
scrapeSalesPagebooleanfalseAlso keep the sales page copy as text, for marketing and positioning analysis
maxItemsinteger100Stop after this many courses. Set it to what you actually need

Give it schoolUrls, courseUrls, or both.

๐Ÿ“Š Output data

One row per course. Every field is what a visitor sees on the school's public pages.

{
"id": "1893119",
"title": "Red Team Hacker Bundle: Learn Hacking Online Red Team Style",
"slug": "1893119",
"url": "https://stationx.teachable.com/courses/1893119",
"shortDescription": "Master ethical hacking with the Red Team Hacker Bundle. Learn red team tactics from the ground up.",
"price": 195,
"originalPrice": 500,
"priceText": "$195",
"currency": "USD",
"isFree": false,
"studentsCount": 50000,
"reviewsCount": 3,
"averageRating": 5,
"instructorName": "Brandon Dennis",
"instructorTitle": "Founder",
"thumbnail": "https://www.stationx.net/wp-content/uploads/2025/04/Red-Team-Hacker-Bundle.jpg",
"testimonials": [
{ "author": "Jared M.", "content": "Exactly the depth I needed.", "rating": 5 }
],
"schoolName": "StationX",
"schoolUrl": "https://stationx.teachable.com",
"scrapedAt": "2026-09-12T16:33:41Z"
}

A course from a modern catalogue carries its outline:

{
"id": "2870553",
"title": "Teachable Blueprint",
"slug": "teachable-blueprint",
"url": "https://academy.teachable.com/l/pdp/teachable-blueprint",
"priceText": "FREE",
"isFree": true,
"currency": "USD",
"category": "Course",
"instructorName": "Monique Lu",
"lessonsCount": 29,
"curriculum": [
{ "title": "Welcome & Orientation", "lessonsCount": 4, "position": 1 },
{ "title": "What's your vision?", "lessonsCount": 4, "position": 2 },
{ "title": "Setting Up Your School", "lessonsCount": 5, "position": 3 }
]
}

๐Ÿ—‚๏ธ Data fields

31 fields per course. Empty fields are omitted rather than filled with a guess.

GroupFields
Identityid, title, slug, url, schoolName, schoolUrl, scrapedAt
Textdescription, shortDescription, salesPageContent
Priceprice, originalPrice, priceText, currency, isFree
AudiencestudentsCount, reviewsCount, averageRating
ContentlessonsCount, curriculum, category, subCategory, tags
InstructorinstructorName, instructorTitle, instructorBio, instructorAvatar
Mediathumbnail, promoVideo, images
Reviewstestimonials

Nested objects carry more:

  • curriculum is a list of sections, 4 fields each: title, lessonsCount, position, lectures.
  • lectures, where the school publishes them, holds 5 fields per lesson: title, type, duration, isFree, position.
  • testimonials holds 5 fields per review: author, content, rating, date, avatar.

Usability notes: id is Teachable's own course or product id and is stable. url is permanent. scrapedAt is UTC in RFC 3339. priceText is the string the page displays, such as $195, $1,199/year or FREE, while price is the number, so a yearly plan is readable both ways. category is Teachable's own label for the listing: Course, Digital product or Collection.

๐Ÿš€ Examples

Get every course in a school

{
"schoolUrls": [{ "url": "https://stationx.teachable.com/courses" }]
}

Scrape a school on the newer catalogue

{
"schoolUrls": [{ "url": "https://academy.teachable.com" }],
"maxItems": 50
}

Pull a fast catalogue without opening every course

{
"schoolUrls": [{ "url": "https://stationx.teachable.com/courses" }],
"scrapeDetails": false,
"maxItems": 500
}

Read three specific courses you already know

{
"courseUrls": [
{ "url": "https://crypto.teachable.com/p/what-is-bitcoin" },
{ "url": "https://academy.teachable.com/l/pdp/teachable-blueprint" },
{ "url": "https://sixfigureacademy.teachable.com/p/start-your-own-online-scrub-boutique" }
]
}

Keep the sales page copy for positioning analysis

{
"schoolUrls": [{ "url": "https://sixfigureacademy.teachable.com" }],
"scrapeSalesPage": true,
"maxItems": 25
}

Benchmark several schools in one run

{
"schoolUrls": [
{ "url": "https://stationx.teachable.com/courses" },
{ "url": "https://academy.teachable.com" },
{ "url": "https://crypto.teachable.com" }
],
"maxItems": 300
}

๐Ÿค– Copy to your AI assistant

Paste this into Claude, ChatGPT or Cursor to give it full context about this Actor:

You have access to the Teachable Scraper on Apify: SilentFlow/teachable-scraper-ppr
Input schema:
- schoolUrls (array of {url}): Teachable schools. Accepts a homepage, a /courses page,
a /l/products catalogue, or a custom domain.
- courseUrls (array of {url}): direct course pages, /p/<slug> or /l/pdp/<slug>.
- scrapeDetails (boolean, default true): open each course for price, instructor,
curriculum, rating and reviews.
- scrapeSalesPage (boolean, default false): also return the sales page copy as text.
- maxItems (integer, default 100): stop after this many courses.
Provide schoolUrls, courseUrls, or both.
Output, one row per course, 31 fields:
- id (string), title (string), slug (string), url (string)
- description (string), shortDescription (string), salesPageContent (string)
- price (number), originalPrice (number), priceText (string), currency (string), isFree (boolean)
- studentsCount (number), reviewsCount (number), averageRating (number)
- lessonsCount (number), category (string), subCategory (string), tags (array of string)
- curriculum (array of {title, lessonsCount, position, lectures[{title, type, duration, isFree, position}]})
- instructorName (string), instructorTitle (string), instructorBio (string), instructorAvatar (string)
- thumbnail (string), promoVideo (string), images (array of string)
- testimonials (array of {author, content, rating, date, avatar})
- schoolName (string), schoolUrl (string), scrapedAt (string, RFC 3339 UTC)
Empty fields are omitted. Use apify-client for Python or JS.

๐Ÿ’ป Integrations

Build a course price benchmark in Python

from apify_client import ApifyClient
from statistics import median
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("SilentFlow/teachable-scraper-ppr").call(run_input={
"schoolUrls": [
{"url": "https://stationx.teachable.com/courses"},
{"url": "https://crypto.teachable.com"},
],
"maxItems": 200,
})
paid = [i for i in client.dataset(run["defaultDatasetId"]).iterate_items() if i.get("price")]
paid.sort(key=lambda i: i["price"])
print(f"{len(paid)} paid courses, median ${median(i['price'] for i in paid):.0f}")
for course in paid[:10]:
print(f" {course['price']:>6.0f} {course.get('currency','')} {course['title'][:60]}")

Build a creator lead list in Node.js

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });
const run = await client.actor('SilentFlow/teachable-scraper-ppr').call({
schoolUrls: [{ url: 'https://stationx.teachable.com/courses' }],
maxItems: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const leads = items
.filter((c) => c.instructorName && (c.studentsCount ?? 0) > 10000)
.map((c) => ({
instructor: c.instructorName,
course: c.title,
students: c.studentsCount,
rating: c.averageRating,
url: c.url,
}))
.sort((a, b) => b.students - a.students);
console.table(leads.slice(0, 20));

Export a catalogue to CSV for the content team

import csv
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("SilentFlow/teachable-scraper-ppr").call(run_input={
"schoolUrls": [{"url": "https://academy.teachable.com"}],
"maxItems": 200,
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
with open("catalogue.csv", "w", newline="", encoding="utf-8") as handle:
writer = csv.writer(handle)
writer.writerow(["title", "instructor", "price", "currency", "lessons", "sections", "url"])
for course in rows:
writer.writerow([
course.get("title"),
course.get("instructorName", ""),
course.get("priceText", ""),
course.get("currency", ""),
course.get("lessonsCount", ""),
len(course.get("curriculum") or []),
course.get("url"),
])
print(f"wrote {len(rows)} courses to catalogue.csv")

Watch one school on a schedule with the Apify CLI

apify call SilentFlow/teachable-scraper-ppr \
--input='{"schoolUrls":[{"url":"https://academy.teachable.com"}],"maxItems":200}'

๐Ÿ“ค Data export

Results are available on the dataset page as JSON, CSV, Excel, XML, HTML and RSS, and through the API:

https://api.apify.com/v2/datasets/{DATASET_ID}/items?format=csv&token=YOUR_TOKEN

The dataset also has a Courses view, which shows the columns most people want first: title, instructor, price, lessons, students, rating and link.

๐Ÿ“ˆ Performance

Measured on 12 September 2026:

RunResult
36 course school, full details36 of 36 courses, every page read, about 3 minutes
Modern catalogue, full details27 listings found, details at roughly 2 seconds per course
Catalogue only, scrapeDetails: false5 courses in 1 second

Catalogue reads are a single page per school. Detail reads are one page per course, so a run's length tracks maxItems almost exactly.

๐Ÿ’ก Tips for best results

  • Paste the URL that works in your browser. If your browser shows the courses, the Actor will too. The homepage, the /courses page and the /l/products catalogue all resolve to the same catalogue.
  • Turn off scrapeDetails to map a school first. You get titles, links and thumbnails in one page read, then run again with details only for the courses you care about.
  • Set maxItems to the size of the job. A school with 30 courses does not need a ceiling of 5,000, and the run length follows the number you set.
  • Use courseUrls for monitoring. Once you know the courses you track, naming them directly is faster and steadier than walking a catalogue that reorders itself.
  • Check priceText when a course has several plans. price holds the cheapest paid plan as a number; priceText keeps the wording, so $1,199/year stays legible as a yearly plan.
  • Read the run's final message when a run is empty. It distinguishes a school that lists nothing publicly from pages that could not be read, and only the second is worth running again.

โ“ FAQ

Q: What exactly does it extract? Everything a visitor can see without logging in: the course catalogue, and for each course its title, description, price and currency, original price, free flag, instructor details, student and review counts, rating, product type, thumbnail, testimonials, and the curriculum outline. Optionally the sales page copy.

Q: Do I need a Teachable account? No. No login, no API key.

Q: Will it work on my school? Yes, on both catalogue templates Teachable serves and on custom domains. If your courses are visible to a visitor, they are readable.

Q: How do I scrape only some courses? Put their URLs in courseUrls. Both page styles are accepted, /p/course-name and /l/pdp/course-name. You can mix courseUrls and schoolUrls in one run.

Q: How fresh is the data? It is read live at the moment of the run. Nothing is cached, so a price changed this morning is the price you get.

Q: Why does a course sometimes have no price? Some schools send their course pages to their own website instead of keeping them on Teachable, and a page on a school's own site states its price however its theme happens to. When no price can be read from the page, the field is left empty rather than filled with a number found somewhere on the page. Every other field still ships.

Q: Why do some courses have section titles but no individual lessons? On the newer course page, a section reveals its lessons only when a visitor expands it, so the section title, its position and its lesson count are published but the lesson titles are not. Older sales pages publish the full lesson list, and it comes back in lectures.

Q: Can I scrape several schools at once? Yes. Put as many as you like in schoolUrls. maxItems applies to the run as a whole, so raise it when you add schools.

Q: What happens with a school that is switched off or empty? A switched off school answers normally and lists nothing, so the run finishes with no rows and says so in its final message. That is the honest answer rather than an error.

Q: Does maxItems count catalogue pages or courses? Courses. It is the number of rows you get.

Q: Are the image and video URLs permanent? thumbnail and images point at the school's own media and stay valid as long as the school keeps them. url is permanent.

Q: What is Teachable? Teachable is a platform where independent creators host and sell their own online courses, each school on its own subdomain or custom domain. It is the counterpart to a marketplace like Udemy, except the catalogue belongs to the creator rather than to a central storefront, which is why there is no single place to query and why per school scraping is the way to see it.

This Actor extracts publicly available data from Teachable schools. It does not bypass any login, paywall or CAPTCHA, and it does not access course content that requires enrolment. Users are responsible for complying with Teachable's terms of service and with applicable data protection laws, including GDPR and CCPA. The output contains personal data where a school publishes it, such as instructor names, bios and avatars, and reviewer names in testimonials, and must be handled accordingly. The data returned is informational; verify it before using it for regulated purposes.

๐Ÿ“ฌ Support

Need a field this Actor does not return yet, or a school shape it does not read? Tell us what you are trying to build and we ship quickly.