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

How to Integrate Bolt.new with LinkedIn Learning

LinkedIn Learning (formerly Lynda) has no public content API available to developers. The honest answer: you cannot embed courses or query course content programmatically without an enterprise LTI agreement with LinkedIn. Your best options are embedding course links in a learning portal, using LinkedIn Marketing API if your goal is LinkedIn Ads, or building a course recommendation engine with a Supabase backend that stores curated course metadata.

What you'll learn

  • Why LinkedIn Learning has no public API and what alternatives exist for developers
  • How to build a course recommendation engine using Supabase to store curated course data
  • How to create Next.js API routes that query and filter course recommendations
  • How to embed LinkedIn Learning course links with proper deep-link URL patterns
  • How the LinkedIn Marketing API works if your goal is LinkedIn advertising (not learning content)
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate17 min read30 minutesOtherApril 2026RapidDev Engineering Team
TL;DR

LinkedIn Learning (formerly Lynda) has no public content API available to developers. The honest answer: you cannot embed courses or query course content programmatically without an enterprise LTI agreement with LinkedIn. Your best options are embedding course links in a learning portal, using LinkedIn Marketing API if your goal is LinkedIn Ads, or building a course recommendation engine with a Supabase backend that stores curated course metadata.

The Reality of LinkedIn Learning Integration and What You Can Build Instead

If you searched for 'LinkedIn Learning API' or 'Lynda API' expecting to find a way to pull course catalogs, track completions, or embed video players, you have encountered one of the most searched-for APIs that does not exist for general developers. LinkedIn Learning shut down its public API program years ago and has never replaced it. The only programmatic access is through enterprise LTI (Learning Tools Interoperability) integrations, which require a paid LinkedIn Learning enterprise license, a formal agreement with LinkedIn's enterprise sales team, and IT infrastructure to implement the LTI standard. This is not accessible to individual developers or small startups.

The disconnect in the 'lynda linkedin learning' search query is revealing: many users searching for this integration actually want one of three different things. First, some want to embed LinkedIn Learning content in their own learning portal — this is not technically possible without enterprise LTI access. Second, some want to build a curated course recommendation tool that links to LinkedIn Learning courses — this is absolutely achievable and is the most practical approach. Third, some are actually looking for the LinkedIn Marketing API to run LinkedIn Ads campaigns in their apps, which is a completely separate product with its own API and developer program.

This tutorial takes the most useful path for Bolt developers: building a course recommendation engine where you manually curate course data (or import it from LinkedIn Learning's UI) into a Supabase database, then surface it through your app. This gives you full control over the data, enables custom filtering and personalization, and works completely within Bolt's architecture. If your goal is LinkedIn Ads integration, the tutorial covers the LinkedIn Marketing API setup as a separate section.

Integration method

Bolt Chat + API Route

LinkedIn Learning does not offer a public REST API for course content. Integration options are: (1) enterprise LTI integration requiring a paid LinkedIn Learning license and IT approval, (2) building a curated course link portal in Bolt using a Supabase database to store course metadata you collect manually, or (3) using the LinkedIn Marketing API if your actual need is LinkedIn advertising. This tutorial covers the realistic path for Bolt developers: building a course recommendation engine with manually curated data.

Prerequisites

  • A Supabase account and project (free tier is sufficient) for storing course data
  • LinkedIn Learning enterprise license if pursuing LTI integration (requires LinkedIn enterprise sales contact)
  • LinkedIn developer application credentials if pursuing Marketing API integration (at developer.linkedin.com)
  • A Bolt.new project using Next.js for API routes and Supabase client
  • A list of LinkedIn Learning course URLs to seed your course database initially

Step-by-step guide

1

Understand the API Limitations and Choose Your Path

Before writing any code, be clear about what LinkedIn Learning's API access actually looks like. LinkedIn Learning offers two types of API access, and neither is a simple public REST API you can call with an API key. The first option is LTI (Learning Tools Interoperability), an education industry standard for connecting learning platforms to each other. LinkedIn Learning supports LTI 1.0 and 1.3. With LTI, your learning management system can launch LinkedIn Learning content, pass user identity, and receive completion events. The catch: LTI integration requires a paid LinkedIn Learning enterprise or Teams license (starting around $30/user/month), a formal request to LinkedIn's enterprise support team to enable LTI for your account, and technical implementation of the LTI protocol. This is primarily used by large companies integrating LinkedIn Learning into existing LMS platforms like Workday or Cornerstone. The second option is using LinkedIn's developer platform at developer.linkedin.com, but the content API is not there. What IS there is the LinkedIn Marketing API (for ads), Sign In with LinkedIn (OAuth), and the LinkedIn Pages API (for company pages). None of these give you access to Learning course content. For 99% of Bolt developers, the practical choice is: build a curated course recommendation system using a database you control (Supabase works great), store course links and metadata manually, and link users out to LinkedIn Learning to watch the content. This is actually what many 'learning platforms' do — they curate external content rather than host it themselves.

typescript
1// LinkedIn Learning URL patterns for deep links:
2// Course page: https://www.linkedin.com/learning/[course-slug]
3// Specific video: https://www.linkedin.com/learning/[course-slug]/[video-slug]
4// Search: https://www.linkedin.com/learning/search?keywords=[query]
5// Category: https://www.linkedin.com/learning/topics/[topic-slug]
6
7// These URLs work for any LinkedIn Learning subscriber.
8// Course slugs are visible in the LinkedIn Learning URL when browsing.

Pro tip: You can find LinkedIn Learning course slugs by browsing to a course on LinkedIn Learning and copying the URL. The slug is the last segment: linkedin.com/learning/react-js-essential-training → slug is 'react-js-essential-training'.

Expected result: You have decided on your integration path: (A) curated course database in Supabase (most practical), (B) LTI enterprise integration (requires LinkedIn enterprise license), or (C) LinkedIn Marketing API (for ads, not learning).

2

Set Up Supabase Course Database

For the curated learning portal approach, Supabase is the ideal backend. Create a courses table that stores all the metadata you want to display and filter on. The key fields are: the LinkedIn Learning course URL (the link users click through to watch), title and description (copied from the LinkedIn Learning course page), duration in minutes, skill tags as an array for multi-value filtering, difficulty level, and category. You will also want a user_enrollments or user_completions table to track which users have been assigned or completed which courses. With Supabase Row Level Security (RLS), you can restrict which users can add courses (admin-only) versus who can view them (all authenticated users) and who can mark completions (only their own records). Seeding the database: this is the manual part. Browse LinkedIn Learning for courses relevant to your audience, and for each one, copy the title, URL, duration (shown on the course page), and choose skill tags. You can also use the LinkedIn Learning API in the browser's DevTools network tab — when you load a course page on LinkedIn Learning, the page makes internal API calls that return structured course data you can capture and import, though this is an unofficial workaround and may break.

Bolt.new Prompt

Set up a Supabase database for a LinkedIn Learning course portal. Create a migration that creates: (1) a 'courses' table with id (uuid, primary key), title (text), description (text), url (text, the LinkedIn Learning link), thumbnail_url (text), duration_minutes (integer), skill_tags (text array), difficulty (text, check constraint: beginner/intermediate/advanced), category (text), is_active (boolean, default true), created_at (timestamptz). (2) a 'user_enrollments' table with id, user_id (references auth.users), course_id (references courses), status (text: assigned/in_progress/completed), enrolled_at, completed_at. Add RLS policies: anyone authenticated can read active courses, only service role can insert courses, users can read/update their own enrollments.

Paste this in Bolt.new chat

supabase/migrations/create_learning_portal.sql
1-- Supabase migration: create_learning_portal_tables
2CREATE TABLE courses (
3 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
4 title TEXT NOT NULL,
5 description TEXT,
6 url TEXT NOT NULL,
7 thumbnail_url TEXT,
8 duration_minutes INTEGER,
9 skill_tags TEXT[] DEFAULT '{}',
10 difficulty TEXT CHECK (difficulty IN ('beginner', 'intermediate', 'advanced')),
11 category TEXT,
12 is_active BOOLEAN DEFAULT true,
13 created_at TIMESTAMPTZ DEFAULT now()
14);
15
16CREATE TABLE user_enrollments (
17 id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
18 user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
19 course_id UUID REFERENCES courses(id) ON DELETE CASCADE,
20 status TEXT CHECK (status IN ('assigned', 'in_progress', 'completed')) DEFAULT 'assigned',
21 enrolled_at TIMESTAMPTZ DEFAULT now(),
22 completed_at TIMESTAMPTZ,
23 UNIQUE(user_id, course_id)
24);
25
26ALTER TABLE courses ENABLE ROW LEVEL SECURITY;
27ALTER TABLE user_enrollments ENABLE ROW LEVEL SECURITY;
28
29CREATE POLICY "Authenticated users can view active courses"
30 ON courses FOR SELECT TO authenticated
31 USING (is_active = true);
32
33CREATE POLICY "Users manage own enrollments"
34 ON user_enrollments FOR ALL TO authenticated
35 USING (auth.uid() = user_id);

Expected result: Supabase has the courses and user_enrollments tables with RLS policies applied. You can insert course data directly in the Supabase Table Editor to seed initial content.

3

Build Course API Routes with Search and Filtering

The API routes for the learning portal need to support the filtering patterns users expect: searching by keyword, filtering by category and difficulty, filtering by skill tag, and fetching a user's enrolled courses with completion status. Supabase's JavaScript client makes query building clean with chainable filter methods. For keyword search across course titles and descriptions, use Supabase's full-text search (textsearch) or the ILIKE operator for simpler pattern matching. Full-text search gives better relevance ranking for a large catalog; ILIKE is simpler to implement and works well for catalogs under a few hundred courses. For skill tag filtering, PostgreSQL's @> (array contains) operator checks if the skill_tags array includes all of the requested tags — useful when users filter by multiple skills at once. The enrollment status endpoint is separate from the course listing — it joins courses with the current user's enrollment records to show status (not enrolled, in progress, completed) on each course card. This query can be made in a single Supabase call using a foreign table join.

Bolt.new Prompt

Create Next.js API routes for the LinkedIn Learning portal. /api/learning/courses (GET): accept query params: search (keyword), category, difficulty, skill (single skill tag), page (default 1), limit (default 20). Query the Supabase courses table with these filters applied. Return courses array and total count. /api/learning/courses/[id] (GET): return single course by ID. /api/learning/enrollments (GET): return current user's enrolled courses with status. /api/learning/enrollments (POST): enroll user in a course (insert to user_enrollments). /api/learning/enrollments/[id] (PATCH): update enrollment status to 'in_progress' or 'completed'.

Paste this in Bolt.new chat

app/api/learning/courses/route.ts
1// app/api/learning/courses/route.ts
2import { createClient } from '@supabase/supabase-js';
3import { NextResponse } from 'next/server';
4
5const supabase = createClient(
6 process.env.NEXT_PUBLIC_SUPABASE_URL!,
7 process.env.SUPABASE_SERVICE_ROLE_KEY!
8);
9
10export async function GET(request: Request) {
11 const { searchParams } = new URL(request.url);
12 const search = searchParams.get('search');
13 const category = searchParams.get('category');
14 const difficulty = searchParams.get('difficulty');
15 const skill = searchParams.get('skill');
16 const page = parseInt(searchParams.get('page') || '1');
17 const limit = parseInt(searchParams.get('limit') || '20');
18
19 let query = supabase
20 .from('courses')
21 .select('*', { count: 'exact' })
22 .eq('is_active', true)
23 .range((page - 1) * limit, page * limit - 1);
24
25 if (search) query = query.ilike('title', `%${search}%`);
26 if (category) query = query.eq('category', category);
27 if (difficulty) query = query.eq('difficulty', difficulty);
28 if (skill) query = query.contains('skill_tags', [skill]);
29
30 const { data, count, error } = await query;
31 if (error) return NextResponse.json({ error: error.message }, { status: 500 });
32
33 return NextResponse.json({ courses: data, total: count, page, limit });
34}

Pro tip: Use the Supabase service role key (SUPABASE_SERVICE_ROLE_KEY, no NEXT_PUBLIC_ prefix) in API routes to bypass RLS for admin operations like inserting courses. For user-facing reads, use the anon key instead.

Expected result: GET /api/learning/courses returns a paginated list of courses. Search and filter params narrow the results. Enrollment endpoints let users track their learning progress.

4

Build the Course Portal UI

The frontend for the LinkedIn Learning portal combines a searchable, filterable course grid with enrollment tracking. The key UX pattern is course cards that show a thumbnail (use LinkedIn Learning's default course thumbnails or a colored gradient fallback), title, duration, difficulty badge, and skill tags. An 'Enroll' button marks the user as enrolled and a 'Mark Complete' button updates the status. Since clicking a course takes users to LinkedIn Learning (a different website), open course links in a new tab. This is important because users will come back to your portal after watching — they won't navigate back from LinkedIn Learning. Track the click as an implicit 'in_progress' enrollment status update: when a user clicks through to LinkedIn Learning, automatically update their enrollment status from 'assigned' to 'in_progress' if they were enrolled. For the admin side (adding and editing courses), build a simple form that lets admins paste in a LinkedIn Learning URL and the rest of the fields. You could pre-fill some metadata by fetching the LinkedIn Learning course page and scraping the Open Graph tags (og:title, og:description, og:image) — a proxy API route can fetch the HTML and extract the og tags server-side, which is acceptable for one-time course setup and avoids CORS issues.

Bolt.new Prompt

Build the LinkedIn Learning portal UI at /learning. Show a grid of course cards with: thumbnail (or gradient placeholder if none), title, duration, difficulty badge, category label, and skill tags as chips. Add a search input and dropdowns for category and difficulty filtering. Show enrollment status on each card (Not Enrolled / In Progress / Completed) with matching action buttons. Admin users see an 'Add Course' button that opens a form. Fetch courses from /api/learning/courses with debounced search. Update enrollment status via /api/learning/enrollments endpoints.

Paste this in Bolt.new chat

Pro tip: LinkedIn Learning course thumbnails follow a predictable pattern you can use to display course images. When adding a course, store the thumbnail URL from the LinkedIn Learning page's og:image meta tag — it's typically https://media.licdn.com/dms/image/... and loads reliably.

Expected result: The learning portal shows a grid of curated LinkedIn Learning courses with search, filtering, and enrollment tracking. Course thumbnails display, and clicking a course opens the LinkedIn Learning page in a new tab.

5

LinkedIn Marketing API Setup (Alternative Path for Ads Integration)

If your use case is actually LinkedIn advertising — managing campaigns, pulling performance metrics, or targeting LinkedIn audiences — the LinkedIn Marketing API is a separate, legitimate API that works well from Bolt.new. This is completely different from LinkedIn Learning and is the likely intent behind some 'LinkedIn Learning API' searches (the GSC query mismatch mentioned in the page brief). To use the LinkedIn Marketing API: go to developer.linkedin.com, create an application, and request access to the Marketing Developer Platform (requires approval, typically 1-3 business days for established companies). The API uses OAuth 2.0 for authentication — users authorize your app to access their LinkedIn Ads account. Once authorized, you get an access token you can use to call the Marketing API endpoints. Key endpoints include: adAccountsV2 (list ad accounts), adCampaignsV2 (create and manage campaigns), adAnalytics (performance metrics: impressions, clicks, spend, conversions), and audienceInsights (demographic data). All endpoints are HTTPS REST APIs that work from Bolt's WebContainer. Store the access token in your .env file during development; use your hosting platform's environment variables in production. Note: the LinkedIn Marketing API has strict rate limits (100 calls per day for most endpoints on the free tier) and access is restricted to approved partners.

Bolt.new Prompt

Create an API route at /api/linkedin/ad-performance that reads LINKEDIN_ACCESS_TOKEN from environment variables and fetches campaign performance data from the LinkedIn Marketing API v2. Call GET https://api.linkedin.com/v2/adAnalyticsV2 with query params: accounts=urn:li:sponsoredAccount:{ACCOUNT_ID}, dateRange.start.year, dateRange.start.month, dateRange.start.day, dateRange.end.year, dateRange.end.month, dateRange.end.day, timeGranularity=MONTHLY, fields=dateRange,costInLocalCurrency,impressions,clicks,conversions. Return the analytics data. Add a dashboard page that shows total spend, impressions, clicks, and CTR for the last 30 days.

Paste this in Bolt.new chat

app/api/linkedin/ad-performance/route.ts
1// app/api/linkedin/ad-performance/route.ts
2import { NextResponse } from 'next/server';
3
4export async function GET() {
5 const accessToken = process.env.LINKEDIN_ACCESS_TOKEN;
6 const accountId = process.env.LINKEDIN_AD_ACCOUNT_ID;
7
8 if (!accessToken || !accountId) {
9 return NextResponse.json(
10 { error: 'LINKEDIN_ACCESS_TOKEN and LINKEDIN_AD_ACCOUNT_ID required' },
11 { status: 400 }
12 );
13 }
14
15 const today = new Date();
16 const thirtyDaysAgo = new Date(today.getTime() - 30 * 86400000);
17
18 const params = new URLSearchParams({
19 'accounts[0]': `urn:li:sponsoredAccount:${accountId}`,
20 'dateRange.start.year': thirtyDaysAgo.getFullYear().toString(),
21 'dateRange.start.month': (thirtyDaysAgo.getMonth() + 1).toString(),
22 'dateRange.start.day': thirtyDaysAgo.getDate().toString(),
23 'dateRange.end.year': today.getFullYear().toString(),
24 'dateRange.end.month': (today.getMonth() + 1).toString(),
25 'dateRange.end.day': today.getDate().toString(),
26 timeGranularity: 'MONTHLY',
27 fields: 'dateRange,costInLocalCurrency,impressions,clicks',
28 });
29
30 const response = await fetch(
31 `https://api.linkedin.com/v2/adAnalyticsV2?${params}`,
32 { headers: { Authorization: `Bearer ${accessToken}`, 'LinkedIn-Version': '202403' } }
33 );
34
35 if (!response.ok) {
36 return NextResponse.json({ error: 'LinkedIn API error', status: response.status }, { status: 502 });
37 }
38
39 return NextResponse.json(await response.json());
40}

Pro tip: LinkedIn API access tokens expire after 60 days. Implement a refresh flow using the OAuth 2.0 refresh token, or set up a reminder to manually refresh the token in your .env before it expires.

Expected result: If you are building a LinkedIn Ads dashboard, this API route returns campaign performance metrics. If you are building a LinkedIn Learning portal, skip this step and focus on the Supabase course database approach from the previous steps.

Common use cases

Employee Learning Portal with Curated Courses

Build an internal learning portal for your team that curates LinkedIn Learning courses by skill area or role. Store course metadata in Supabase (title, URL, duration, skill tags, difficulty level) and build a searchable interface. Employees click through to watch on LinkedIn Learning with their company license. You control what courses are recommended and can track which ones your team has been assigned.

Bolt.new Prompt

Build an employee learning portal. Create a Supabase table called 'courses' with columns: id, title, description, url (LinkedIn Learning link), duration_minutes, skill_tags (array), difficulty (beginner/intermediate/advanced), category, added_by, created_at. Create API routes at /api/courses (GET with search/filter params) and /api/courses (POST for adding new courses). Build a UI with a searchable course grid, filter by category and difficulty, and a 'Add Course' form for admins. Use NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY.

Copy this prompt to try it in Bolt.new

Skill Gap Learning Tracker

Create a tool where users self-assess their skills and get recommended LinkedIn Learning courses to fill the gaps. Users select skills they want to develop from a predefined list, and the app queries a curated course database to surface relevant recommendations. Track which courses users have completed by letting them mark courses as done — stored in Supabase.

Bolt.new Prompt

Build a skill gap tracker. Users select from a list of 20 skills they want to improve (React, Python, project management, etc.). Query a Supabase 'courses' table filtered by skill_tags matching the selected skills. Show recommended courses sorted by difficulty (beginner first). Add a 'Mark Complete' button that inserts a row into a 'user_completions' table with user_id, course_id, completed_at. Show a progress summary: X of Y recommended courses completed.

Copy this prompt to try it in Bolt.new

LinkedIn Ads Campaign Manager (Marketing API)

If your actual goal is managing LinkedIn advertising campaigns programmatically, the LinkedIn Marketing API is the correct product. It lets you create, update, and analyze ad campaigns, manage ad accounts, pull performance metrics, and manage audiences — all via a proper REST API with OAuth 2.0. This is completely separate from LinkedIn Learning.

Bolt.new Prompt

I need to integrate with the LinkedIn Marketing API to show ad campaign performance in my dashboard. Create an API route at /api/linkedin/campaigns that exchanges a LinkedIn OAuth token for campaign data from the LinkedIn Marketing API v2 (https://api.linkedin.com/v2/adCampaignsV2). Read LINKEDIN_ACCESS_TOKEN from environment variables. Return campaigns with name, status, spend, impressions, and clicks. Display the data in a metrics table with sparkline charts.

Copy this prompt to try it in Bolt.new

Troubleshooting

Cannot find any LinkedIn Learning API documentation for developers

Cause: LinkedIn Learning shut down its public developer API and never reopened it. The only remaining access is enterprise LTI, which is not documented in LinkedIn's public developer portal.

Solution: Accept that a public LinkedIn Learning API does not exist. For a learning portal, use the Supabase database approach to store curated course metadata. For LinkedIn advertising, use the LinkedIn Marketing API at developer.linkedin.com, which is a fully documented and accessible API.

LinkedIn Marketing API returns 403 or 'ACCESS_DENIED' error

Cause: LinkedIn Marketing API access requires application approval. Creating a LinkedIn developer app does not automatically grant Marketing API access — you must request it through the partner program and wait for approval.

Solution: Go to developer.linkedin.com, select your app, and click 'Request Access' under the Marketing Developer Platform product. Fill in the access request form explaining your use case. Approval typically takes 1-3 business days. Until approved, your app can only call basic profile APIs.

Course thumbnails fail to load, showing broken image icons

Cause: LinkedIn Learning course thumbnail URLs (from og:image tags) may be served with Referer checking or CDN policies that block direct hotlinking from other domains.

Solution: Add a fallback image or gradient placeholder for all course cards. In your course database, store a local copy of the thumbnail URL from when the course was added, and implement an onerror handler on the img element that switches to the placeholder when the LinkedIn CDN blocks the load.

typescript
1// Add to course card component:
2<img
3 src={course.thumbnail_url}
4 alt={course.title}
5 onError={(e) => {
6 e.currentTarget.src = '/course-placeholder.png';
7 }}
8/>

Supabase query with skill_tags filter returns no results

Cause: The contains() operator in Supabase requires the skill_tags column to be a proper PostgreSQL array (text[]), and the filter value must be an array too. Passing a string instead of an array to the contains filter fails silently.

Solution: Ensure skill_tags is defined as text[] in your schema. Pass skill filter values as arrays: .contains('skill_tags', [skill]) with square brackets around the value. Check that stored tags use consistent casing — 'React' and 'react' are different array elements in PostgreSQL.

typescript
1// Correct Supabase array contains filter:
2if (skill) query = query.contains('skill_tags', [skill.toLowerCase()]);
3// When inserting, normalize tags:
4const normalizedTags = tags.map(t => t.toLowerCase().trim());

Best practices

  • Be honest with users that course content is hosted on LinkedIn Learning — clearly label all links as 'Watch on LinkedIn Learning' and open them in new tabs so users return to your portal
  • Normalize skill tags to lowercase when storing and filtering to prevent duplicate tags like 'React' and 'react' splitting your course catalog
  • Store thumbnail URLs at insertion time rather than fetching them dynamically — LinkedIn CDN URLs can change and hotlinking may be blocked
  • Add completion tracking immediately, even if just a checkbox — users find value in marking progress and it gives you engagement data
  • Build the course admin interface early so non-technical team members can add and manage the course catalog without touching the database directly
  • Index the skill_tags column with a GIN index in PostgreSQL for fast array contains queries as your catalog grows: CREATE INDEX idx_courses_skill_tags ON courses USING GIN(skill_tags)
  • Include a 'last verified' timestamp on course records since LinkedIn Learning occasionally retires or moves courses — a course that 404s damages user trust

Alternatives

Frequently asked questions

Is there a LinkedIn Learning REST API developers can use?

No public REST API exists for LinkedIn Learning course content. LinkedIn shut down its developer API access years ago. The only integration path is enterprise LTI, which requires a paid LinkedIn Learning enterprise license and a formal agreement with LinkedIn's sales team. For general developers, there is no API key or OAuth flow you can use to access course catalogs or completion data.

Can I embed LinkedIn Learning videos in my Bolt app?

Not without an enterprise LTI agreement. LinkedIn Learning videos are behind authentication and cannot be embedded in iframes on external sites — LinkedIn enforces X-Frame-Options headers that prevent iframe embedding. You can only link users to LinkedIn Learning where they watch content on LinkedIn's own platform, or use the enterprise LTI integration which opens courses in a LinkedIn-hosted frame within your LMS.

Does the LinkedIn Marketing API work from Bolt's WebContainer?

Yes. The LinkedIn Marketing API communicates over HTTPS and works from Bolt's Next.js API routes during development. Store your access token in the .env file and call the API from server-side routes only — never expose access tokens in client-side code with NEXT_PUBLIC_ prefixes. The only limitation is that LinkedIn OAuth flows require a deployed redirect URI, so complete the initial OAuth authorization with your deployed app URL.

How do I track whether a user has completed a LinkedIn Learning course?

You cannot receive completion events from LinkedIn Learning programmatically without an enterprise LTI integration. For most apps, the practical approach is self-reported completion: the user clicks 'Mark as Complete' in your app after they finish watching on LinkedIn Learning. Store this in your database. It relies on user honesty but is sufficient for most internal learning portals and requires no LinkedIn API access.

What is LTI and do I need it for LinkedIn Learning integration?

LTI (Learning Tools Interoperability) is an education industry standard for connecting learning platforms. With LTI, LinkedIn Learning can launch content within your LMS and send back completion events. You need it only if you require automated completion tracking or single sign-on launch. LTI requires a paid LinkedIn Learning enterprise or Teams plan and IT setup — it is not available for individual developers or apps without an enterprise agreement.

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.