---
title: "Courses"
description: "List, filter, and load BlendX course details, outlines, effort, and enrollment state."
source: "https://docs.blend-ed.com/blendx-discovery/courses/"
---

# Courses

Catalog routes accept an authenticated user identity or a valid `clientapisecret`. Examples below use the server-side client credential.

## List courses
```
GET /blendxapi/discovery/courses/
```

| Query parameter | Default | Description |
| --- | --- | --- |
| `limit` | `8` | Page size, clamped to 1–100. |
| `page` | `1` | 1-indexed page number. |
| `search` | empty | Search course name, short description, or overview. |
| `org` | empty | Comma-separated organisation names. |
| `tags` | empty | Comma-separated tags; a match on any tag is included. |
| `order_by` | `created_at.desc` | `{field}.{direction}`, for example `display_name.asc`. |
| `fields` | `light` | `light` for cards or `full` for all supported fields. |

```
curl "https://<LMS_HOST>/blendxapi/discovery/courses/?search=safety&org=Acme&tags=compliance&order_by=display_name.asc" \  -H "clientapisecret: $BLENDX_CLIENT_API_SECRET"
```
The envelope contains `courses`, `total_count`, `has_more`, `limit`, `page`, and `offset`. If one item cannot be serialized, the request can still succeed and include `partial_errors`.

### Course list fields
The default `light` fieldset contains every field below.

| Field | Description |
| --- | --- |
| `id` | BlendX/Hasura course UUID used by checkout and client enrollment. |
| `openedx_course_id` | Open edX course key used by detail, outline, effort, and enrollment-check routes. |
| `display_name`, `display_image`, `short_description` | Course card content. |
| `price`, `currency`, `course_mode` | Lowest visible mode price, currency, and mode. |
| `originalPrice`, `discountPct` | Original price and applied regional discount. |
| `nativeCurrency`, `nativeAmount` | Optional local-currency display hints. |
| `start`, `end` | Course availability dates. |
| `org`, `org_name` | Open edX organisation key and learner-facing organisation name. |
| `org_logo_url`, `org_favicon_url` | Organisation branding URLs. |
| `tags` | Assigned tag-name array. |
| `is_invite_only`, `is_published` | Catalog visibility flags. |
| `avg_rating`, `review_count` | Rating summary. |

With `fields=full`, each list item also contains:

| Field | Description |
| --- | --- |
| `overview` | Course overview HTML. |
| `effort` | Configured course-effort value. |
| `enrollment_start`, `enrollment_end` | Enrollment window. |
| `certificate_available_date` | Certificate availability date. |
| `has_any_active_web_certificate` | Whether an active web certificate is configured. |
| `link_to_course` | Learner-dashboard course link. |
| `language`, `self_paced`, `number` | Language, pacing flag, and course number. |
| `course_video_url` | Course promotional video URL. |
| `available_course_modes` | All visible purchase/enrollment modes. |
| `is_new` | New-course status. |

## Regional price fields
Catalog prices are evaluated under each item’s owning organisation policy:

| Field | Meaning |
| --- | --- |
| `price` | Price after the applicable regional discount. |
| `originalPrice` | Price before that discount. |
| `discountPct` | Applied discount percentage, or `0`. |
| `nativeCurrency` | Optional local-currency display hint. Omitted when unavailable. |
| `nativeAmount` | Optional converted display amount. It is not the settlement amount. |

Always use the checkout response as the authoritative amount to charge.

## Get course details
```
GET /blendxapi/discovery/course/{openedx_course_id}/
```
The response contains the following fields:

| Field | Description |
| --- | --- |
| `id`, `openedx_course_id` | BlendX UUID and Open edX course key. |
| `display_name`, `short_description`, `overview` | Course copy; `overview` is HTML. |
| `course_image_url`, `course_video_url` | Course media URLs. |
| `start`, `start_display`, `end` | Course dates and display-ready start value. |
| `effort`, `total_minutes` | Configured effort and computed content duration. |
| `enrollment_start`, `enrollment_end` | Enrollment window. |
| `certificate_available_date`, `has_any_active_web_certificate` | Certificate availability. |
| `language`, `self_paced` | Language and pacing. |
| `is_invite_only`, `is_published`, `is_new` | Catalog status flags. |
| `price`, `course_mode`, `available_course_modes` | Lowest visible offer and all visible modes. |
| `originalPrice`, `discountPct`, `nativeCurrency`, `nativeAmount` | Regional price fields; native fields are optional. |
| `organisation`, `organisation_id` | Organisation short name and UUID. |
| `organisation_logo_url`, `organisation_favicon_url` | Organisation branding URLs. |
| `course_structure` | Hierarchical content structure, or `null` for `fields=summary`. |
| `avg_rating`, `reviews` | Rating and review data. |

Each entry in `available_course_modes` contains `modeSlug`, `minPrice`, `modeDisplayName`, `currency`, `expirationDatetime`, `visible`, `description`, `upsellableCourseModes`, and `hasCertificate`.

Add `?fields=summary` to set `course_structure` to `null` and skip the effort traversal, so `total_minutes` is `0`:

```
curl "https://<LMS_HOST>/blendxapi/discovery/course/course-v1%3AAcme%2BSAF101%2B2026_T1/?fields=summary" \  -H "clientapisecret: $BLENDX_CLIENT_API_SECRET"
```
The course-detail cache defaults to 300 seconds. Regional price fields are applied after the cached base response so one visitor’s location is not reused for another.

> **Note**
> The list uses `display_image`; the detail response uses `course_image_url`. Plan for this naming difference in a shared client model.

## Get an outline
```
GET /blendxapi/discovery/course-outline/{openedx_course_id}/
```
The top level contains `sections`, `total_sections`, `total_subsections`, and `course_info`.

- Each section contains `name` , `usage_key` , `hide_from_toc` , `visible_to_staff_only` , and `subsections` .
- Each subsection contains `name` , `usage_key` , `inaccessible_after_due` , `hide_from_toc` , `visible_to_staff_only` , `exam` , and `units` .
- Each unit contains `name` and `usage_key` .
- `exam` , when present, contains `is_practice_exam` , `is_proctored_enabled` , and `is_time_limited` .
- `course_info` contains `course_key` , `title` , `published_at` , `published_version` , `days_early_for_beta` , `entrance_exam_id` , `self_paced` ,
and `course_visibility` .

This is catalog structure, not a learner-specific courseware-access response.

## Bulk effort and new-course status
```
POST /blendxapi/discovery/courses/effort/Content-Type: application/json
{"course_ids":["course-v1:Acme+SAF101+2026_T1"]}
```
Returns a map from course key to `total_minutes`. Up to 50 IDs are processed; results are cached for 1 hour.

```
POST /blendxapi/discovery/courses/is-new/Content-Type: application/json
{"course_ids":["course-v1:Acme+SAF101+2026_T1"]}
```
Returns a map from course key to boolean. Up to 100 IDs are processed.

## Check learner enrollment
```
GET /blendxapi/discovery/check-enrollment/{openedx_course_id}/
```
With a valid learner session, the response identifies enrollment and the current mode:

```
{"enrolled": true, "mode": "honor"}
```
Without a learner identity, the route returns `{"enrolled": false}` even when the server-side storefront credential is valid.
