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