# Udemy API

> Every Udemy endpoint on Cotlet: inputs, JSON Schema, cURL, Python and Node.js examples and an example response for each call.

Source: https://cotlet.dev/docs/api/udemy

Docs sections: [Get started](https://cotlet.dev/docs.md) · [REST API](https://cotlet.dev/docs/api.md) · [MCP](https://cotlet.dev/docs/mcp.md) · [SDKs & CLI](https://cotlet.dev/docs/sdks.md) · [Integrations](https://cotlet.dev/docs/integrations.md)

11 endpoints for Udemy. Each one is a POST with a JSON body, called with your API key.

- Base URL: `https://cotlet.dev/api/v1/udemy`
- Connects with: Your own Udemy instructor account (the account that teaches the courses)
- Integration page: https://cotlet.dev/integrations/udemy-api-mcp-server

## Endpoints

| Endpoint | Access | Description |
|---|---|---|
| `list_courses` | read | Courses you teach, with status, price, students, rating and URL. |
| `get_course_performance` | read | Revenue, enrollments and rating for one course over a period, from the Performance dashboard. |
| `get_revenue_report` | read | Monthly earnings with revenue by channel (Udemy Organic, Your Promotions, Udemy Business and more), by course, refunds and tax withheld. |
| `list_coupons` | read | Coupons of a course with code, type, price, redemptions and expiry, plus how many you can still create this month. |
| `list_reviews` | read | Course reviews with rating, text, date and your response if any. |
| `list_questions` | read | Questions from the course Q&A with lecture, text, replies and whether it is answered. |
| `create_coupon` | write | New coupon for a course, within Udemy's monthly limit per course; Udemy sets each type's duration. |
| `update_course_landing_page` | write | Change the title, subtitle or description shown on the public course page. |
| `send_announcement` | write | Email enrolled students: an educational announcement or a promotional email, within the monthly limits Udemy sets per course. |
| `reply_to_question` | write | Post an instructor answer in the course Q&A. |
| `respond_to_review` | write | Public instructor response under a student review. |

## List courses

`POST https://cotlet.dev/api/v1/udemy/list_courses`

Access: read.

Returns every course on your instructor account, published and draft. Use course_id from each item as the parameter of the other endpoints. Students and rating are the numbers Udemy shows for the course.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `status` | enum | no | published, draft or all. Default: all. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/list_courses \
  -H "Authorization: Bearer $COTLET_API_KEY"
```

**Python**

```python
result = client.call("udemy", "list_courses")
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'list_courses');
```

**CLI**

```bash
cotlet call udemy list_courses
```

### Example response

```json
{
  "courses": [
    {
      "course_id": "4821736",
      "title": "Python for Beginners",
      "status": "published",
      "price": "$54.99",
      "students": 8421,
      "rating": 4.6,
      "url": "https://www.udemy.com/course/python-for-yourname/"
    },
    {
      "course_id": "5102298",
      "title": "Excel Pivot Tables",
      "status": "draft",
      "price": null,
      "students": 0,
      "rating": null,
      "url": null
    }
  ]
}
```

## Get course performance

`POST https://cotlet.dev/api/v1/udemy/get_course_performance`

Access: read.

Returns the figures the Performance dashboard shows for one course: revenue, new enrollments and average rating for the period. Use it for a weekly or monthly check on a single course.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `period` | enum | no | 7d, 30d, 12m or all. Default: 30d. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/get_course_performance \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2"}'
```

**Python**

```python
result = client.call("udemy", "get_course_performance", {"course_id":"course_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'get_course_performance', {"course_id":"course_8c41d2"});
```

**CLI**

```bash
cotlet call udemy get_course_performance --data '{"course_id":"course_8c41d2"}'
```

### Example response

```json
{
  "course_id": "4821736",
  "period": "30d",
  "revenue": {
    "amount": 1284.5,
    "currency": "USD"
  },
  "enrollments": 212,
  "rating": 4.6
}
```

## Get revenue report

`POST https://cotlet.dev/api/v1/udemy/get_revenue_report`

Access: read.

Returns the monthly Revenue report: total earnings, then the split by channel and by course, with refunds and tax withheld. One month per call; the current month is partial until Udemy closes it.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `month` | string | yes | Month as YYYY-MM. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/get_revenue_report \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"month":"month"}'
```

**Python**

```python
result = client.call("udemy", "get_revenue_report", {"month":"month"})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'get_revenue_report', {"month":"month"});
```

**CLI**

```bash
cotlet call udemy get_revenue_report --data '{"month":"month"}'
```

### Example response

```json
{
  "month": "2026-09",
  "currency": "USD",
  "total": 2310.4,
  "by_channel": [
    {
      "channel": "Udemy Organic",
      "amount": 1420.1
    },
    {
      "channel": "Your Promotions",
      "amount": 612.3
    },
    {
      "channel": "Udemy Business",
      "amount": 278
    }
  ],
  "by_course": [
    {
      "course_id": "4821736",
      "title": "Python for Beginners",
      "amount": 1790.2
    },
    {
      "course_id": "5102298",
      "title": "Excel Pivot Tables",
      "amount": 520.2
    }
  ],
  "refunds": 41.5,
  "tax_withheld": 0
}
```

## List coupons

`POST https://cotlet.dev/api/v1/udemy/list_coupons`

Access: read.

Lists the coupons of one course, newest first, with type, price, redemptions and expiry. It also returns how many new coupons you can still create this month, because Udemy limits that number per course.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `active_only` | boolean | no | Skip expired coupons. Default: true. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/list_coupons \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2"}'
```

**Python**

```python
result = client.call("udemy", "list_coupons", {"course_id":"course_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'list_coupons', {"course_id":"course_8c41d2"});
```

**CLI**

```bash
cotlet call udemy list_coupons --data '{"course_id":"course_8c41d2"}'
```

### Example response

```json
{
  "coupons": [
    {
      "coupon_id": "9917000",
      "code": "SEPT1499",
      "type": "custom_price",
      "price": "14.99",
      "redemptions": 37,
      "expires_at": "2026-10-05T09:00:00-07:00"
    }
  ],
  "coupons_left_this_month": 2
}
```

## List reviews

`POST https://cotlet.dev/api/v1/udemy/list_reviews`

Access: read.

Lists reviews of one course, newest first, with rating, text, date and your response if you wrote one. Filter by star rating or unresponded_only to find what still needs an answer.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `rating` | integer | no | Only this star rating, 1 to 5. |
| `unresponded_only` | boolean | no | Skip reviews you already responded to. |
| `since` | date | no | Only reviews from this date on. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/list_reviews \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2"}'
```

**Python**

```python
result = client.call("udemy", "list_reviews", {"course_id":"course_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'list_reviews', {"course_id":"course_8c41d2"});
```

**CLI**

```bash
cotlet call udemy list_reviews --data '{"course_id":"course_8c41d2"}'
```

### Example response

```json
{
  "reviews": [
    {
      "review_id": "rv_33801",
      "rating": 2,
      "text": "Pivot tables were skipped.",
      "created_at": "2026-10-02T11:20:00Z",
      "response": null
    }
  ],
  "next_cursor": null
}
```

## List Q&A questions

`POST https://cotlet.dev/api/v1/udemy/list_questions`

Access: read.

Lists questions from the course Q&A, newest first, each with its lecture, text and replies. Set unanswered_only to see questions with no instructor reply.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `unanswered_only` | boolean | no | Only questions with no instructor reply. |
| `since` | date | no | Only questions from this date on. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/list_questions \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2"}'
```

**Python**

```python
result = client.call("udemy", "list_questions", {"course_id":"course_8c41d2"})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'list_questions', {"course_id":"course_8c41d2"});
```

**CLI**

```bash
cotlet call udemy list_questions --data '{"course_id":"course_8c41d2"}'
```

### Example response

```json
{
  "questions": [
    {
      "question_id": "q_70418",
      "lecture": "Filters in Excel",
      "text": "How do I toggle filters faster?",
      "created_at": "2026-10-06T08:12:00Z",
      "answered": false,
      "replies": []
    }
  ],
  "next_cursor": null
}
```

## Create coupon

`POST https://cotlet.dev/api/v1/udemy/create_coupon`

Access: write. Confirm: waits for the user's OK before it runs.

Creates one coupon for a course and returns its code and link. Udemy's rule is three new coupons per course per calendar month, and Udemy does not let you delete a coupon afterwards. A custom price must be inside the range Udemy offers. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `type` | enum | yes | best_price, custom_price, free_open or free_targeted. |
| `code` | string | no | Your own coupon code. Default: generated. |
| `price` | string | no | For custom_price: an amount inside the range Udemy offers, e.g. 12.99. |
| `starts_at` | datetime | no | Activation time, ISO 8601 with time zone, inside the current calendar month in U.S. Pacific Time. Default: now. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/create_coupon \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"4821736","type":"custom_price","code":"OCTOBER1299","price":"12.99","starts_at":"2026-10-12T09:00:00-07:00"}'
```

**Python**

```python
result = client.call("udemy", "create_coupon", {"course_id":"4821736","type":"custom_price","code":"OCTOBER1299","price":"12.99","starts_at":"2026-10-12T09:00:00-07:00"})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'create_coupon', {"course_id":"4821736","type":"custom_price","code":"OCTOBER1299","price":"12.99","starts_at":"2026-10-12T09:00:00-07:00"});
```

**CLI**

```bash
cotlet call udemy create_coupon --data '{"course_id":"4821736","type":"custom_price","code":"OCTOBER1299","price":"12.99","starts_at":"2026-10-12T09:00:00-07:00"}'
```

### Example response

```json
{
  "coupon_id": "9917342",
  "course_id": "4821736",
  "code": "OCTOBER1299",
  "type": "custom_price",
  "price": "12.99",
  "currency": "USD",
  "starts_at": "2026-10-12T09:00:00-07:00",
  "expires_at": "2026-11-12T09:00:00-08:00",
  "url": "https://www.udemy.com/course/python-for-yourname/?couponCode=OCTOBER1299",
  "coupons_left_this_month": 2
}
```

## Update course landing page

`POST https://cotlet.dev/api/v1/udemy/update_course_landing_page`

Access: write. Confirm: waits for the user's OK before it runs.

Changes the title, subtitle or description of the public course page. Only the fields you pass change. Udemy may review changes to a published course before they show. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `fields` | object | yes | Any of title, subtitle, description_markdown. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/update_course_landing_page \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"course_8c41d2","fields":"fields"}'
```

**Python**

```python
result = client.call("udemy", "update_course_landing_page", {"course_id":"course_8c41d2","fields":"fields"})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'update_course_landing_page', {"course_id":"course_8c41d2","fields":"fields"});
```

**CLI**

```bash
cotlet call udemy update_course_landing_page --data '{"course_id":"course_8c41d2","fields":"fields"}'
```

### Example response

```json
{
  "course_id": "4821736",
  "status": "saved",
  "updated": [
    "subtitle"
  ]
}
```

## Send announcement

`POST https://cotlet.dev/api/v1/udemy/send_announcement`

Access: write. Confirm: waits for the user's OK before it runs.

Sends one message to the students enrolled in a course. Udemy's rules allow 4 educational announcements and 2 promotional emails per course per month, and promotional emails may not contain external links. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `course_id` | string | yes | Course ID from list_courses. |
| `kind` | enum | yes | educational or promotional. |
| `subject` | string | yes | Email subject. |
| `body_markdown` | string | yes | Message body. Udemy's announcement rules apply. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/send_announcement \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"course_id":"4821736","kind":"educational","subject":"New lecture: React Server Components","body_markdown":"Section 6 now has a lecture on Server Components, with a short exercise at the end."}'
```

**Python**

```python
result = client.call("udemy", "send_announcement", {"course_id":"4821736","kind":"educational","subject":"New lecture: React Server Components","body_markdown":"Section 6 now has a lecture on Server Components, with a short exercise at the end."})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'send_announcement', {"course_id":"4821736","kind":"educational","subject":"New lecture: React Server Components","body_markdown":"Section 6 now has a lecture on Server Components, with a short exercise at the end."});
```

**CLI**

```bash
cotlet call udemy send_announcement --data '{"course_id":"4821736","kind":"educational","subject":"New lecture: React Server Components","body_markdown":"Section 6 now has a lecture on Server Components, with a short exercise at the end."}'
```

### Example response

```json
{
  "announcement_id": "an_2204",
  "course_id": "4821736",
  "kind": "educational",
  "status": "sent",
  "sent_at": "2026-10-12T10:00:00-04:00"
}
```

## Reply to Q&A question

`POST https://cotlet.dev/api/v1/udemy/reply_to_question`

Access: write. Confirm: waits for the user's OK before it runs.

Posts an answer under your instructor name in the course Q&A, visible to every student of the course. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `question_id` | string | yes | Question ID from list_questions. |
| `body` | string | yes | Answer text. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/reply_to_question \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question_id":"question_8c41d2","body":"body"}'
```

**Python**

```python
result = client.call("udemy", "reply_to_question", {"question_id":"question_8c41d2","body":"body"})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'reply_to_question', {"question_id":"question_8c41d2","body":"body"});
```

**CLI**

```bash
cotlet call udemy reply_to_question --data '{"question_id":"question_8c41d2","body":"body"}'
```

### Example response

```json
{
  "question_id": "q_70418",
  "reply_id": "rp_9081",
  "status": "published"
}
```

## Respond to review

`POST https://cotlet.dev/api/v1/udemy/respond_to_review`

Access: write. Confirm: waits for the user's OK before it runs.

Posts a public response under one student review, as you. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `review_id` | string | yes | Review ID from list_reviews. |
| `body` | string | yes | Response text. |

### Request

**cURL**

```bash
curl -X POST https://cotlet.dev/api/v1/udemy/respond_to_review \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"review_id":"rv_33801","body":"Thanks for pointing this out. Pivot tables now have their own section, starting at lecture 14."}'
```

**Python**

```python
result = client.call("udemy", "respond_to_review", {"review_id":"rv_33801","body":"Thanks for pointing this out. Pivot tables now have their own section, starting at lecture 14."})
```

**Node.js**

```js
const result = await cotlet.call('udemy', 'respond_to_review', {"review_id":"rv_33801","body":"Thanks for pointing this out. Pivot tables now have their own section, starting at lecture 14."});
```

**CLI**

```bash
cotlet call udemy respond_to_review --data '{"review_id":"rv_33801","body":"Thanks for pointing this out. Pivot tables now have their own section, starting at lecture 14."}'
```

### Example response

```json
{
  "review_id": "rv_33801",
  "status": "published"
}
```
