A public JSON API for 2,587 vibe-coded products — real apps, tools and SaaS built with AI, ranked by community upvotes. No API key. No signup. No rate-limit dance. CORS is open, so you can call it straight from browser JavaScript.
*
Format: JSON
Method: GET
Cost: free
Counts read live from the database when this page renders. 93% of the 2,586 listings we have probed are still online.
Base URL is https://vibeking.fun. Everything is a GET. There is nothing to configure:
curl -s "https://vibeking.fun/api/products?limit=5"
Press "Run it" to fetch a real response from this page.
GET /api/products — structured JSONThe one you want. Returns approved products as structured JSON, ordered by upvotes descending, wrapped in an envelope that also carries the directory total and the liveness aggregate.
| Param | Type | Default | Notes |
|---|---|---|---|
limit | 1–500 or all | 500 | Rows per response. Out-of-range values clamp to 500; all returns the entire directory (~3 MB). |
offset | integer | 0 | Rows to skip. Page with offset += count until offset >= total. |
{
"success": true,
"total": 2587, // approved products in the directory
"count": 1, // rows in this response
"offset": 0,
"liveness": { "live": 2408, "at_risk": 178, "dead": 0, "unchecked": 1 },
"data": [
{
"id": 3527,
"name": "APIDot",
"tagline": "Unified AI API Platform for Image, Video, Music, and Chat Models",
"description": "APIDot is a unified AI API platform built for developers ...",
"url": "https://apidot.ai/",
"maker": "APIDot",
"ai_tool": "",
"category": "AI Coding",
"upvotes": 951,
"created_at": "2026-01-12 02:20:34",
"logo_data": "https://submitsaas.com/images/product-icon-ec57....jpg",
"live_status": "live",
"last_checked": "2026-08-05 12:00:54",
"fail_count": 0
}
]
}
Envelope fields: success, total (whole directory, not this page), count (rows returned), offset, liveness, and data. Each row also carries maker, maker_x, use_cases, audience, alternative_to and approved — the optional metadata fields are often empty strings or null, so code defensively.
Categories are data, not a fixed enum. Derive the current set from the dump rather than hardcoding it.
GET /api/list — paginated rendered rowsOne page of the directory as a pre-rendered HTML fragment plus pagination metadata. This is what the site's own “load more” uses. Handy if you want VibeKing's row markup verbatim; use /api/products if you want fields.
| Param | Type | Default | Notes |
|---|---|---|---|
sort | trending | new | trending | Upvotes descending, or newest first. |
category | string | — | Exact category label, case-insensitive (e.g. Game, AI Coding). Omit or pass All for everything. An unknown category returns zero rows, not an error. |
page | integer | 1 | 30 products per page. |
curl -s "https://vibeking.fun/api/list?category=Game&page=1"
{
"html": "\n <div class=\"product-row\">...</div>\n",
"count": 15,
"page": 1,
"total": 15,
"hasMore": false
}
GET /api/search — keyword searchMatches name, tagline and description. Same response shape as /api/list, plus the echoed query. Also pre-rendered rows.
| Param | Type | Default | Notes |
|---|---|---|---|
q | string | — | Query, truncated to 80 characters. Empty or missing q returns an empty result set with HTTP 200. |
page | integer | 1 | 30 results per page. |
curl -s "https://vibeking.fun/api/search?q=todo"
{
"html": "\n <div class=\"product-row\">...</div>\n",
"count": 5,
"page": 1,
"total": 5,
"hasMore": false,
"q": "todo"
}
Exact name matches rank first, then prefix matches, then everything else by upvotes. Nothing found is 200 with count: 0 — check count, not the status code.
GET /badge/{id}.svg — live badgeA live “Featured on VibeKing” SVG for any approved product, rendered from current data and cached for an hour. An unknown or unapproved id returns 404 — badges cannot be minted for products that are not really listed.
| Param | Type | Default | Notes |
|---|---|---|---|
theme | dark | light | dark | Badge colour scheme. |
style | count | rank | count | count shows upvotes; rank shows the product's position inside its category. |
<a href="https://vibeking.fun/product/3527" target="_blank" rel="noopener">
<img src="https://vibeking.fun/badge/3527.svg" alt="Featured on VibeKing" width="250" height="60">
</a>
The badge is the only endpoint without a CORS header — <img> embeds do not need one, but a cross-origin fetch() of the SVG will be blocked. Makers who display the badge on their own site unlock a followed link on their listing; see Verified Makers.
Directories rot. Links break, projects get abandoned, domains lapse, and most directories keep serving the corpses because nobody checks. VibeKing re-probes every listed URL on a schedule and every /api/products response ships the aggregate:
"liveness": { "live": 2408, "at_risk": 178, "dead": 0, "unchecked": 1 }
Per-row you also get live_status (live, at_risk, dead, or null when it has not been probed yet), last_checked, and fail_count. Bot-blocking responses (401, 403, 405, 429, 451) are deliberately not counted as failures, because a site that blocks our probe is not a dead site.
If you are building anything on top of a directory — a dataset, a newsletter, a leaderboard — that field is the difference between real numbers and inherited rot.
# The whole envelope, first 5 products
curl -s "https://vibeking.fun/api/products?limit=5"
# Page 2 of the newest submissions (rendered rows)
curl -s "https://vibeking.fun/api/list?sort=new&page=2"
# Search
curl -s "https://vibeking.fun/api/search?q=todo"
# A product badge
curl -s "https://vibeking.fun/badge/3527.svg?theme=light&style=rank"
// Top 10 vibe-coded products by upvotes. Runs in the browser too — CORS is open.
const res = await fetch('https://vibeking.fun/api/products?limit=10');
const { total, liveness, data } = await res.json();
console.log(`${total} products indexed, ${liveness.live} confirmed live`);
for (const p of data) {
console.log(`${p.upvotes} ${p.name} — ${p.url}`);
}
import requests
# Page through the whole directory 500 at a time.
products, offset = [], 0
while True:
r = requests.get("https://vibeking.fun/api/products",
params={"limit": 500, "offset": offset}, timeout=30)
body = r.json()
products += body["data"]
offset += body["count"]
if offset >= body["total"] or body["count"] == 0:
break
print(len(products), "products")
print(sorted({p["category"] for p in products}))
Machine-readable specs live at /openapi.json, /.well-known/ai-plugin.json, /llms.txt and /llms-full.txt.
Two public repos back this API — docs and runnable clients in one, a regenerated dataset in the other.
?limit=all is roughly 3 MB; once an hour is plenty.limit + offset instead of pulling the whole dump on every request.Cache-Control: public, max-age=3600 — do not poll them./product/{id}.No. The VibeKing API is free and public. There is no key, no signup, no OAuth and no rate-limit header. Every documented endpoint is a plain GET request that works from curl, a server, or browser JavaScript.
Yes. Every /api/* endpoint sends Access-Control-Allow-Origin: *, so a cross-origin fetch() from any site works without a proxy. The one exception is the badge SVG at /badge/{id}.svg, which sends no CORS header — it is meant for <img> embeds, which do not need one.
The directory currently holds 2,587 approved products. /api/products returns 500 per request by default; pass ?limit and ?offset to page through it, or ?limit=all for the whole dump in one response. Please cache it — the data does not change faster than hourly.
VibeKing periodically re-checks whether every listed product URL still resolves, and every /api/products response carries an aggregate count of how many listings are live, at risk, dead, or not yet checked. Right now that is 2,408 live, 178 at risk, 0 dead and 1 not yet probed. Most directories never tell you how much of their index has rotted; this one does.
Yes. The API is free to use, including commercially. Product names, taglines, logos and descriptions belong to the makers who submitted them, so credit the makers and link back to the listing at vibeking.fun/product/{id} where you display them.
Built something with AI? Add it to the directory — free, and it shows up in this API within minutes.