API Documentation

Everything returns JSON. CORS is on. Base URL:

https://lagoon.io/api/v1

The full specification is available as an OpenAPI document.

Authentication

No key needed to start. You get 30 requests per minute immediately. For higher limits, pass your Usage or Pro key in the X-API-Key header:

curl "https://lagoon.io/api/v1/search?nl=glasses+on+twitter"

With a Usage or Pro key:

curl \ -H "X-API-Key: YOUR_KEY" \ "https://lagoon.io/api/v1/search?nl=glasses+on+twitter"

Rate Limits

Free requests are capped per IP. Usage and Pro keys are capped per key. Hit the limit and you'll get a 429 with a Retry-After header.

TierRatePriceScope
Free30 / min$0Per IP
Usage120 / min$2 / 1k requestsPer key
Pro300 / min$25 / monthPer key

Deleted content

When an artist removes their work, the API marks the post "deleted": true and returns a null source URL. Tags and metadata stay available. The /search endpoint excludes deleted posts; /post returns them by ID. Thumbnails are removed for deleted posts; the /thumb endpoint returns 404.

Endpoints

GET /api/v1/search

Find posts by natural language, text, tag, artist, or platform. Combine parameters to narrow results.

ParameterTypeDescription
nlstringNatural language query. The API extracts platform, artist, date range, tags, rating, sort order, and exclusions from your text. Leftover words become a full-text search. See Natural language search.
qstringFull-text search query. Searches post titles, descriptions, and indexed content using PostgreSQL full-text search. Supports quoted phrases, OR, and -exclusion.
tagstringExact tag match (normalized form, e.g. genshin_impact). Use /tags to discover available tags.
artiststringArtist handle or display name (case-insensitive pattern match).
platformstringPlatform slug: x, pixiv, bluesky, deviantart, artstation, newgrounds, tumblr, zerochan.
sfwstringSFW filter, on by default. Only posts tagged rating:general or rating:sensitive are returned. Set to 0 to include all ratings.
ratingstringFilter to a specific rating tag: general, sensitive, questionable, or explicit. Takes precedence over sfw if both are set.
fromstringStart date filter (YYYY-MM-DD). Only return posts published on or after this date.
tostringEnd date filter (YYYY-MM-DD). Only return posts published on or before this date.
sincestringIngestion timestamp filter (ISO 8601). Only return posts added to the index on or after this time. Unlike from/to (which filter by publication date), since filters by when the post was indexed. Use this to monitor for newly indexed content.
limitintegerResults per page, 1-100 (default: 50).
offsetintegerPagination offset (default: 0). Use with limit to page through results.
colorstringFilter by dominant color. Accepts named colors (red, orange, yellow, green, cyan, teal, blue, purple, pink, brown, black, white, gray), moods (pastel, dark, warm, cool), or hex codes (#FF6600). Comma-separate for AND logic: color=blue,warm returns posts with both blue and warm tones in their palette. Maximum 4 colors per query. ~79% of posts have palette data.
sortstringSort order: newest (default) or oldest. When q is used without an explicit sort, results are ordered by relevance instead.
facetsstringSet to 1 to include faceted counts. Returns total result count, per-platform breakdown, and per-rating breakdown for the full unpaginated result set.

At least one of nl, q, tag, artist, since, or color is required.

Choosing your search approach

The search endpoint offers three ways to query, depending on where the input comes from and how much control you need.

nl: Natural language. Use when the input comes from a human typing freeform text. The API parses platform, artist, dates, tags, rating, sort, and exclusions out of the text. Multi-word phrases that match known tags (like "white hair" or "dark skin") are resolved to exact tag filters. Anything left becomes a full-text search. Best for: search bars, chatbot commands, user-facing input where you don't want to build your own parser.

# User typed "white hair on pixiv from 2025" into your search bar curl "https://lagoon.io/api/v1/search?nl=white+hair+on+pixiv+from+2025" # API extracts: platform=pixiv, from=2025-01-01, to=2025-12-31 # "white hair" resolves to the white_hair tag filter

q: Full-text search. Use when you want direct control over what gets searched against post titles, descriptions, and indexed content. No parsing, no extraction. The string goes straight to the search engine. Supports PostgreSQL websearch_to_tsquery syntax: quoted phrases for exact match, OR for alternatives, -word to exclude. Best for: programmatic queries where you've already parsed the input yourself, or when you want to combine structured params with a specific text search.

# Search for posts with "blue hair" OR "purple hair" in their text curl "https://lagoon.io/api/v1/search?q=blue+hair+OR+purple+hair" # Exact phrase search curl "https://lagoon.io/api/v1/search?q=%22dark+elf%22" # Exclude a term from text search curl "https://lagoon.io/api/v1/search?q=sword+-shield"

Explicit parameters: tag, artist, platform, from, to, sfw, rating. Use when you have structured UI controls (dropdowns, date pickers, checkboxes, tag selectors) and know exactly what each filter is. Best for: filter panels, faceted search, data pipelines, automated queries.

# Structured query: Genshin Impact art on Pixiv, newest 20 curl "https://lagoon.io/api/v1/search?tag=genshin_impact&platform=pixiv&limit=20"

Combining them. All three compose. Explicit parameters override anything the NL parser extracts for the same field. This lets you have a search bar (nl) alongside structured controls (checkboxes, dropdowns) that always take priority.

# User typed "long hair on twitter" but your platform dropdown is set to Pixiv curl "https://lagoon.io/api/v1/search?nl=long+hair+on+twitter&platform=pixiv" # NL parses platform=x, but explicit platform=pixiv overrides it # Result: long hair posts on Pixiv

Content rating tags

Every post carries a rating tag that classifies the source artwork. The API returns this as a text tag in the JSON response alongside all other tags. Lagoon serves structured metadata and scaled thumbnails for identification purposes. Full-resolution artwork is not hosted or transmitted.

Rating tagSource artwork classification
generalNo suggestive elements in the source artwork.
sensitiveMildly suggestive source artwork (e.g. swimwear).
questionableSuggestive source artwork.
explicitAdult source artwork.

SFW filtering is on by default. All queries return only general and sensitive content unless you opt out:

# Default behavior: SFW only (no param needed) curl "https://lagoon.io/api/v1/search?tag=genshin_impact" # Specific rating: returns only posts with that exact rating tag curl "https://lagoon.io/api/v1/search?tag=genshin_impact&rating=general" # Disable SFW filter to include all rating tags curl "https://lagoon.io/api/v1/search?tag=genshin_impact&sfw=0" # Via natural language: "sfw", "wholesome", etc. are recognized curl "https://lagoon.io/api/v1/search?nl=sfw+genshin+impact"

Use-case examples

Gallery app with tag browsing. Your app shows a grid of art filtered by tag with pagination. SFW filtering is on by default.

# Page 1: first 20 Genshin Impact posts curl "https://lagoon.io/api/v1/search?tag=genshin_impact&limit=20" # Page 2 curl "https://lagoon.io/api/v1/search?tag=genshin_impact&limit=20&offset=20" # User picks a different tag from your sidebar curl "https://lagoon.io/api/v1/search?tag=blue_hair&limit=20"

Discord bot search command. Users type /search touhou on twitter newest. Pass their input directly to nl. The parsed filters in the response tell you what was extracted, which you can show back to the user.

# User runs: /search touhou on twitter newest curl "https://lagoon.io/api/v1/search?nl=touhou+on+twitter+newest&limit=5" # Response includes parsed filters you can display: # "filters": {"platform": "x", "q": "touhou"}

Monitoring an artist for new posts. Check for new work by a specific artist on a specific platform since a given date. Run this on a schedule to alert when new posts appear.

# All posts by hews__ on X since January 2025 (by publication date) curl "https://lagoon.io/api/v1/search?artist=hews__&platform=x&from=2025-01-01" # Same thing via NL curl "https://lagoon.io/api/v1/search?nl=by+hews__+on+twitter+since+2025-01-01"

Index monitoring with since. Track newly indexed content by ingestion time. Unlike from/to (which filter by publication date), since filters by when the post was added to the index. Use this to build feeds or detect new content without fragile offset tracking.

# Everything indexed in the last 24 hours curl "https://lagoon.io/api/v1/search?since=2026-06-16T00:00:00Z" # New Pixiv posts indexed since yesterday curl "https://lagoon.io/api/v1/search?since=2026-06-16&platform=pixiv" # Combine with artist to monitor for newly indexed work curl "https://lagoon.io/api/v1/search?since=2026-06-16&artist=hews__"

Search bar with structured controls. Your UI has a text input, a platform dropdown, and an SFW checkbox. The text input feeds nl, the dropdown feeds platform, and the checkbox feeds sfw. Explicit params override anything the NL parser finds.

# User typed "solo girl blue eyes" with platform=pixiv and sfw=on curl "https://lagoon.io/api/v1/search?nl=solo+girl+blue+eyes&platform=pixiv" # Even if the user types "on twitter" in the search bar, # your explicit platform=pixiv overrides it

Date-bounded data export. Pull all posts matching a tag within a date range. Page through the full result set.

# All blue_hair posts from January to June 2025 on X curl "https://lagoon.io/api/v1/search?tag=blue_hair&platform=x&from=2025-01-01&to=2025-06-30&limit=100" # Next page curl "https://lagoon.io/api/v1/search?tag=blue_hair&platform=x&from=2025-01-01&to=2025-06-30&limit=100&offset=100"

Browsing an artist's full portfolio. First look up the artist to see all their linked accounts, then search for their posts.

# Step 1: Get cross-platform profile curl "https://lagoon.io/api/v1/artist?handle=hews__" # Step 2: Browse their posts, newest first curl "https://lagoon.io/api/v1/search?artist=hews__&limit=50" # Step 2 alt: Only their Pixiv posts, SFW curl "https://lagoon.io/api/v1/search?artist=hews__&platform=pixiv"

Faceted search for filter panels. Pass facets=1 to get the total result count and breakdowns by platform and rating. Use this to build filter UIs with accurate counts.

# Search with faceted counts curl "https://lagoon.io/api/v1/search?tag=blue_hair&facets=1" # Response includes: "facets": {"total": 5432, "platforms": [...], "ratings": [...]}

Color palette search. Filter posts by dominant colors in their extracted palette. Combine with other filters to find art with specific color schemes.

# Posts with blue as a dominant color curl "https://lagoon.io/api/v1/search?color=blue&limit=10" # Blue and gold color scheme curl "https://lagoon.io/api/v1/search?color=blue,orange&tag=1girl" # Pastel-toned art on Pixiv curl "https://lagoon.io/api/v1/search?color=pastel&platform=pixiv" # Specific hex color (matches within ±20 HSL) curl "https://lagoon.io/api/v1/search?color=%23FF6600"

Full-text search with OR and exclusion. Use q directly when you need PostgreSQL search syntax that the NL parser doesn't handle.

# Posts mentioning "elf" OR "fairy" but not "dark" curl "https://lagoon.io/api/v1/search?q=elf+OR+fairy+-dark" # Exact phrase match curl "https://lagoon.io/api/v1/search?q=%22dark+elf%22&platform=pixiv"

Parameter precedence

When nl is combined with explicit parameters, the explicit parameter always wins for the same field:

NL extractsExplicit paramResult
platform=xplatform=pixivplatform=pixiv
from=2026-05-09from=2025-01-01from=2025-01-01
q=armorq=swordq=sword
rating=general(none)rating=general (specific rating overrides default SFW)
(none)sfw=0SFW filter off (all ratings returned)

Pagination

Use limit and offset to page through results. The response count tells you how many results were returned in this page. To get the next page, add the current limit to the current offset. When count is less than limit, you've reached the last page.

# Page 1: offset=0, get 20 results curl "https://lagoon.io/api/v1/search?tag=blue_hair&limit=20" # count: 20 # Page 2: offset=20 curl "https://lagoon.io/api/v1/search?tag=blue_hair&limit=20&offset=20" # count: 20 # Page 3: offset=40 curl "https://lagoon.io/api/v1/search?tag=blue_hair&limit=20&offset=40" # count: 14 (last page)

Example response:

{ "ok": true, "count": 1, "offset": 0, "results": [ { "id": 184201, "source_url": "https://x.com/artist/status/...", "thumb_url": "https://lagoon.io/api/v1/thumb?id=184201", "title": "character name (series)", "posted_at": "2024-11-15 08:30:00+00", "platform": "x", "platform_name": "X (Twitter)", "artist_handle": "artist_name", "artist_name": "Artist Name", "deleted": false, "width": 1920, "height": 2560, "palette": ["#656AA7", "#8888B6", "#C7C7F9", "#87F9FC", "#FBE7FC", "#05E7F9"], "tags": [ "character:name", "copyright:series", "blue hair", "sword", "rating:general" ] } ] }

width and height are the original image dimensions in pixels (null when not available from the source platform; ~99.8% coverage). palette is an array of up to 6 dominant hex colors extracted from the image, ordered roughly by dominance (null when not available; ~79% coverage).

When facets=1 is passed, the response includes a facets object with the total unpaginated count and breakdowns by platform and content rating:

{ "facets": { "total": 5432, "platforms": [ {"slug": "pixiv", "name": "Pixiv", "count": 3000}, {"slug": "x", "name": "X (Twitter)", "count": 1500} ], "ratings": [ {"rating": "general", "count": 2000}, {"rating": "sensitive", "count": 1500} ] } }

The nl parameter accepts plain English queries. The API parses your text into structured filters, runs the search, and returns both the results and the filters it extracted so you can see exactly how the query was interpreted.

The parser recognizes:

CategoryExamplesWhat it becomes
Platformson twitter, from pixiv, blueskyplatform=x
Artists@username, by sakimichanhandle=sakimichan
Dateslast 3 months, from 2024, march 2025, summer 2024, yesterday, this weekfrom=... and to=...
Tagsdark skin, blue hair, species:cat, solo, two girlstags=[...]
Ratingsfw, nsfw, explicit, wholesomerating=...
Sortingnewest, oldest, earliestsort=oldest
Exclusionsnot from pixiv, without watermark, minus @userNOT filters on platform, handle, or text
Limitstop 10, first 5limit=10

Filler words like "show me", "find", "art", "posts", "please" are stripped automatically. Multi-word phrases left after parsing are matched against known tags and converted to exact tag filters when found (e.g. "dark skin" resolves to the dark_skin tag). Any remaining text becomes the full-text search query (q).

When nl is used, the response includes a filters key showing what was extracted:

curl "https://lagoon.io/api/v1/search?nl=dark+skin+on+twitter+from+2025" # Response: { "ok": true, "count": 50, "offset": 0, "filters": { "platform": "x", "from": "2025-01-01", "to": "2025-12-31", "tags": ["dark_skin"] }, "results": [...] }

More NL examples:

# Solo character art from Pixiv, sorted oldest first curl "https://lagoon.io/api/v1/search?nl=solo+girl+on+pixiv+oldest" # Specific artist, recent work curl "https://lagoon.io/api/v1/search?nl=by+hews__+on+twitter+last+3+months" # Multi-word tag with year curl "https://lagoon.io/api/v1/search?nl=red+eyes+from+2025" # Exclusions curl "https://lagoon.io/api/v1/search?nl=hololive+not+from+pixiv+without+watermark" # Combine NL with explicit params (explicit wins over NL-parsed platform) curl "https://lagoon.io/api/v1/search?nl=armor+on+twitter&platform=pixiv"

GET /api/v1/random

Return random posts from the index. Accepts the same filters as /search (tag, platform, artist, date range, rating, NL) to narrow the pool before sampling. No required parameters.

ParameterTypeDescription
nlstringNatural language filter. Same parser as /search: extracts platform, date, tags, rating, exclusions.
qstringFull-text search filter. Only posts matching this text are eligible for random selection.
tagstringExact tag match (normalized).
artiststringArtist handle or display name (case-insensitive).
platformstringPlatform slug.
sfwstringSFW filter, on by default. Set to 0 to include all ratings.
ratingstringFilter to a specific rating.
fromstringStart date (YYYY-MM-DD).
tostringEnd date (YYYY-MM-DD).
limitintegerNumber of random posts to return, 1-50 (default: 1).

Example requests:

# One random post from the entire index curl "https://lagoon.io/api/v1/random" # 5 random Genshin Impact posts from Pixiv curl "https://lagoon.io/api/v1/random?tag=genshin_impact&platform=pixiv&limit=5" # Random post matching NL filter curl "https://lagoon.io/api/v1/random?nl=blue+hair+from+pixiv" # 10 random posts from the last 30 days curl "https://lagoon.io/api/v1/random?from=2026-05-16&limit=10"

Response format is identical to /search, with each result including tags and description_long. When nl is used, the response includes a filters key showing what was extracted.

GET /api/v1/artist

Query a handle and get back every linked account across platforms. Use this to build profile pages, cross-reference artists, or verify that two handles belong to the same person.

ParameterTypeDescription
handle requiredstringArtist handle to look up. When platform is set, must be the platform-specific handle (e.g. a Pixiv numeric user ID, not the artist's X handle).
platformstringNarrow the handle match to a specific platform. Useful when the same handle string exists on multiple platforms for different people.

When to use this vs. /search?artist=: This endpoint returns the artist's profile (linked accounts, post count). The artist param on /search returns the artist's posts. Use this first to confirm you have the right artist, then search their posts.

Example request:

curl "https://lagoon.io/api/v1/artist?handle=hews__"

Example response:

{ "ok": true, "artist": { "handle": "hews__", "display_name": "Hews", "post_count": 47, "platforms": [ { "platform": "x", "handle": "hews__", "profile_url": "https://x.com/hews__" }, { "platform": "pixiv", "handle": "12345678", "profile_url": "https://pixiv.net/users/12345678" } ] } }

Use-case: disambiguating a Pixiv artist by their numeric ID.

# The handle "12345678" is a Pixiv user ID, not an X handle curl "https://lagoon.io/api/v1/artist?handle=12345678&platform=pixiv"

GET /api/v1/post

Get full metadata for a single post by ID: tags, extended description, artist profile, and source link. Use this when you already have a post ID from a search result and want the complete data (search results include core fields; this endpoint adds description_long and artist_profile).

ParameterTypeDescription
id requiredintegerPost ID (from search results or your own database).

Example request:

curl "https://lagoon.io/api/v1/post?id=5995"

Example response:

{ "ok": true, "post": { "id": 5995, "source_url": "https://x.com/artist/status/...", "thumb_url": "https://lagoon.io/api/v1/thumb?id=5995", "title": "character name (series) by artist", "description": null, "posted_at": "2026-04-27 03:54:15+00", "platform": "x", "platform_name": "X (Twitter)", "artist_handle": "artist_name", "artist_name": "Artist Name", "deleted": false, "description_long": "An anime-style character with long white hair in a detailed fantasy outfit, holding a glowing sword...", "artist_profile": "https://x.com/artist_name", "width": 1920, "height": 2560, "palette": ["#656AA7", "#8888B6", "#C7C7F9", "#87F9FC", "#FBE7FC", "#05E7F9"], "tags": [ "1girl", "blue hair", "character:name", "copyright:series", "rating:general" ] } }

GET /api/v1/tags

Search the tag index by name. Use this to discover available tags, build tag autocomplete, or check tag post counts before searching. Returns matching tags sorted by category priority (character, copyright, then general) and by post count within each category.

ParameterTypeDescription
q requiredstringTag name search (min 2 characters). Matches anywhere in the tag name.
limitintegerMax results, 1-50 (default: 20).

Tags are categorized as character, copyright, species, rating, or general. Use this to find the exact normalized tag name before passing it to /search?tag=.

Example: building a tag autocomplete. User types "gen" into your search field, you query the tag index to show suggestions.

curl "https://lagoon.io/api/v1/tags?q=genshin&limit=3"

Example response:

{ "ok": true, "count": 3, "tags": [ { "name": "character:ganyu (genshin impact)", "normalized": "character_ganyu_genshin_impact", "post_count": 2116, "category": "character" }, { "name": "character:mona (genshin impact)", "normalized": "character_mona_genshin_impact", "post_count": 1058, "category": "character" }, { "name": "character:lumine (genshin impact)", "normalized": "character_lumine_genshin_impact", "post_count": 982, "category": "character" } ] }

Then use the tag name in a search:

curl "https://lagoon.io/api/v1/search?tag=genshin_impact&limit=20"

GET /api/v1/stats

Returns index-wide statistics: total posts, artists, tags, and a per-platform breakdown of searchable post counts. No parameters required.

Use this to check data coverage before integrating, display index size in your UI, or verify which platforms are tracked.

Example request:

curl "https://lagoon.io/api/v1/stats"

Example response:

{ "ok": true, "total_posts": 467246, "total_artists": 92258, "total_tags": 30674, "platforms": [ { "slug": "pixiv", "name": "Pixiv", "post_count": 154063 }, { "slug": "x", "name": "X (Twitter)", "post_count": 73326 }, { "slug": "tumblr", "name": "Tumblr", "post_count": 2242 } ] }
FieldTypeDescription
total_postsintegerTotal indexed posts across all platforms.
total_artistsintegerTotal unique artists tracked across all platforms.
total_tagsintegerTotal tags with at least 3 posts (the same threshold used by /tags).
platformsarrayPer-platform breakdown, sorted by post count descending. Each entry has slug, name, and post_count. Post counts reflect the searchable subset: tagged, non-duplicate, non-deleted posts matching what /search returns.

GET /api/v1/co-occurrence Analytics

Tag co-occurrence data. Returns tags that frequently appear alongside a given tag, with counts and Jaccard similarity scores. Requires an API key (not available on the free tier). Billed at the analytics rate: 10 mills per call ($10/1,000 requests).

ParameterTypeDescription
tagstringRequired. Tag name to look up co-occurrences for. Matched by highest post count, so genshin_impact resolves to copyright:genshin impact.
limitintegerMax results. Default 20, max 100.
min_countintegerMinimum co-occurrence count to include. Default 10.

Example request:

curl -H "X-API-Key: YOUR_KEY" \ "https://lagoon.io/api/v1/co-occurrence?tag=blue_archive&limit=5"

Example response:

{ "ok": true, "tag": "copyright:blue archive", "tag_post_count": 12844, "count": 5, "co_occurrences": [ { "tag": "halo", "category": "general", "count": 11182, "jaccard": 0.5406 }, { "tag": "1girl", "category": "general", "count": 11593, "jaccard": 0.0423 } ] }
FieldTypeDescription
tagstringThe resolved tag name.
tag_post_countintegerNumber of active posts with this tag.
co_occurrences[].tagstringCo-occurring tag name.
co_occurrences[].categorystringTag category: general, character, copyright, species, or rating.
co_occurrences[].countintegerNumber of posts where both tags appear together.
co_occurrences[].jaccardnumberJaccard similarity (intersection / union). Ranges from 0 to 1; higher means stronger association.

Tags that are appearing disproportionately often in recently indexed posts compared to their historical rate. The trend score is the ratio of observed frequency to expected frequency: a score of 10 means the tag appeared 10x more than its baseline rate in the window. Requires an API key (not available on the free tier). Billed at the analytics rate: 10 mills per call ($10/1,000 requests).

ParameterTypeDescription
windowstringTime window to measure. One of 1h, 6h, 12h, 24h, 48h, 7d. Default 24h.
limitintegerMax results. Default 20, max 50.
min_postsintegerMinimum total post count for a tag to be eligible. Default 10. Increase to filter out rare tags.
categorystringFilter to a specific tag category: character, copyright, species, rating, or general.

Example request:

curl -H "X-API-Key: YOUR_KEY" \ "https://lagoon.io/api/v1/trending?window=24h&limit=5"

Example response:

{ "ok": true, "window": "24h", "count": 5, "tags": [ { "tag": "character:sessyoin kiara (heaven's hole)", "normalized": "character_sessyoin_kiara_heaven_s_hole", "category": "character", "recent_count": 29, "total_count": 46, "trend_score": 36.0 } ] }
FieldTypeDescription
windowstringThe time window used.
tags[].tagstringTag display name.
tags[].normalizedstringNormalized tag name (pass to /search?tag=).
tags[].categorystringTag category: general, character, copyright, species, or rating.
tags[].recent_countintegerNumber of posts with this tag in the window.
tags[].total_countintegerTotal posts with this tag across the entire index.
tags[].trend_scorenumberObserved/expected ratio. Higher means stronger trend. A score of 1.0 means the tag is appearing at its normal rate.

Filter to specific tag categories to focus on what matters:

# Trending series/franchises in the last 48 hours curl -H "X-API-Key: YOUR_KEY" \ "https://lagoon.io/api/v1/trending?category=copyright&window=48h" # Trending characters right now (1-hour window) curl -H "X-API-Key: YOUR_KEY" \ "https://lagoon.io/api/v1/trending?category=character&window=1h"

GET /api/v1/batch-similar Compute

Pairwise cosine similarity matrix for a set of post IDs. Returns all pairs above a similarity threshold. Requires an API key (not available on the free tier). Billed per input post ID: 10 mills each. N=50 posts costs 500 mills ($0.50).

ParameterTypeDescription
idsstringRequired. Comma-separated post IDs. Maximum 100 on Usage tier, 200 on Pro.
thresholdnumberMinimum similarity score to include in results. Default 0.5. Range 0 to 1.
# Similarity matrix for 3 posts curl -H "X-API-Key: YOUR_KEY" \ "https://lagoon.io/api/v1/batch-similar?ids=5995,6001,6050&threshold=0.3"

Response

{ "ok": true, "count": 3, "pairs": [ {"id_a": 5995, "id_b": 6050, "similarity": 0.8959}, {"id_a": 5995, "id_b": 6001, "similarity": 0.8358}, {"id_a": 6001, "id_b": 6050, "similarity": 0.8069} ] }
FieldTypeDescription
pairs[].id_aintegerFirst post ID (always less than id_b).
pairs[].id_bintegerSecond post ID.
pairs[].similaritynumberCosine similarity (0 to 1). Higher means more similar.
missing_idsarrayPost IDs from the request that had no embedding. Omitted if all IDs were found.

Pairs are sorted by similarity descending. Duplicate IDs in the input are deduplicated. Each unique pair appears once (id_a < id_b).

GET /api/v1/similar

Find semantically similar posts using vector embeddings. Two modes: pass a post ID to find posts similar to it, or pass a text query to find posts matching a description. All filters from /search (platform, artist, SFW, rating) compose with similarity.

ParameterTypeDescription
idintegerPost ID to find similar posts for. Mutually exclusive with q.
qstringText description to match semantically (3-500 characters). Mutually exclusive with id.
platformstringFilter results to a specific platform slug.
artiststringFilter results to a specific artist handle (case-insensitive).
sfwstringSFW filter, on by default. Set to 0 to include all ratings.
ratingstringFilter to a specific rating. Overrides sfw.
limitintegerMax results, 1-50 (default: 10).

At least one of id or q is required.

How it works

Every post has a vector embedding derived from its tags and metadata. The endpoint ranks results by cosine similarity (0 to 1, where 1 is identical), then applies any filters you specify.

When using q, your text is encoded at request time. Describe what you're looking for in plain language and get results ranked by semantic relevance, not keyword overlap.

Similar by ID

Find posts similar to one you already have:

# Posts similar to post 5995 curl "https://lagoon.io/api/v1/similar?id=5995&limit=5" # Same, but only from Pixiv curl "https://lagoon.io/api/v1/similar?id=5995&platform=pixiv&limit=5"

Semantic text search

Describe what you're looking for. Unlike /search?q= (which matches keywords in titles and descriptions), this matches against the semantic content of posts:

# Find posts matching a visual description curl "https://lagoon.io/api/v1/similar?q=girl+with+sword+in+the+rain" # Semantic search with platform filter curl "https://lagoon.io/api/v1/similar?q=girl+in+school+uniform+with+cherry+blossoms&platform=pixiv" # Combine with artist filter curl "https://lagoon.io/api/v1/similar?q=mecha+battle+scene&artist=hews__"

When to use /similar vs. /search

Use caseEndpointWhy
User typed "blue hair sword on pixiv"/search?nl=...NL parser extracts structured filters from casual text.
Find posts like one you already have/similar?id=...Vector similarity finds visually/thematically related posts.
Match a description no tag covers/similar?q=...Semantic matching finds posts even without exact tag or keyword overlap.
Autocomplete or tag browsing/tags?q=...Tag index search, not semantic.

Example response:

{ "ok": true, "count": 3, "results": [ { "id": 216477, "source_url": "https://www.pixiv.net/artworks/...", "thumb_url": "https://lagoon.io/api/v1/thumb?id=216477", "title": "character (series) by artist", "posted_at": "2022-10-24 17:23:58+00", "platform": "pixiv", "platform_name": "Pixiv", "artist_handle": "76910634", "artist_name": "artist name", "deleted": false, "tags": ["1girl", "blue hair", "sword", "..."], "similarity": 0.8734 } ] }

The similarity field ranges from 0 to 1. Values above 0.8 indicate strong similarity; values above 0.9 typically mean the posts share most of the same tags and subject matter.

GET /api/v1/similar-artists Analytics

Find artists with a similar visual style. Each artist in the index has a pre-computed style vector derived from their body of work. Pass an artist handle to find stylistically similar artists, or a post ID to find artists whose overall style matches that post. Requires an API key (not available on the free tier). Billed at the analytics rate: 10 mills per call ($10/1,000 requests).

ParameterTypeDescription
handlestringArtist handle to find similar artists for. Mutually exclusive with id.
platformstringPlatform slug to disambiguate the handle (e.g. x, pixiv). Optional; without it, the handle with the most indexed posts is used.
idintegerPost ID. Finds artists whose style matches this specific post. Mutually exclusive with handle.
limitintegerMax results. Default 10, max 50.

Similar artists by handle

# Artists with a similar style to hews__ curl -H "X-API-Key: YOUR_KEY" \ "https://lagoon.io/api/v1/similar-artists?handle=hews__&limit=5"

Similar artists by post

# Artists whose overall style matches post 5995 curl -H "X-API-Key: YOUR_KEY" \ "https://lagoon.io/api/v1/similar-artists?id=5995&limit=5"

Response

{ "ok": true, "count": 3, "results": [ { "handle": "dondayo_", "platform": "x", "post_count": 102, "similarity": 0.9885, "also_on": [ {"handle": "amano_don", "platform": "pixiv"} ] } ] }
FieldTypeDescription
results[].handlestringPrimary handle for this artist (the one with the most indexed posts).
results[].platformstringPlatform slug for the primary handle.
results[].post_countintegerNumber of indexed posts for this handle.
results[].similaritynumberCosine similarity of style vectors (0 to 1). Higher means more stylistically similar.
results[].also_onarrayOther known handles for the same artist on other platforms. Omitted if none are linked.

When searching by handle, the source artist and all their linked handles are excluded from results. When searching by post ID, no artist is excluded.

GET /api/v1/thumb

Returns a scaled thumbnail image for a post. The response is a WebP image (max 360px on the longest side), not JSON. Errors are still returned as JSON.

Thumbnail requests do not require an API key and do not count toward API rate limits or usage billing. They have a separate IP-based limit of 300 per minute.

ParameterTypeDescription
idintegerPost ID (required).

Every post object returned by /search, /post, /similar, and /random includes a thumb_url field pointing to this endpoint.

Responses include Cache-Control: public, max-age=86400 and Last-Modified headers. Conditional requests with If-Modified-Since return 304 when the thumbnail has not changed.

Returns 404 for posts that do not exist, have been removed at source, or have no cached thumbnail.

Example:

# Fetch a thumbnail curl "https://lagoon.io/api/v1/thumb?id=5995" -o thumb.webp # Use in HTML <img src="https://lagoon.io/api/v1/thumb?id=5995" alt="Post thumbnail">

Errors

Every error returns "ok": false with an "error" message. Standard HTTP codes:

Example error response:

HTTP/1.1 429 Too Many Requests Retry-After: 42 { "ok": false, "error": "Rate limit exceeded. Max 30 requests per 60s." }

CORS

Every endpoint allows * origins and responds to OPTIONS preflight, so you can call from the browser without a proxy.