Skip to content

Instantly share code, notes, and snippets.

@MaxDonchenko
Created January 15, 2026 00:12
Show Gist options
  • Select an option

  • Save MaxDonchenko/6d5ba4efb8bfa1c47f429562f5386c19 to your computer and use it in GitHub Desktop.

Select an option

Save MaxDonchenko/6d5ba4efb8bfa1c47f429562f5386c19 to your computer and use it in GitHub Desktop.
script to export all data from the Kajabi platform (works only for Pro plan introduced in 2026)
import axios, { AxiosInstance } from 'axios';
import * as fs from 'fs-extra';
import * as path from 'path';
const API_BASE_URL = 'https://api.kajabi.com';
const PAGE_SIZE = 50;
// API credentials - replace with your actual credentials
// API Key from Kajabi settings -> API Key
const CLIENT_ID = 'YOUR_CLIENT_ID_HERE';
// API Secret from Kajabi settings -> API Secret
const CLIENT_SECRET = 'YOUR_CLIENT_SECRET_HERE';
// Alternative: If API Key/Secret don't work, try username/password
const USERNAME: string = ''; // Your Kajabi email/username
const PASSWORD: string = ``; // Your Kajabi password
interface TokenResponse {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: number;
}
interface PaginatedResponse<T> {
data: T[];
links?: {
self?: string;
first?: string;
prev?: string;
next?: string;
last?: string;
};
meta?: {
total_pages?: number;
total_count?: number;
current_page?: number;
};
}
let apiClient: AxiosInstance;
let accessToken: string;
async function authenticate(): Promise<void> {
// Try client credentials first (API Key/Secret)
const hasClientCredentials = CLIENT_ID && CLIENT_SECRET;
if (hasClientCredentials) {
try {
const clientId = CLIENT_ID.trim();
const clientSecret = CLIENT_SECRET.trim();
// Debug info (without exposing full secret)
console.log(`Attempting authentication...`);
console.log(`API Key (client_id): ${clientId.substring(0, 3)}...${clientId.substring(clientId.length - 3)}`);
console.log(`API Secret length: ${clientSecret.length} characters`);
// Try with form-encoded body (standard OAuth2)
const params = new URLSearchParams();
params.append('client_id', clientId);
params.append('client_secret', clientSecret);
params.append('grant_type', 'client_credentials');
const response = await axios.post<TokenResponse>(
`${API_BASE_URL}/v1/oauth/token`,
params,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
}
);
accessToken = response.data.access_token;
apiClient = axios.create({
baseURL: API_BASE_URL,
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: 'application/vnd.api+json',
},
});
console.log('✅ Authenticated successfully with API Key/Secret');
return;
} catch (error: any) {
if (error.response?.status === 401) {
const errorMsg = error.response?.data?.error || 'Invalid credentials';
console.log(`❌ API Key/Secret authentication failed: ${errorMsg}`);
console.log(`\n💡 Troubleshooting:`);
console.log(` 1. Verify your API Key and Secret from: https://app.kajabi.com/admin/settings/security`);
console.log(` 2. Make sure there are no extra spaces or characters`);
console.log(` 3. Check that the API Key has the required permissions`);
console.log(` 4. Try creating a new API Key if this one doesn't work\n`);
if (USERNAME && PASSWORD) {
console.log('Trying username/password authentication as fallback...');
} else {
throw new Error('Authentication failed. Please check your API Key/Secret or add username/password credentials.');
}
} else {
throw error;
}
}
}
// Fallback to username/password if client credentials fail
if (USERNAME && PASSWORD) {
const params = new URLSearchParams();
params.append('username', USERNAME.trim());
params.append('password', PASSWORD.trim());
const response = await axios.post<TokenResponse>(
`${API_BASE_URL}/v1/oauth/token`,
params,
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
);
accessToken = response.data.access_token;
apiClient = axios.create({
baseURL: API_BASE_URL,
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: 'application/vnd.api+json',
},
});
console.log('✅ Authenticated successfully with username/password');
return;
}
throw new Error('Authentication failed. Please check your API Key/Secret or username/password.');
}
async function fetchAllPages<T>(
endpoint: string,
resourceName: string
): Promise<T[]> {
const allItems: T[] = [];
let pageNumber = 1;
let hasMore = true;
while (hasMore) {
try {
const response = await apiClient.get<PaginatedResponse<T>>(endpoint, {
params: {
'page[number]': pageNumber,
'page[size]': PAGE_SIZE,
},
});
const items = response.data.data || [];
allItems.push(...items);
console.log(`Fetched page ${pageNumber}: ${items.length} ${resourceName}`);
const meta = response.data.meta;
if (meta?.total_pages && pageNumber >= meta.total_pages) {
hasMore = false;
} else if (items.length === 0) {
hasMore = false;
} else {
pageNumber++;
}
} catch (error: any) {
if (error.response?.status === 404) {
console.log(`Endpoint ${endpoint} not available, skipping`);
hasMore = false;
} else {
throw error;
}
}
}
return allItems;
}
async function fetchResource<T>(endpoint: string, resourceName: string): Promise<T[]> {
console.log(`Fetching ${resourceName}...`);
try {
const items = await fetchAllPages<T>(endpoint, resourceName);
console.log(`✅ Downloaded ${items.length} ${resourceName}\n`);
return items;
} catch (error: any) {
console.error(`❌ Error fetching ${resourceName}:`, error.message);
return [];
}
}
async function saveToFile(data: any, filename: string, outputDir: string): Promise<void> {
const filePath = path.join(outputDir, filename);
await fs.ensureDir(outputDir);
await fs.writeJSON(filePath, data, { spaces: 2 });
}
async function downloadBlogPosts(outputDir: string): Promise<void> {
const posts = await fetchResource('/v1/blog_posts', 'blog posts');
await saveToFile(posts, 'blog_posts.json', outputDir);
}
async function downloadContactNotes(outputDir: string): Promise<void> {
const notes = await fetchResource('/v1/contact_notes', 'contact notes');
await saveToFile(notes, 'contact_notes.json', outputDir);
}
async function downloadContactTags(outputDir: string): Promise<void> {
const tags = await fetchResource('/v1/contact_tags', 'contact tags');
await saveToFile(tags, 'contact_tags.json', outputDir);
}
async function downloadContacts(outputDir: string): Promise<void> {
const contacts = await fetchResource('/v1/contacts', 'contacts');
await saveToFile(contacts, 'contacts.json', outputDir);
}
async function downloadCourses(outputDir: string): Promise<void> {
const courses = await fetchResource('/v1/courses', 'courses');
await saveToFile(courses, 'courses.json', outputDir);
}
async function downloadCustomFields(outputDir: string): Promise<void> {
const fields = await fetchResource('/v1/custom_fields', 'custom fields');
await saveToFile(fields, 'custom_fields.json', outputDir);
}
async function downloadCustomers(outputDir: string): Promise<void> {
const customers = await fetchResource('/v1/customers', 'customers');
await saveToFile(customers, 'customers.json', outputDir);
}
async function downloadFormSubmissions(outputDir: string): Promise<void> {
const submissions = await fetchResource('/v1/form_submissions', 'form submissions');
await saveToFile(submissions, 'form_submissions.json', outputDir);
}
async function downloadForms(outputDir: string): Promise<void> {
const forms = await fetchResource('/v1/forms', 'forms');
await saveToFile(forms, 'forms.json', outputDir);
}
async function downloadHooks(outputDir: string): Promise<void> {
const hooks = await fetchResource('/api/v1/hooks', 'hooks');
await saveToFile(hooks, 'hooks.json', outputDir);
}
async function downloadLandingPages(outputDir: string): Promise<void> {
const pages = await fetchResource('/v1/landing_pages', 'landing pages');
await saveToFile(pages, 'landing_pages.json', outputDir);
}
async function downloadOffers(outputDir: string): Promise<void> {
const offers = await fetchResource('/v1/offers', 'offers');
await saveToFile(offers, 'offers.json', outputDir);
}
async function downloadOrderItems(outputDir: string): Promise<void> {
const items = await fetchResource('/v1/order_items', 'order items');
await saveToFile(items, 'order_items.json', outputDir);
}
async function downloadOrders(outputDir: string): Promise<void> {
const orders = await fetchResource('/v1/orders', 'orders');
await saveToFile(orders, 'orders.json', outputDir);
}
async function downloadProducts(outputDir: string): Promise<void> {
const products = await fetchResource('/v1/products', 'products');
await saveToFile(products, 'products.json', outputDir);
}
async function downloadPurchases(outputDir: string): Promise<void> {
const purchases = await fetchResource('/v1/purchases', 'purchases');
await saveToFile(purchases, 'purchases.json', outputDir);
}
async function downloadSites(outputDir: string): Promise<void> {
const sites = await fetchResource('/v1/sites', 'sites');
await saveToFile(sites, 'sites.json', outputDir);
}
async function downloadTransactions(outputDir: string): Promise<void> {
const transactions = await fetchResource('/v1/transactions', 'transactions');
await saveToFile(transactions, 'transactions.json', outputDir);
}
async function downloadWebsitePages(outputDir: string): Promise<void> {
const pages = await fetchResource('/v1/website_pages', 'website pages');
await saveToFile(pages, 'website_pages.json', outputDir);
}
async function downloadMe(outputDir: string): Promise<void> {
try {
console.log('Fetching user info...');
const response = await apiClient.get('/v1/me');
await saveToFile(response.data, 'me.json', outputDir);
console.log('✅ Downloaded user info\n');
} catch (error: any) {
console.error('❌ Error fetching user info:', error.message);
}
}
async function main(): Promise<void> {
const outputDir = path.join(process.cwd(), 'backup', new Date().toISOString().split('T')[0]);
console.log('Starting Kajabi backup...\n');
console.log(`Output directory: ${outputDir}\n`);
try {
await authenticate();
await downloadMe(outputDir);
await downloadSites(outputDir);
await downloadBlogPosts(outputDir);
await downloadContactNotes(outputDir);
await downloadContactTags(outputDir);
await downloadContacts(outputDir);
await downloadCourses(outputDir);
await downloadCustomFields(outputDir);
await downloadCustomers(outputDir);
await downloadFormSubmissions(outputDir);
await downloadForms(outputDir);
await downloadHooks(outputDir);
await downloadLandingPages(outputDir);
await downloadOffers(outputDir);
await downloadOrderItems(outputDir);
await downloadOrders(outputDir);
await downloadProducts(outputDir);
await downloadPurchases(outputDir);
await downloadTransactions(outputDir);
await downloadWebsitePages(outputDir);
console.log('✅ Backup completed successfully!');
console.log(`✅ Data saved to: ${outputDir}`);
} catch (error: any) {
console.error('❌ Backup failed:', error.message);
if (error.response) {
console.error('Response:', error.response.data);
}
process.exit(1);
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment