ytdl-org/youtube-dl · error · ExtractorError
Course %s is not free. You have to pay for it before you can
Error message
Course %s is not free. You have to pay for it before you can download. Use this URL to confirm purchase: %s
What it means
Raised by UdemyIE._enroll_course when the course page HTML exposes a /(payment|cart)/checkout/ link — the course is paid and not enrolled, so lecture content cannot be fetched. The error embeds the full checkout URL so the user can complete the purchase in a browser. Expected error.
Source
Thrown at youtube_dl/extractor/udemy.py:93
r'ng-init=["\'].*\bcourse=({.+?})[;"\']',
webpage, 'course', default='{}')),
video_id, fatal=False) or {}
course_id = course.get('id') or self._search_regex(
[
r'data-course-id=["\'](\d+)',
r'"courseId"\s*:\s*(\d+)'
], webpage, 'course id')
return course_id, course.get('title')
def _enroll_course(self, base_url, webpage, course_id):
def combine_url(base_url, url):
return compat_urlparse.urljoin(base_url, url) if not url.startswith('http') else url
checkout_url = unescapeHTML(self._search_regex(
r'href=(["\'])(?P<url>(?:https?://(?:www\.)?udemy\.com)?/(?:payment|cart)/checkout/.+?)\1',
webpage, 'checkout url', group='url', default=None))
if checkout_url:
raise ExtractorError(
'Course %s is not free. You have to pay for it before you can download. '
'Use this URL to confirm purchase: %s'
% (course_id, combine_url(base_url, checkout_url)),
expected=True)
enroll_url = unescapeHTML(self._search_regex(
r'href=(["\'])(?P<url>(?:https?://(?:www\.)?udemy\.com)?/course/subscribe/.+?)\1',
webpage, 'enroll url', group='url', default=None))
if enroll_url:
webpage = self._download_webpage(
combine_url(base_url, enroll_url),
course_id, 'Enrolling in the course',
headers={'Referer': base_url})
if '>You have enrolled in' in webpage:
self.to_screen('%s: Successfully enrolled in the course' % course_id)
def _download_lecture(self, course_id, lecture_id):
return self._download_json(View on GitHub (pinned to 956b8c5855)
Solutions
- Open the checkout URL from the error message in a browser, buy the course, then export cookies and rerun with --cookies.
- If a free coupon exists, enroll via the coupon link in the browser first so /course/subscribe/ is used, then pass those cookies.
- Log into Udemy in a browser and pass --username/--password or --cookies so _enroll_course sees your entitlement.
- Choose a genuinely free course/lecture.
Defensive patterns
Strategy: try-catch
Validate before calling
import requests
html = requests.get(lecture_url, cookies=udemy_cookies).text
if '/payment/checkout/' in html or '/cart/checkout/' in html:
needs_purchase(course_id) Type guard
def udemy_needs_checkout(html: str) -> bool:
return '/payment/checkout/' in html or '/cart/checkout/' in html Try / catch
try:
ydl.extract_info(lecture_url)
except ExtractorError as e:
if 'not free' in str(e):
checkout = str(e).split('purchase: ')[-1].strip()
record_checkout_url(course_id, checkout)
else:
raise Prevention
- Enroll (including via free coupons) in a browser before downloading; then export cookies.
- Reuse the checkout URL embedded in the error message to complete purchase instead of guessing.
When it happens
Trigger: Extracting an Udemy lecture while not enrolled: _extract_course_info loads the page, and the checkout-URL regex matches an href to /payment/checkout/ or /cart/checkout/, triggering the raise before the free /course/subscribe/ enroll path is tried.
Common situations: Paid Udemy courses without a logged-in enrolled account, logged-out users hitting paywalled lecture pages, free coupon not applied (must visit the coupon URL first to enroll).
Related errors
- Video %s is not available for free
- Unable to login. Twitch said: %s
- Udemy asks you to solve a CAPTCHA. Login with browser, solve
- Unable to login: %s
- We're sorry, but either the User ID or Password entered is n
AI-assisted analysis of ytdl-org/youtube-dl@956b8c5855 (2026-08-14).
Data as JSON: /api/errors/fc2140fb98294a74.
Report an issue: GitHub.