How I Exported 2k AI Prompts from Higgsfield

Vibe coding · 8 min read
How I Exported 2k AI Prompts from Higgsfield

My first real task with the desktop Claude code. And it works brilliantly! Kudos to Claude for writing this blog for me.

After months of intensive work with Higgsfield, I encountered one major practical issue:
more than 2k generated images, each with carefully crafted prompts, created with my trained Chatgpt - ZAPPA GPT — and no way to export them.

Everything was locked within the platform.
And that didn’t feel right.

My first real task with the desktop Claude code. And it works brilliantly! Kudos to Claude for writing this blog for me. I found it particularly interesting that the task wasn’t even fully completed, and I was already hitting the Claude PRO account limit, if I wanted to continue to MAX...... #thatsfast?

Higgsfield prompts preservation (nano banana pro)

Anyone seriously working with AI image generation knows:
your prompts are your capital.

You build them layer by layer. You test. You refine. And at some point, you have a library you don’t want to lose.

But what if:

  • Higgsfield stops tomorrow?
    You want to reuse your prompts in another system?
    You simply want your own backup?

There was no export function.
A Chrome extension only helped with new prompts — not for my existing archive.

Attempt 1 — The API

My first thought: this must be possible via an API.
There is one, but authentication is fully integrated into the frontend.

Result: “Invalid or expired token”.
No access.

❌ Failed.

Attempt 2 — Simple browser script

Then I tried to empty the Asset Library via the browser.
Scroll, grab images, done.

Result: 45 assets.
Out of 4500.

It turned out that Higgsfield doesn’t scroll in the browser window, but in an internal container.

❌ Missed again.

Attempt 3 — Right container, wrong assumption

After some investigation, I found the real scroll container.
ScrollHeight: 272,533 pixels.

But the count kept fluctuating strangely.
Sometimes more assets, sometimes fewer.

And that was the moment when the penny dropped.

The real issue — virtualised lists

Higgsfield doesn’t load all assets at once.
Only what you see exists in the DOM. The rest is dynamically replaced.

My script counted at the end —
but by that time, the first thousands of items had already disappeared.

The solution — collect while scrolling

Instead of counting afterwards, I started collecting while scrolling.

Each visible asset ID went directly into a Set (no duplicates).
Then continue scrolling.

And then it went quickly:

  • 300 assets
    2000 assets
    3000+ assets
    4571 unique assets

✅ Success.

From assets to real prompts

Those 4571 assets turned out to be approximately 2500 unique prompts.
Logical — you often create multiple variations of one prompt.

Everything is automatically grouped per prompt, with associated images.

What I learned from this

  • An API is not always the solution
    Modern web apps are smart — and sometimes deliberately shielded
    Debugging is done step by step, not in one leap

  • Your creative work should remain yours

How the export is technically structured

The process consists of three steps:

  1. Browser script
    Scrolls through Higgsfield and collects all asset IDs + prompts;
    Here .js for console.

    // higgsfield-prompt-exporter
    // **
     * ╔═══════════════════════════════════════════════════════════════╗
     * ║         HIGGSFIELD PROMPT EXPORTER                            ║
     * ║         Export to Prompt Saver JSON format                    ║
     * ╚═══════════════════════════════════════════════════════════════╝
     *
     * USAGE:
     * 1. Go to https://higgsfield.ai/asset/all
     * 2. Ensure you are logged in
     * 3. Open browser console (F12 → Console tab)
     * 4. Copy this ENTIRE script and paste it into the console
     * 5. Press Enter
     * 6. Wait until it is done (can take 30-60 min for 2000+ assets)
     * 7. JSON file will be automatically downloaded
     *
     * TIP: Keep the browser tab open and active during scraping
     */
    
    (async function HiggsFieldExporter() {
        'use strict';
    
        // ─────────────────────────────────────────────────────────────
        // CONFIGURATION
        // ─────────────────────────────────────────────────────────────
        const CONFIG = {
            SCROLL_DELAY: 800,       // ms between scrolls (increased!)
            SCROLL_STEP: 2000,       // pixels per scroll
            STABLE_ROUNDS: 10,       // more rounds to wait for "done"
            LOAD_DELAY: 1200,        // ms to wait after navigation
            BATCH_LOG_SIZE: 50,      // log progress every N assets
            MAX_ASSETS: 10000        // safety limit
        };
    
        // ─────────────────────────────────────────────────────────────
        // STATE
        // ─────────────────────────────────────────────────────────────
        const promptsMap = new Map();  // Group by unique prompts
        let totalProcessed = 0;
        let errors = 0;
        const startTime = Date.now();
    
        console.clear();
        console.log('%c🚀 HIGGSFIELD PROMPT EXPORTER', 'font-size: 20px; font-weight: bold; color: #00ff00');
        console.log('━'.repeat(50));
    
        // ─────────────────────────────────────────────────────────────
        // STEP 1: SCROLL TO LOAD ALL ASSETS
        // ─────────────────────────────────────────────────────────────
        console.log('\n📜 STEP 1: Loading all assets (scrolling)...');
    
        // First navigate to asset overview
        if (!window.location.pathname.includes('/asset/all')) {
            window.location.href = 'https://higgsfield.ai/asset/all';
            throw new Error('Navigate to /asset/all and run the script again');
        }
    
        console.log('   Searching for scroll container...');
    
        // IMPORTANT: Higgsfield uses an internal scroll container, not window!
        let scrollContainer = null;
        const allElements = document.querySelectorAll('div');
        for (const el of allElements) {
            const style = window.getComputedStyle(el);
            if ((style.overflowY === 'auto' || style.overflowY === 'scroll') &&
                el.scrollHeight > el.clientHeight + 100 &&
                el.scrollHeight > 5000) {
                scrollContainer = el;
                console.log(`   ✓ Scroll container found (${el.scrollHeight}px)`);
                break;
            }
        }
    
        if (!scrollContainer) {
            console.error('   ❌ No scroll container found! Trying window...');
            scrollContainer = document.documentElement;
        }
    
        // VIRTUALIZED LIST FIX: Collect IDs WHILE scrolling!
        // Higgsfield removes items from DOM that are not visible
        const collectedIds = new Set();
        let lastCollectedCount = 0;
        let noNewIdsRounds = 0;
    
        console.log('   Scrolling through all assets (collecting IDs along the way)...');
    
        while (noNewIdsRounds < 15) {
            // First collect all visible IDs before scrolling
            const visibleImgs = document.querySelectorAll('img[alt*="media asset by id"]');
            visibleImgs.forEach(img => {
                const match = img.alt.match(/id of ([a-f0-9-]+)/);
                if (match) {
                    collectedIds.add(match[1]);
                }
            });
    
            // Check if we found new IDs
            if (collectedIds.size === lastCollectedCount) {
                noNewIdsRounds++;
            } else {
                noNewIdsRounds = 0;
                if (collectedIds.size % 100 < 10) {
                    console.log(`   📦 ${collectedIds.size} unique assets collected...`);
                }
            }
            lastCollectedCount = collectedIds.size;
    
            // Scroll down
            scrollContainer.scrollTop += CONFIG.SCROLL_STEP;
            await sleep(CONFIG.SCROLL_DELAY);
    
            // Extra wait if we are at the bottom
            if (scrollContainer.scrollTop >= scrollContainer.scrollHeight - scrollContainer.clientHeight - 500) {
                await sleep(400);
                // Small scroll back and forth to load more
                scrollContainer.scrollTop -= 500;
                await sleep(200);
                scrollContainer.scrollTop += 600;
                await sleep(400);
            }
    
            if (collectedIds.size >= CONFIG.MAX_ASSETS) {
                console.log('   ⚠️ Maximum reached');
                break;
            }
        }
    
        // Scroll back to the top
        scrollContainer.scrollTop = 0;
        await sleep(500);
    
        // Convert Set to Array
        const assetIds = Array.from(collectedIds);
    
        console.log(`✅ ${assetIds.length} unique assets found`);
    
        // ─────────────────────────────────────────────────────────────
        // STEP 2: RETRIEVE PROMPTS FROM EACH ASSET
        // ─────────────────────────────────────────────────────────────
        const estMinutes = Math.round(assetIds.length * 1.2 / 60);
        console.log(`\n🔍 STEP 2: Retrieving prompts (~${estMinutes} minutes)...`);
    
        for (let i = 0; i < assetIds.length; i++) {
            const assetId = assetIds[i];
    
            try {
                // Navigate to asset
                window.history.pushState({}, '', `/asset/all/${assetId}`);
                window.dispatchEvent(new PopStateEvent('popstate'));
                await sleep(CONFIG.LOAD_DELAY);
    
                // Extract prompt
                const data = extractPrompt();
    
                if (data.prompt && data.prompt.length > 10) {
                    const key = data.prompt.trim();
    
                    if (!promptsMap.has(key)) {
                        promptsMap.set(key, {
                            promptText: data.prompt,
                            model: data.model,
                            assetIds: []
                        });
                    }
    
                    promptsMap.get(key).assetIds.push(assetId);
                    totalProcessed++;
                }
    
            } catch (e) {
                errors++;
            }
    
            // Progress log
            if ((i + 1) % CONFIG.BATCH_LOG_SIZE === 0) {
                const pct = Math.round((i + 1) / assetIds.length * 100);
                const elapsed = (Date.now() - startTime) / 60000;
                const remaining = (elapsed / (i + 1)) * (assetIds.length - i - 1);
                console.log(`   ${pct}% (${i + 1}/${assetIds.length}) - approximately ~${Math.round(remaining)} min left`);
            }
        }
    
        // Back to overview
        window.history.pushState({}, '', '/asset/all');
        window.dispatchEvent(new PopStateEvent('popstate'));
    
        console.log(`✅ ${totalProcessed} prompts collected (${errors} errors)`);
    
        // ─────────────────────────────────────────────────────────────
        // STEP 3: EXPORT TO PROMPT SAVER FORMAT
        // ─────────────────────────────────────────────────────────────
        console.log('\n💾 STEP 3: Generating JSON...');
    
        const exportData = [];
    
        promptsMap.forEach((data, promptKey) => {
            // Create title from first 50 chars
            const title = data.promptText.replace(/[\n\r]+/g, ' ').trim();
            const shortTitle = title.length > 50 ? title.substring(0, 47) + '...' : title;
    
            exportData.push({
                title: shortTitle,
                promptText: data.promptText,
                imageUrl: `https://higgsfield.ai/asset/all/${data.assetIds[0]}`,
                // Extra metadata (Prompt Saver ignores this)
                _meta: {
                    model: data.model,
                    totalImages: data.assetIds.length,
                    allUrls: data.assetIds.map(id => `https://higgsfield.ai/asset/all/${id}`)
                }
            });
        });
    
        // Download
        const filename = `higgsfield-prompts-${new Date().toISOString().slice(0, 10)}.json`;
        downloadJSON(exportData, filename);
    
        // ─────────────────────────────────────────────────────────────
        // DONE!
        // ─────────────────────────────────────────────────────────────
        const totalTime = Math.round((Date.now() - startTime) / 60000);
    
        console.log('\n' + '═'.repeat(50));
        console.log('%c✅ EXPORT COMPLETED!', 'font-size: 16px; font-weight: bold; color: #00ff00');
        console.log('═'.repeat(50));
        console.log(`📊 ${exportData.length} unique prompts`);
        console.log(`🖼️  ${totalProcessed} total images`);
        console.log(`⏱️  ${totalTime} minutes`);
        console.log(`📁 File: ${filename}`);
        console.log('═'.repeat(50));
    
        // Return for debugging
        return exportData;
    
        // ─────────────────────────────────────────────────────────────
        // HELPER FUNCTIONS
        // ─────────────────────────────────────────────────────────────
    
        function sleep(ms) {
            return new Promise(r => setTimeout(r, ms));
        }
    
        function extractPrompt() {
            const body = document.body.innerText;
    
            // Find prompt between "Copy" and "See all"
            const startIdx = body.indexOf('Copy');
            const endIdx = body.indexOf('See all');
    
            let prompt = '';
            if (startIdx > 0 && endIdx > startIdx) {
                prompt = body.substring(startIdx + 4, endIdx).trim();
            }
    
            // Find model name
            let model = 'Unknown';
            const models = ['Nano Banana Pro', 'Kling', 'Minimax', 'Luma', 'Runway'];
            for (const m of models) {
                if (body.includes(m)) {
                    model = m;
                    break;
                }
            }
    
            return { prompt, model };
        }
    
        function downloadJSON(data, filename) {
            const json = JSON.stringify(data, null, 2);
            const blob = new Blob([json], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
    
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
    
            URL.revokeObjectURL(url);
        }
    
    })();
    
  2. Node.js script
    Downloads thumbnails and transfers everything to personal storage

  3. Import
    Final JSON import into my own prompt library

Total time: ±2 hours
(most of which was just waiting)