Skip to main content
RapidDev - Software Development Agency
bolt-ai-integrationsBolt Chat + API Route

How to Integrate Bolt.new with FutureLearn

FutureLearn has no public API, so direct data integration is not possible. Instead, build your own learning platform in Bolt.new using Supabase (course catalog, video hosting, progress tracking, certificates), link out to FutureLearn courses via their affiliate program, or use Coursera's catalog API for programmatic access to online education content.

What you'll learn

  • Why FutureLearn has no public API and what the practical alternatives are for Bolt.new developers
  • How to build a course catalog and learning platform in Bolt.new with Supabase as the backend
  • How to implement video hosting, progress tracking, and certificate generation in a Bolt-built learning app
  • How to use FutureLearn's affiliate program to link courses from a custom learning portal
  • Which learning platform APIs (Coursera, Teachable, Thinkific) work best with Bolt.new's API route pattern
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate22 min read30 minutesOtherApril 2026RapidDev Engineering Team
TL;DR

FutureLearn has no public API, so direct data integration is not possible. Instead, build your own learning platform in Bolt.new using Supabase (course catalog, video hosting, progress tracking, certificates), link out to FutureLearn courses via their affiliate program, or use Coursera's catalog API for programmatic access to online education content.

Building a Learning Platform with Bolt.new Instead of a FutureLearn API

FutureLearn is one of the UK's most prominent online learning platforms, partnering with over 250 universities and institutions globally to offer short courses, microcredentials, and full degrees. Its learner base skews toward professional upskilling in the UK and Europe. However, unlike some of its competitors, FutureLearn does not offer a public REST API that third-party developers can use to access course data, enrollment information, or learner progress.

For Bolt.new developers, this means there is no way to directly query FutureLearn course listings, check enrollment status, or sync learner progress into a custom app. The platform is designed as a destination, not as a headless service. If you came here hoping to pull FutureLearn course data into your Bolt app, the honest answer is: that is not possible with FutureLearn's current product. However, there are two genuinely useful paths available.

The first path is FutureLearn's affiliate program, which lets you link to FutureLearn courses from your Bolt-built website or app and earn commission when users enroll. This is the right choice if you are building a course recommendation website or learning resource aggregator — you curate content, link outbound to FutureLearn, and the actual learning happens on FutureLearn's platform. The second and more powerful path is building your own learning platform in Bolt.new from scratch, using Supabase as the backend for course catalogs, student accounts, video hosting, progress tracking, and certificate generation. This approach gives you full control over the experience and data. If you need programmatic access to an established course catalog, Coursera's catalog API and Teachable's REST API are the best alternatives — both are documented and accessible through Bolt's server-side API route pattern.

Integration method

Bolt Chat + API Route

FutureLearn does not provide a public API for third-party developers, so there is no direct data integration path. The two practical options are: (1) link to FutureLearn courses using their affiliate program with outbound links, or (2) build a complete learning platform in Bolt.new with Supabase that mirrors FutureLearn's functionality. For developers who need programmatic access to online course catalogs, Coursera and Teachable both offer documented REST APIs that work well through Bolt's API route pattern.

Prerequisites

  • A Bolt.new account and a project using Next.js or Vite with React and TypeScript
  • A Supabase account (free tier is sufficient) if building a custom learning platform
  • A FutureLearn account and approval in their affiliate program (futurelearn.com/partnerships/affiliates) if building an affiliate-based portal
  • A Coursera developer account at coursera.org/about/developer if using Coursera's catalog API as the FutureLearn alternative
  • A deployed URL on Netlify or Bolt Cloud for any OAuth callback flows — OAuth redirects cannot complete in Bolt's WebContainer preview

Step-by-step guide

1

Understand FutureLearn's API limitations and choose your approach

Before writing any code, it is important to understand why direct FutureLearn integration is not possible and which alternative best fits your project goals. FutureLearn, owned by the SEEK Group since 2022, operates as a closed learning platform. Unlike Coursera (which has a public catalog API at api.coursera.org), Udemy (which has an affiliate API), or Teachable (which has a REST API for course creators), FutureLearn has made a deliberate product decision not to expose their course data, enrollment information, or learner progress to third-party developers. There is no FutureLearn developer portal, no OAuth application registration, and no documented REST endpoints. This means any Bolt.new integration strategy must choose one of three paths: (1) The affiliate link path is best if your goal is course discovery and recommendation — you build the front end, FutureLearn delivers the learning, and you earn commission. Affiliate links are simple outbound URLs with a tracking parameter appended. No API, no backend complexity, no deployment requirements — this path works in the Bolt WebContainer preview immediately. (2) The custom platform path is best if you want to own the learning experience end-to-end. You build courses, video players, progress tracking, and certificates inside your Bolt app using Supabase as the backend. This requires more development effort but gives you complete control over content, data, and monetization. (3) The Coursera API alternative is best if you need programmatic access to an established, large-scale course catalog without building your own. Coursera provides a free public API for course discovery that returns titles, descriptions, partner names, and photos. For most Bolt.new developers building educational apps, the custom platform path with Supabase delivers the most value — you get a real product with your own brand and data, rather than sending users away to FutureLearn. Ask yourself: do you want to own the learner relationship, or refer learners to FutureLearn and earn affiliate commission? Your answer determines which path to take.

Bolt.new Prompt

I want to build a learning platform similar to FutureLearn. Before we start coding, set up the Supabase schema. Create a SQL migration that creates these tables: (1) courses: id uuid primary key, title text, slug text unique, description text, category text, level text (Beginner/Intermediate/Advanced), duration_weeks integer, thumbnail_url text, published boolean default false, created_at timestamptz default now(). (2) lessons: id uuid primary key, course_id uuid references courses(id), title text, video_url text, duration_minutes integer, order_index integer, created_at timestamptz default now(). (3) enrollments: id uuid primary key, user_id uuid references auth.users(id), course_id uuid references courses(id), enrolled_at timestamptz default now(), unique(user_id, course_id). (4) lesson_completions: id uuid primary key, user_id uuid references auth.users(id), lesson_id uuid references lessons(id), completed_at timestamptz default now(), unique(user_id, lesson_id). Add RLS policies so users can only read their own enrollments and completions.

Paste this in Bolt.new chat

supabase/migrations/001_learning_platform.sql
1-- supabase/migrations/001_learning_platform.sql
2-- Course catalog
3CREATE TABLE courses (
4 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
5 title TEXT NOT NULL,
6 slug TEXT UNIQUE NOT NULL,
7 description TEXT,
8 category TEXT,
9 level TEXT CHECK (level IN ('Beginner', 'Intermediate', 'Advanced')),
10 duration_weeks INTEGER,
11 thumbnail_url TEXT,
12 published BOOLEAN DEFAULT false,
13 created_at TIMESTAMPTZ DEFAULT now()
14);
15
16-- Lessons within courses
17CREATE TABLE lessons (
18 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
19 course_id UUID REFERENCES courses(id) ON DELETE CASCADE,
20 title TEXT NOT NULL,
21 video_url TEXT,
22 duration_minutes INTEGER,
23 order_index INTEGER NOT NULL,
24 created_at TIMESTAMPTZ DEFAULT now()
25);
26
27-- User enrollments
28CREATE TABLE enrollments (
29 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
30 user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
31 course_id UUID REFERENCES courses(id) ON DELETE CASCADE,
32 enrolled_at TIMESTAMPTZ DEFAULT now(),
33 UNIQUE(user_id, course_id)
34);
35
36-- Lesson completion tracking
37CREATE TABLE lesson_completions (
38 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
39 user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
40 lesson_id UUID REFERENCES lessons(id) ON DELETE CASCADE,
41 completed_at TIMESTAMPTZ DEFAULT now(),
42 UNIQUE(user_id, lesson_id)
43);
44
45-- RLS policies
46ALTER TABLE courses ENABLE ROW LEVEL SECURITY;
47ALTER TABLE lessons ENABLE ROW LEVEL SECURITY;
48ALTER TABLE enrollments ENABLE ROW LEVEL SECURITY;
49ALTER TABLE lesson_completions ENABLE ROW LEVEL SECURITY;
50
51-- Anyone can read published courses and their lessons
52CREATE POLICY "Public read published courses" ON courses FOR SELECT USING (published = true);
53CREATE POLICY "Public read lessons" ON lessons FOR SELECT USING (true);
54
55-- Users manage their own enrollments and completions
56CREATE POLICY "Users manage own enrollments" ON enrollments
57 USING (user_id = auth.uid()) WITH CHECK (user_id = auth.uid());
58CREATE POLICY "Users manage own completions" ON lesson_completions
59 USING (user_id = auth.uid()) WITH CHECK (user_id = auth.uid());

Pro tip: If you choose the affiliate path instead of building a custom platform, you do not need any Supabase setup. Simply store course metadata (title, description, FutureLearn affiliate URL) in a static JSON file or a simple Supabase table — the 'integration' is purely outbound links.

Expected result: The Supabase database has four tables with proper relationships and RLS policies. The schema is ready for building course catalog, lesson player, and progress tracking features in subsequent steps.

2

Build the course catalog page with enrollment

With the database schema in place, the next step is building the course discovery and enrollment flow. The catalog page shows all published courses with their titles, categories, difficulty levels, duration, and thumbnails. For authenticated users, it shows whether they are already enrolled in each course. The enrollment action inserts a row into the enrollments table and redirects the user to the first lesson. The catalog page is a React Server Component that queries Supabase for published courses. For the enrollment check, you fetch the current user's enrollments and compare them against the course list client-side. This avoids N+1 queries — one query for courses, one query for the user's enrollments, then a JavaScript join. For course thumbnails, Supabase Storage is the recommended approach: upload images to a public bucket called 'course-thumbnails' and store the public URL in the courses table. Alternatively, use external image URLs (from Unsplash or your own CDN) stored directly in the thumbnail_url column. The learning platform does not need native FutureLearn thumbnails since you are building your own content. The category filter uses client-side state to filter the visible course cards without re-fetching from the server. This works well for catalogs up to a few hundred courses. For larger catalogs, move filtering to the Supabase query with a WHERE clause on the category column. The difficulty level filter works the same way. When a user clicks 'Enroll', the component calls a Server Action that inserts into the enrollments table. Server Actions are the correct pattern for database mutations in Next.js App Router — they run on the server, have access to process.env credentials, and do not require a separate API route. After enrollment, the user is redirected to the first lesson in the course.

Bolt.new Prompt

Build a course catalog page at app/courses/page.tsx. It should: (1) Fetch all published courses from Supabase (title, slug, description, category, level, duration_weeks, thumbnail_url), (2) Show courses as grid cards with thumbnail, title, category badge, level badge, and duration, (3) Add filter buttons at the top for categories (All, Technology, Business, Health, Creative), (4) Add a 'Enroll Now' button on each card that calls a server action to insert into the enrollments table for the logged-in user, (5) Show 'Continue Learning' instead of 'Enroll Now' for already-enrolled courses, (6) Use Tailwind CSS and shadcn/ui Card components. Fetch the current user's enrollments to determine enrollment state.

Paste this in Bolt.new chat

app/courses/actions.ts
1// app/courses/actions.ts
2'use server';
3import { createClient } from '@/lib/supabase/server';
4import { redirect } from 'next/navigation';
5
6export async function enrollInCourse(courseId: string, firstLessonId?: string) {
7 const supabase = await createClient();
8 const { data: { user } } = await supabase.auth.getUser();
9
10 if (!user) {
11 redirect('/login');
12 }
13
14 const { error } = await supabase
15 .from('enrollments')
16 .insert({ user_id: user.id, course_id: courseId });
17
18 if (error && error.code !== '23505') {
19 // 23505 = unique constraint violation (already enrolled)
20 throw new Error(`Enrollment failed: ${error.message}`);
21 }
22
23 if (firstLessonId) {
24 redirect(`/lessons/${firstLessonId}`);
25 }
26}

Pro tip: Seed your Supabase courses table with 4-6 example courses so the catalog page has content to display immediately in the Bolt preview. Use Supabase's Table Editor to insert rows directly, or add a seed SQL file to your migrations folder.

Expected result: The course catalog page shows published courses as styled cards with category filters. Clicking 'Enroll Now' inserts an enrollment record and the button changes to 'Continue Learning' for enrolled courses.

3

Build the lesson player with progress tracking

The lesson player is the core of the learning experience. Each lesson page shows the video embed, the lesson title, the course navigation sidebar (all lessons in the course with completion indicators), and a 'Mark as Complete' button. When the user clicks 'Mark as Complete', a Server Action inserts a row into the lesson_completions table and updates the sidebar state. For video embedding, YouTube is the simplest and most reliable approach for a Bolt-built learning platform: store the YouTube video URL or ID in the video_url column and embed it using a standard iframe. The YouTube embed URL format is https://www.youtube.com/embed/{videoId}. For private or custom video content, use Supabase Storage with a signed URL. Supabase Storage supports direct video streaming and generates time-limited signed URLs for private content — prompt Bolt to generate an API route that creates a signed URL from SUPABASE_SERVICE_ROLE_KEY. The progress sidebar shows all lessons in the course with a checkmark icon for completed lessons. This sidebar is a Client Component that receives the initial completion state as a prop from the Server Component parent, and updates optimistically when the user marks a lesson complete. Optimistic UI means the checkbox appears checked immediately when clicked, before the database write confirms — this makes the interface feel instant even on slow connections. Course completion is calculated as (completed lessons / total lessons) * 100. Display this as a progress bar at the top of the sidebar. When the percentage reaches 100%, show a congratulations message and a link to download or view the course certificate. The certificate feature is covered in the next step. Navigation between lessons uses the order_index column. The 'Next Lesson' button fetches the lesson with order_index + 1 in the same course. The 'Previous Lesson' button uses order_index - 1. Handle edge cases: no previous lesson on the first lesson, no next lesson on the last lesson.

Bolt.new Prompt

Build a lesson player page at app/lessons/[lessonId]/page.tsx. It should: (1) Fetch the lesson and its parent course from Supabase using the lessonId param, (2) Embed the lesson video as a YouTube iframe (extract video ID from the video_url), (3) Show a sidebar with all lessons in the course, sorted by order_index, with a checkmark for completed lessons, (4) Show a progress bar at the top of the sidebar showing completion percentage, (5) Show a 'Mark as Complete' button that calls a server action to insert into lesson_completions, (6) Show 'Next Lesson' and 'Previous Lesson' navigation buttons, (7) Handle the case where the user is not enrolled in the course by redirecting them to the course catalog page.

Paste this in Bolt.new chat

app/lessons/[lessonId]/actions.ts
1// app/lessons/[lessonId]/actions.ts
2'use server';
3import { createClient } from '@/lib/supabase/server';
4import { revalidatePath } from 'next/cache';
5
6export async function markLessonComplete(lessonId: string, courseSlug: string) {
7 const supabase = await createClient();
8 const { data: { user } } = await supabase.auth.getUser();
9
10 if (!user) return { error: 'Not authenticated' };
11
12 const { error } = await supabase
13 .from('lesson_completions')
14 .insert({ user_id: user.id, lesson_id: lessonId });
15
16 if (error && error.code !== '23505') {
17 return { error: error.message };
18 }
19
20 revalidatePath(`/lessons/${lessonId}`);
21 return { success: true };
22}
23
24export async function getProgressForCourse(courseId: string, userId: string) {
25 const supabase = await createClient();
26
27 // Get total lesson count
28 const { count: total } = await supabase
29 .from('lessons')
30 .select('*', { count: 'exact', head: true })
31 .eq('course_id', courseId);
32
33 // Get completed count for this user
34 const { data: completions } = await supabase
35 .from('lesson_completions')
36 .select('lesson_id, lessons!inner(course_id)')
37 .eq('user_id', userId)
38 .eq('lessons.course_id', courseId);
39
40 const completed = completions?.length ?? 0;
41 const percentage = total ? Math.round((completed / total) * 100) : 0;
42
43 return { completed, total: total ?? 0, percentage };
44}

Pro tip: Use YouTube's privacy-enhanced embed URL (https://www.youtube-nocookie.com/embed/{videoId}) instead of the standard embed URL. The privacy-enhanced mode does not track viewers with YouTube cookies until they click play — a better experience for your learners.

Expected result: The lesson player shows the video embed, a sidebar with all course lessons and completion checkmarks, a progress bar, and a 'Mark as Complete' button. Clicking the button updates the checkmark in the sidebar instantly.

4

Set up the FutureLearn affiliate link path as an alternative

If building a full custom learning platform is more than your project requires, the FutureLearn affiliate program is a much simpler integration path. FutureLearn's affiliate program lets you link to their courses with a tracking parameter appended to the URL. When a user clicks your link and enrolls in a FutureLearn course within the attribution window, you earn a commission (typically 10-30% of the course fee). This approach is essentially outbound linking with revenue sharing — no API, no backend, no deployment required. To join the FutureLearn affiliate program, go to futurelearn.com/partnerships/affiliates and apply. FutureLearn uses an affiliate network (historically Impact or Awin) — after approval, you get access to a dashboard where you can generate affiliate links, create banners, and track your earnings. The affiliate link format is typically: https://www.futurelearn.com/courses/{course-slug}?utm_source={your_affiliate_id}&utm_medium=affiliate. For a Bolt-built course recommendation portal, store your curated course list in a Supabase table with columns for title, provider, description, image URL, affiliate URL, and metadata. The portal fetches this table and renders course cards. The 'View Course' or 'Enroll' button on each card opens the affiliate URL in a new tab. This is a pure content site — no authentication, no server-side API calls, no environment variables needed. It works completely in the Bolt WebContainer preview. Note a key limitation: since FutureLearn has no API, your course database must be maintained manually. You cannot automatically sync new courses, price changes, or availability. For a small curated catalog (20-50 courses), this is manageable. For a large-scale course aggregator, a platform with a real API (like Coursera or Udemy) is a better fit. For Bolt's WebContainer context: this affiliate approach generates zero WebContainer-related issues. There are no incoming webhooks, no API rate limits, no native modules, no CORS concerns. Outbound links work exactly the same in Bolt's preview as in production.

Bolt.new Prompt

Create a Supabase table called 'affiliate_courses' with columns: id uuid primary key default gen_random_uuid(), title text not null, provider text default 'FutureLearn', category text, level text, duration_weeks integer, description text, image_url text, affiliate_url text not null, featured boolean default false, created_at timestamptz default now(). Enable row level security with a policy allowing public read access (no authentication required). Then build a React page at app/recommended-courses/page.tsx that fetches all rows from this table and displays them as course cards with an affiliate link button. Add Tailwind CSS styling and a category filter. Seed the table with 4 example FutureLearn course entries.

Paste this in Bolt.new chat

app/recommended-courses/page.tsx
1// Example affiliate course data for seeding
2// Insert via Supabase Table Editor or SQL:
3/*
4INSERT INTO affiliate_courses (title, provider, category, level, duration_weeks, description, image_url, affiliate_url, featured)
5VALUES
6 (
7 'Introduction to Data Science',
8 'FutureLearn',
9 'Technology',
10 'Beginner',
11 4,
12 'Learn data science fundamentals with Python, covering data analysis, visualization, and machine learning basics.',
13 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=400',
14 'https://www.futurelearn.com/courses/data-science',
15 true
16 ),
17 (
18 'Digital Marketing Strategy',
19 'FutureLearn',
20 'Business',
21 'Intermediate',
22 6,
23 'Master digital marketing channels including SEO, social media, email marketing, and paid advertising.',
24 'https://images.unsplash.com/photo-1432888622747-4eb9a8efeb07?w=400',
25 'https://www.futurelearn.com/courses/digital-marketing',
26 true
27 );
28*/
29
30// app/recommended-courses/page.tsx
31import { createClient } from '@/lib/supabase/server';
32
33export default async function RecommendedCoursesPage() {
34 const supabase = await createClient();
35 const { data: courses } = await supabase
36 .from('affiliate_courses')
37 .select('*')
38 .order('featured', { ascending: false })
39 .order('created_at', { ascending: false });
40
41 return (
42 <div className="mx-auto max-w-6xl px-4 py-12">
43 <h1 className="mb-2 text-3xl font-bold">Recommended Courses</h1>
44 <p className="mb-8 text-gray-600">Curated courses from FutureLearn and partner institutions</p>
45 <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
46 {courses?.map((course) => (
47 <div key={course.id} className="rounded-xl border bg-white p-0 shadow-sm overflow-hidden">
48 {course.image_url && (
49 <img src={course.image_url} alt={course.title} className="h-48 w-full object-cover" />
50 )}
51 <div className="p-5">
52 <span className="text-xs font-medium uppercase text-blue-600">{course.category}</span>
53 <h2 className="mt-1 text-lg font-semibold">{course.title}</h2>
54 <p className="mt-2 text-sm text-gray-600 line-clamp-2">{course.description}</p>
55 <div className="mt-3 flex items-center gap-3 text-xs text-gray-500">
56 <span>{course.level}</span>
57 <span>·</span>
58 <span>{course.duration_weeks} weeks</span>
59 </div>
60 <a
61 href={course.affiliate_url}
62 target="_blank"
63 rel="noopener noreferrer"
64 className="mt-4 block w-full rounded-lg bg-blue-600 py-2 text-center text-sm font-medium text-white hover:bg-blue-700"
65 >
66 View on FutureLearn
67 </a>
68 </div>
69 </div>
70 ))}
71 </div>
72 </div>
73 );
74}

Pro tip: FutureLearn courses can be browsed at futurelearn.com/courses — use the filter options to find courses by subject, level, and duration. Copy the course URL, sign up for the affiliate program to get your tracking parameter, and append it to each course URL you store in your database.

Expected result: The recommended courses page shows a grid of FutureLearn course cards fetched from Supabase. Each card has a 'View on FutureLearn' button that opens the affiliate URL in a new tab. The entire page works in Bolt's WebContainer preview with no API credentials needed.

5

Use Coursera's API as the programmatic alternative to FutureLearn

For developers who specifically need programmatic access to a large course catalog — the kind of integration you would use FutureLearn for if they had an API — Coursera is the most practical alternative. Coursera provides a public REST API at api.coursera.org that returns course listings, partner information, and course metadata. The API is free to use for catalog data and requires no OAuth for basic course discovery queries. Coursera's catalog API follows a standard REST pattern. The courses endpoint is GET https://api.coursera.org/api/courses.v1 and accepts query parameters including q (query type: search or browse), query (search terms), fields (comma-separated list of fields to return), and limit/start for pagination. Fields available include name, slug, photoUrl, description, and partnerIds. This API is HTTP-based and works perfectly through a Bolt.new Next.js API route — outbound calls from the server side have no CORS issues. The API route pattern is straightforward: your React component calls your own /api/courses endpoint, which adds any required headers and calls api.coursera.org, then returns the cleaned data to the frontend. This proxy pattern serves two purposes: it keeps any API credentials server-side (though Coursera's catalog API does not require credentials for basic queries), and it lets you add caching, error handling, and response transformation in one place. Pagination is important for a good user experience. Coursera's API returns results with a paging object containing a next cursor. Implement infinite scroll or 'Load More' pagination in the React component using the cursor from the previous response. Store the cursor in component state and include it as a start parameter in the next API call. Note that Coursera's API is designed for course discovery, not for managing enrollments or accessing learner data — those features require Coursera's enterprise integration (Coursera for Campus or Coursera for Business). For a Bolt.new app building a learning resource aggregator, the catalog API is exactly the right level of access.

Bolt.new Prompt

Build a Coursera course discovery feature. Create a Next.js API route at app/api/coursera/search/route.ts that accepts a GET request with a 'query' search param, calls https://api.coursera.org/api/courses.v1?q=search&query={query}&fields=name,slug,photoUrl,description,partnerIds&limit=12, and returns the results. Then build a React page at app/discover/page.tsx with a search input and a grid of Coursera course cards. Each card shows the course photo, name, and description. Add a 'View Course' button linking to https://www.coursera.org/learn/{slug}. Show a loading spinner while searching. Handle empty results and API errors gracefully.

Paste this in Bolt.new chat

app/api/coursera/search/route.ts
1// app/api/coursera/search/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4interface CourseraCoursesResponse {
5 elements: Array<{
6 id: string;
7 name: string;
8 slug: string;
9 photoUrl?: string;
10 description?: string;
11 partnerIds?: string[];
12 }>;
13 paging?: { next?: string };
14}
15
16export async function GET(request: NextRequest) {
17 const { searchParams } = new URL(request.url);
18 const query = searchParams.get('query') ?? 'programming';
19 const start = searchParams.get('start') ?? '0';
20
21 try {
22 const url = new URL('https://api.coursera.org/api/courses.v1');
23 url.searchParams.set('q', 'search');
24 url.searchParams.set('query', query);
25 url.searchParams.set('fields', 'name,slug,photoUrl,description,partnerIds');
26 url.searchParams.set('limit', '12');
27 url.searchParams.set('start', start);
28
29 const response = await fetch(url.toString(), {
30 headers: {
31 'Accept': 'application/json',
32 },
33 next: { revalidate: 300 }, // Cache for 5 minutes
34 });
35
36 if (!response.ok) {
37 return NextResponse.json(
38 { error: `Coursera API error: ${response.status}` },
39 { status: response.status }
40 );
41 }
42
43 const data: CourseraCoursesResponse = await response.json();
44
45 return NextResponse.json({
46 courses: data.elements.map((course) => ({
47 id: course.id,
48 name: course.name,
49 slug: course.slug,
50 photoUrl: course.photoUrl,
51 description: course.description,
52 courseUrl: `https://www.coursera.org/learn/${course.slug}`,
53 })),
54 nextCursor: data.paging?.next,
55 });
56 } catch (error) {
57 const message = error instanceof Error ? error.message : 'Request failed';
58 return NextResponse.json({ error: message }, { status: 500 });
59 }
60}

Pro tip: Coursera's public catalog API has rate limits. Add next: { revalidate: 300 } to your fetch call (as shown in the code) to cache API responses for 5 minutes in Next.js. This reduces repeat API calls when multiple users search for the same query.

Expected result: The Coursera course discovery page shows a search input and a grid of course results fetched from Coursera's API. Typing a search query like 'machine learning' or 'web development' returns relevant courses from Coursera's catalog.

Common use cases

Course Recommendation Portal with FutureLearn Affiliate Links

A content website that curates online learning resources for a specific niche (e.g., data science, healthcare, sustainability) and links to relevant FutureLearn courses using affiliate URLs. The portal is built in Bolt.new with a Supabase database of manually curated course metadata, and earns commission when users click through and enroll on FutureLearn.

Bolt.new Prompt

Build a course recommendation portal for data science learners. Create a Supabase table called 'courses' with columns: id, title, provider, url, affiliate_url, category, duration_weeks, difficulty, description, image_url, rating. Seed it with 6 example FutureLearn courses. Build a Next.js page that displays courses as cards with title, provider badge, duration, difficulty badge, and a 'View Course' button that opens the affiliate_url. Add filter buttons for category and difficulty. Use Tailwind CSS and shadcn/ui components.

Copy this prompt to try it in Bolt.new

Custom Learning Platform with Supabase Backend

A full learning management system built in Bolt.new using Supabase: course catalog, video lessons via Supabase Storage or YouTube embeds, lesson completion tracking per user, quiz results, and downloadable certificates. This replicates FutureLearn-style functionality entirely within your own product, with full control over content and data.

Bolt.new Prompt

Build a learning platform with Supabase. Create these tables: courses (id, title, description, thumbnail_url, category, level), lessons (id, course_id, title, video_url, duration_minutes, order_index), user_progress (id, user_id, lesson_id, completed_at), enrollments (id, user_id, course_id, enrolled_at). Build a course catalog page showing enrolled and available courses. Build a lesson player page with a YouTube embed, a 'Mark Complete' button that inserts into user_progress, and a progress bar showing completion percentage. Use Supabase Auth for user sessions. Use Tailwind CSS.

Copy this prompt to try it in Bolt.new

Tech Education Dashboard Using Coursera API

For developers who need programmatic access to an online course catalog, Coursera's catalog API provides public access to course listings, descriptions, and partner information. This Bolt.new app fetches course data from Coursera's API and displays it in a searchable dashboard — achieves what a FutureLearn API would provide, using Coursera's documented endpoints.

Bolt.new Prompt

Build a course discovery dashboard that fetches programming courses from Coursera's API. Create a Next.js API route at /api/courses that calls https://api.coursera.org/api/courses.v1?q=search&query=javascript&fields=name,slug,photoUrl,description,partnerIds and returns the results. In the frontend, show a search input and display results as cards with course name, photo, and a link to the Coursera course page. Add a loading skeleton. Handle API errors with a user-friendly message. Use Tailwind CSS.

Copy this prompt to try it in Bolt.new

Troubleshooting

Supabase query returns empty array even though courses were inserted in the Table Editor

Cause: The courses table has a published column with a default of false, and the query filters for WHERE published = true. Newly inserted courses have published = false unless explicitly set.

Solution: In Supabase Table Editor, edit the course rows you inserted and set published = true. Alternatively, update your Supabase query during development to remove the published filter: .select('*') without .eq('published', true). Add the filter back when you are ready for production.

typescript
1// Development: no filter
2const { data: courses } = await supabase.from('courses').select('*');
3
4// Production: only show published courses
5const { data: courses } = await supabase
6 .from('courses')
7 .select('*')
8 .eq('published', true);

Coursera API returns a 404 or the fetch call fails with a CORS error in the browser console

Cause: The Coursera API call is being made from the client-side React component directly, not through the Next.js API route. Direct browser-to-Coursera API calls may fail due to CORS restrictions. Alternatively, the API route path may be incorrect.

Solution: Ensure the Coursera API call happens inside the Next.js API route at app/api/coursera/search/route.ts, not in the React component. The React component should call /api/coursera/search?query=javascript, which then proxies to Coursera. Never call api.coursera.org directly from browser-side React code.

typescript
1// WRONG: Direct call from React component
2const data = await fetch('https://api.coursera.org/api/courses.v1?...');
3
4// CORRECT: Call your own API route proxy
5const data = await fetch(`/api/coursera/search?query=${encodeURIComponent(searchQuery)}`);

The YouTube video embed shows 'Video unavailable' or a blank iframe in the lesson player

Cause: The video_url stored in Supabase is either the full watch URL (https://www.youtube.com/watch?v=VIDEO_ID) rather than the embed URL, the video ID is missing, or the video has embedding disabled by the YouTube channel owner.

Solution: Extract the video ID from the full YouTube URL and construct the embed URL. The embed format is https://www.youtube.com/embed/{videoId}. Parse the video ID from watch URLs (v= parameter) or short URLs (youtu.be/{id}). Test with a video you control or know has embedding enabled — not all YouTube videos allow embedding on external sites.

typescript
1// Extract YouTube video ID from any YouTube URL format
2function getYouTubeEmbedUrl(url: string): string {
3 const patterns = [
4 /youtube\.com\/watch\?v=([^&]+)/,
5 /youtu\.be\/([^?]+)/,
6 /youtube\.com\/embed\/([^?]+)/,
7 ];
8 for (const pattern of patterns) {
9 const match = url.match(pattern);
10 if (match) return `https://www.youtube-nocookie.com/embed/${match[1]}`;
11 }
12 return url; // Return as-is if already an embed URL
13}

The affiliate link tracking is not working — FutureLearn's affiliate dashboard shows zero clicks

Cause: The affiliate tracking parameter is missing from the course URL, the affiliate network tracking parameter format has changed, or users are using browser extensions that strip tracking parameters from URLs.

Solution: Verify your affiliate link format by clicking it yourself and checking that the affiliate network registers the click in their dashboard. Affiliate network link formats vary: Impact uses impact.com tracking links, Awin uses aw.ncsbn.com redirects. Use the link builder in your affiliate network's dashboard rather than manually appending parameters. Some ad blockers strip tracking parameters — test in an incognito browser without extensions.

Best practices

  • Be transparent with your users: if you are building a course recommendation portal with affiliate links, make it clear that you may earn a commission when they enroll — this builds trust and is required by FTC guidelines
  • Store your curated course metadata in Supabase rather than hardcoding it in React — this allows you to add, edit, or remove courses without redeploying your Bolt app
  • For the custom learning platform path, always enable Row Level Security on all Supabase tables and write explicit policies — without RLS, all course data and user progress is publicly accessible
  • Use Supabase Storage for course videos only if your content is private or proprietary — for public educational content, YouTube embeds are free, globally cached, and more reliable than self-hosted video
  • Cache Coursera API responses in your Next.js API route using the fetch next: { revalidate: N } option — repeated identical searches will return cached results without hitting Coursera's rate limits
  • Keep the lesson_completions and enrollments tables indexed on (user_id, course_id) for fast progress queries — as your platform grows, unindexed queries on these tables will slow down
  • For the affiliate path, manually review and update your course list quarterly — FutureLearn courses change prices, go on hiatus, or get retired, and dead links hurt user experience and SEO

Alternatives

Frequently asked questions

Does FutureLearn have a public API that works with Bolt.new?

No. FutureLearn does not offer a public REST API for third-party developers. There is no developer portal, no OAuth application registration, and no documented endpoints for accessing course data, enrollment status, or learner progress. If you need programmatic access to online course data, Coursera's catalog API is the closest alternative that works with Bolt.new's API route pattern.

How do I add FutureLearn course links to my Bolt.new app?

Join FutureLearn's affiliate program at futurelearn.com/partnerships/affiliates to get tracking links for each course. Store the course metadata (title, description, image, affiliate URL) in a Supabase table and build a course recommendation page in Bolt that links out to FutureLearn. The entire implementation is outbound links — no API key, no backend API calls, and it works in Bolt's WebContainer preview immediately.

Can I build something similar to FutureLearn inside Bolt.new?

Yes — Bolt.new with Supabase is well-suited for building a custom learning platform. Supabase provides the database for course catalogs, user accounts, and progress tracking. For video hosting, use YouTube embeds (free) or Supabase Storage (for private content). Certificate generation can be done with a PDF library like pdf-lib or jsPDF. The schema in this guide covers the core data model for courses, lessons, enrollments, and progress tracking.

Which online learning platform has the best API for use with Bolt.new?

Coursera has the most accessible public API for course discovery — free to use, no authentication required for catalog queries, and HTTP-based so it works through Bolt's Next.js API route pattern. Teachable and Thinkific have REST APIs for course creators who want to manage their own course content programmatically. Udemy has an affiliate API. FutureLearn, Udacity, and LinkedIn Learning (Lynda) do not have public APIs available for independent developers.

Does building a custom learning platform in Bolt.new require deployment to work?

The course catalog, lesson player, and progress tracking features work in Bolt's WebContainer preview since they use Supabase (HTTP-based) for the backend. Authentication with Supabase Auth also works in preview for email/password flows. The main features that require deployment are OAuth social login (Google, GitHub) — these need a stable redirect URI that the WebContainer's dynamic URL cannot provide. Deploy to Netlify or Bolt Cloud to enable social login.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.