Teachable Scraper - Courses, Prices & Instructors
Pricing
from $3.80 / 1,000 results
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
Maintained by CommunityActor 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.
- Paste a school URL. Anything that shows courses in your browser works:
https://yourschool.teachable.com, its/coursespage, its/l/productscatalogue, or a custom domain. - Press Start. The Actor walks the catalogue, then opens each course to read its price, instructor, outline and reviews.
- 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
/coursesgrid and the newer/l/productsone, 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
$49course 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
| Team | What they build |
|---|---|
| Pricing | A benchmark of what courses in one niche actually charge, with original prices next to current ones, before setting their own |
| Growth | Lead lists of course creators worth partnering with, filtered by student count, topic and whether they sell paid or free courses |
| Content | Curriculum gap analysis across rival schools: which sections everyone teaches, and the 29 lesson course that goes deeper than your 9 lesson one |
| Marketplace | A course directory that refreshes itself, with titles, prices, thumbnails, instructors and descriptions for every listing |
| Affiliate | Catalogue pages built from real listings, so a retired course stops earning clicks that go nowhere |
| Research | Datasets on the creator economy: price distribution, catalogue size and enrolment per school across a sample of schools |
| Sales | Qualified outreach to creators, using their own course topics, audience size and ratings as the opening line |
| Competitive | A weekly snapshot of a rival school, so a new launch or a price change shows up as a diff |
๐ฅ Input parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
schoolUrls | array | Teachable schools to scrape. A homepage, a /courses page, a /l/products catalogue, or a custom domain | |
courseUrls | array | Direct course URLs, in either page style: /p/course-name or /l/pdp/course-name | |
scrapeDetails | boolean | true | Open each course for its price, instructor, outline, rating and reviews. Turn it off for a fast catalogue of titles and links |
scrapeSalesPage | boolean | false | Also keep the sales page copy as text, for marketing and positioning analysis |
maxItems | integer | 100 | Stop 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.
| Group | Fields |
|---|---|
| Identity | id, title, slug, url, schoolName, schoolUrl, scrapedAt |
| Text | description, shortDescription, salesPageContent |
| Price | price, originalPrice, priceText, currency, isFree |
| Audience | studentsCount, reviewsCount, averageRating |
| Content | lessonsCount, curriculum, category, subCategory, tags |
| Instructor | instructorName, instructorTitle, instructorBio, instructorAvatar |
| Media | thumbnail, promoVideo, images |
| Reviews | testimonials |
Nested objects carry more:
curriculumis 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.testimonialsholds 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-pprInput 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 ApifyClientfrom statistics import medianclient = 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 csvfrom apify_client import ApifyClientclient = 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:
| Run | Result |
|---|---|
| 36 course school, full details | 36 of 36 courses, every page read, about 3 minutes |
| Modern catalogue, full details | 27 listings found, details at roughly 2 seconds per course |
Catalogue only, scrapeDetails: false | 5 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
/coursespage and the/l/productscatalogue all resolve to the same catalogue. - Turn off
scrapeDetailsto 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
maxItemsto 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
courseUrlsfor monitoring. Once you know the courses you track, naming them directly is faster and steadier than walking a catalogue that reorders itself. - Check
priceTextwhen a course has several plans.priceholds the cheapest paid plan as a number;priceTextkeeps the wording, so$1,199/yearstays 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.
โ๏ธ Legal
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.
๐ Related scrapers
- Udemy Scraper for the marketplace side of online courses
- YouTube Transcript Scraper to turn a creator's free content into text
- Instagram Engagement Scraper to size up a creator's audience
- LinkedIn Profiles & Companies Scraper to enrich a creator lead list
๐ฌ 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.