Guides / JavaScript & Node.js

JavaScript & Node.js

Call the Lagoon API from the browser or a Node.js server using native fetch. No dependencies. The API sets Access-Control-Allow-Origin: *, so browser requests work without a proxy.

Prerequisites

Any modern browser, or Node.js 18+ (which includes fetch natively).

Search by tag

const response = await fetch( "https://lagoon.io/api/v1/search?tag=blue_hair&limit=10" ); const data = await response.json(); if (data.ok) { for (const post of data.results) { console.log(`${post.title} by ${post.artist_handle}`); console.log(` ${post.platform_name} | ${post.posted_at.slice(0, 10)}`); console.log(` Tags: ${post.tags.slice(0, 5).join(", ")}`); } }

Natural language search

Pass user input directly to the nl parameter. The API parses platform, date range, tags, and more from freeform text. The response includes a filters object showing what was extracted.

const query = "solo girl on pixiv from 2025"; const response = await fetch( `https://lagoon.io/api/v1/search?nl=${encodeURIComponent(query)}` ); const data = await response.json(); console.log("Parsed filters:", data.filters); // { platform: "pixiv", from: "2025-01-01", to: "2025-12-31", tags: ["1girl"], ... } console.log(`Found ${data.count} results`);

Look up an artist

Query a handle to get every linked account across platforms.

const response = await fetch( "https://lagoon.io/api/v1/artist?handle=hews__" ); const data = await response.json(); if (data.ok) { const { artist } = data; console.log(`${artist.display_name} (${artist.post_count} posts)`); for (const p of artist.platforms) { console.log(` ${p.platform_name}: ${p.handle}`); } }

Authenticate with an API key

Unauthenticated requests are rate-limited to 30 per minute. To use a higher-limit tier, pass your key in the X-API-Key header.

const API_KEY = "lg_your_key_here"; const response = await fetch( "https://lagoon.io/api/v1/search?tag=blue_hair&limit=20", { headers: { "X-API-Key": API_KEY } } ); const data = await response.json();

Build a URL with multiple parameters

Use URLSearchParams to construct queries with multiple filters.

const params = new URLSearchParams({ tag: "genshin_impact", platform: "pixiv", from: "2025-01-01", limit: "20", }); const response = await fetch( `https://lagoon.io/api/v1/search?${params}` ); const data = await response.json();

Paginate through results

When count equals your limit, there may be more pages. Increment offset by limit to get the next page. When count < limit, you have reached the last page.

async function searchAll(params) { const results = []; const limit = parseInt(params.limit || "50"); let offset = 0; while (true) { const url = new URL("https://lagoon.io/api/v1/search"); for (const [k, v] of Object.entries({ ...params, offset, limit })) { url.searchParams.set(k, String(v)); } const resp = await fetch(url); const data = await resp.json(); if (!data.ok) break; results.push(...data.results); if (data.count < limit) break; offset += limit; } return results; } // Fetch all blue_hair posts from X const allPosts = await searchAll({ tag: "blue_hair", platform: "x", limit: "100", }); console.log(`Total: ${allPosts.length} posts`);

Handle errors

Check the HTTP status code before parsing the body. The API returns "ok": false with an error message on failure.

async function lagoonFetch(url, apiKey) { const opts = apiKey ? { headers: { "X-API-Key": apiKey } } : {}; const resp = await fetch(url, opts); if (resp.status === 429) { const retryAfter = resp.headers.get("Retry-After") || "60"; throw new Error(`Rate limited. Retry in ${retryAfter}s.`); } if (resp.status === 402) { throw new Error("Insufficient balance."); } if (resp.status === 401) { throw new Error("Invalid or expired API key."); } const data = await resp.json(); if (!data.ok) throw new Error(data.error); return data; }

Full example: Node.js CLI search

A standalone Node.js script (18+) that takes a natural language query as a command-line argument and prints results.

// search.mjs - run with: node search.mjs "sword art on pixiv from 2025" const query = process.argv[2]; if (!query) { console.error("Usage: node search.mjs \"your search query\""); process.exit(1); } const BASE = "https://lagoon.io/api/v1"; // Search using natural language const resp = await fetch( `${BASE}/search?nl=${encodeURIComponent(query)}&limit=10` ); const data = await resp.json(); if (!data.ok) { console.error("Error:", data.error); process.exit(1); } console.log(`Found ${data.count} results`); if (data.filters) { console.log("Parsed filters:", JSON.stringify(data.filters)); } console.log(); for (const post of data.results) { const tags = post.tags .filter(t => !t.startsWith("rating:")) .slice(0, 6); console.log(post.title || `Post #${post.id}`); console.log(` ${post.artist_handle} on ${post.platform_name}`); console.log(` ${tags.join(", ")}`); if (post.source_url) console.log(` ${post.source_url}`); console.log(); }

Next steps