Neeed Directory
Search curated software and digital products, compare tools, and manage your own Neeed Directory listings through OAuth. Public catalog tools need no API key.
Hosted MCP Server
npx add-mcp 'https://neeed.directory/api/mcp'Installs into Claude Code, Codex, Cursor and more
Documentation
API and MCP
Read the Neeed Directory catalog from your own code or an AI assistant: a JSON API described by an OpenAPI document, an MCP server and RSS feeds. The API, the feeds and the read tools need no key and no sign-in.
Usage
Calls the API and the MCP server answered over the last 30 days, counted as they are served. The counts refresh every six hours.
39,560
Calls, last 30 days
39,430
Over HTTP
130
Over MCP
getProduct
Most called
Calls per day
Quick start
Every endpoint is a GET that answers JSON and allows cross-origin requests, so a browser, a script or a spreadsheet can call it as is. Only live listings are returned.
curl "https://neeed.directory/api/v1/products?limit=5"
curl "https://neeed.directory/api/v1/search?q=analytics&limit=5"
curl "https://neeed.directory/api/v1/categories"
To read one listing, replace {slug} with the slug of a listing from those answers:
curl "https://neeed.directory/api/v1/products/{slug}"
Endpoints
GET/api/v1/searchSearch or list products
Live listings, newest first. Narrow them with a full-text query and by category, label or tag; every filter is optional.
Parameters
| Name | Type | Description |
|---|---|---|
| qoptional | stringquery | Full-text query over name, tagline and description. 2 to 100 characters |
| categoryoptional | stringquery | Category slug |
| labeloptional | stringquery | Label slug |
| tagoptional | stringquery | Tag slug |
| pageoptional | integerquery | Page number. 1 to 500, default 1 |
| limitoptional | integerquery | Products per page. 1 to 50, default 20 |
Response
Errors 400 429 500 What they mean
GET/api/v1/products/{slug}Get a product
One live listing by slug. A listing that is not live is a 404.
Parameters
| Name | Type | Description |
|---|---|---|
| slug | stringpath | The slug in the listing's URL. At most 200 characters |
Response
A Product.
Errors 400 404 429 500 What they mean
GET/api/v1/categoriesList categories
Every category with the number of live listings it holds, by name.
Response
A Categories.
Errors 429 500 What they mean
GET/api/v1/productsList product cards
Approved products as plain-text cards, newest first, with optional query, category, label and tag filters.
Parameters
| Name | Type | Description |
|---|---|---|
| qoptional | stringquery | Full-text query over name, tagline and description. 2 to 100 characters |
| categoryoptional | stringquery | Category slug |
| labeloptional | stringquery | Label slug |
| tagoptional | stringquery | Tag slug |
| pageoptional | integerquery | Page number. 1 to 500, default 1 |
| limitoptional | integerquery | Products per page. 1 to 50, default 20 |
Response
A ProductCards.
Errors 400 429 500 What they mean
Objects
Product
| Name | Type | Description |
|---|---|---|
| id | string (uuid) | |
| slug | string | |
| name | string | |
| punchline | string | The one-line tagline |
| description | string | HTML, as the listing page renders it |
| url | string (uri) | The listing on Neeed Directory |
| website | string | The product's own site |
| logo | string (uri) or null | |
| cover | string (uri) or null | |
| category | string or null | Category slug |
| labels | string[] | Label slugs |
| tags | string[] | Tag slugs |
| product_type | "software" | "digital_product" or null | |
| pricing_model | "free" | "freemium" | "paid" | "open_source" or null | |
| string or null | X handle, without the @ | |
| featured | boolean | |
| listed_at | string (date-time) or null | When the listing went live |
| highlights | string[] | Premium profile: what it does, one fact per line |
| ideal_for | string[] | Premium profile: who it is for |
| faq | FaqItem [] | Premium profile: questions answered from the product's site |
| promo | Promo or null | The maker's promo code for visitors, or null |
FaqItem
| Name | Type | Description |
|---|---|---|
| question | string | |
| answer | string |
Promo
| Name | Type | Description |
|---|---|---|
| code | string | |
| discount | string | The offer, in the maker's words: "20% off" |
| url | string (uri) or null | Where to redeem it; null means the product's own site |
| expires_at | string (date) or null | Last valid day, yyyy-mm-dd; null means open-ended |
Category
| Name | Type | Description |
|---|---|---|
| slug | string | |
| name | string | |
| noun | string | What the directory calls its listings, e.g. "AI agents" |
| url | string (uri) | |
| count | integer | Live listings in the category |
ProductCard
| Name | Type | Description |
|---|---|---|
| slug | string | |
| name | string | |
| punchline | string | The one-line tagline |
| url | string (uri) | The listing on Neeed Directory |
| website | string | The product's own site |
| logo | string (uri) or null | |
| category | string or null | Category slug |
| pricing_model | "free" | "freemium" | "paid" | "open_source" or null | |
| product_type | "software" | "digital_product" or null | |
| featured | boolean | |
| description | string | A plain-text description |
ProductSearch
| Name | Type | Description |
|---|---|---|
| products | Product [] | |
| total | integer | Matches across every page |
| page | integer | |
| limit | integer |
Categories
| Name | Type | Description |
|---|---|---|
| categories | Category [] |
ProductCards
| Name | Type | Description |
|---|---|---|
| products | ProductCard [] | |
| total | integer | Matches across every page |
| page | integer | |
| limit | integer |
Errors and limits
An error is a JSON object with a code and a message. A 400 adds the rejected parameters under data.issues. A 429 adds data.retryAfter and a Retry-After header, both in seconds.
{ "code": "NOT_FOUND", "message": "No live listing has this slug." }
400
Invalid input, with the rejected parameters under data.issues
404
Nothing exposed matches
429
Rate limit exceeded; Retry-After and data.retryAfter give the seconds to wait
500
The catalog could not be read; try again shortly
Requests are counted per IP: 30 searches a minute and 60 other calls a minute, shared by the API and the MCP server's read tools. A signed-in assistant can call each tool that reads the account 30 times a minute and each tool that changes it 10 times every 10 minutes. On top of that, check_badge shares the dashboard's Verify budget of 10 checks an hour, and upgrade_listing opens at most 10 checkout links an hour.
Until the window resets, a REST call past a limit answers HTTP 429 with data.retryAfter and a Retry-After header, and an MCP call answers an error tool result (isError) whose text says how long to wait.
Answers can be cached at the edge for a few minutes, so a change to a listing can take that long to show.
MCP server
The same reads as tools, plus a tool that drafts a listing and, once a maker signs in, tools that manage their listings, for any Model Context Protocol client. Streamable HTTP, stateless, no session. The read tools and submit_product need no key and no sign-in.
Clients configured in JSON (key names vary by client)
{ "mcpServers": { "neeed-directory": { "type": "http", "url": "https://neeed.directory/api/mcp" } } }
Apps and CLIs that add a server by URL
Add a remote MCP server (Streamable HTTP) with the server URL, as a custom connector or with the client's add command. The read tools need no authentication; you sign in only when a tool needs your account.
https://neeed.directory/api/mcp
Clients that only run local (stdio) servers
npx -y mcp-remote https://neeed.directory/api/mcp
Tools
| Tool | What it does |
|---|---|
search Read only, no sign-in | Search products. Search approved products on Neeed Directory, newest first. Returns at most 20 results with id, title and url. The id is the product slug; pass it to fetch for a plain-text card. Queries shorter than two characters return no results. readOnlyHint: true destructiveHint: false idempotentHint: true openWorldHint: false |
fetch Read only, no sign-in | Fetch product. One approved product on Neeed Directory by id, the slug from search or list_products. Returns its title, plain-text card, url and metadata including its website, category and pricing model. readOnlyHint: true destructiveHint: false idempotentHint: true openWorldHint: false |
list_categories Read only, no sign-in | List categories. Every category on Neeed Directory with its slug, name, approved product count and url. Pass a category slug to list_products to read its product cards. readOnlyHint: true destructiveHint: false idempotentHint: true openWorldHint: false |
list_products Read only, no sign-in | List products. Approved product cards on Neeed Directory, newest first, with optional query, category, label and tag filters. Returns at most 50 cards per page with a total count. Each slug is an id that fetch accepts. readOnlyHint: true destructiveHint: false idempotentHint: true openWorldHint: false |
submit_product Sign-in optional listings:write (Edit your listings) offline_access (Stay connected between visits) | Submit a product. Without sign-in, saves a private draft listing on Neeed Directory and returns a claim link: nothing is published or charged until the maker opens it, signs in and submits. Signed in with permission to edit listings, submits the listing to the maker's account: Neeed's automatic check publishes it or holds it for review, and Premium returns a Stripe Checkout link the maker pays in the browser. readOnlyHint: false destructiveHint: false idempotentHint: false openWorldHint: true |
my_listings Needs sign-in listings:read (See your listings) offline_access (Stay connected between visits) | My listings. Your listings on Neeed Directory, newest first: status, plan, badge, the admin's feedback when there is any, and the page to open. Pass a slug to get one. When next_page is a number, call again with that page for older listings. readOnlyHint: true destructiveHint: false idempotentHint: true openWorldHint: false |
check_badge Needs sign-in listings:read (See your listings) offline_access (Stay connected between visits) | Check my badge. Looks for the Neeed badge on one of your Free listings' sites now and answers found, missing or inconclusive, with the markup to add. It records nothing: the maker presses Verify on the listing's edit page to make the link dofollow or bring a hidden listing back. readOnlyHint: true destructiveHint: false idempotentHint: true openWorldHint: true |
update_listing Needs sign-in listings:write (Edit your listings) offline_access (Stay connected between visits) | Update a listing. Edits one of your listings: name, punchline, description, category, pricing model or Twitter handle, under the dashboard's rules. Editing a listing that is not live sends it back for review; a live listing stays live. A rejected listing cannot be edited. readOnlyHint: false destructiveHint: true idempotentHint: true openWorldHint: false |
upgrade_listing Needs sign-in listings:write (Edit your listings) offline_access (Stay connected between visits) | Upgrade to Premium. Returns a Stripe Checkout link that upgrades one of your live Free listings to Premium, with the price. Nothing is charged until the maker opens the link and pays in the browser. readOnlyHint: false destructiveHint: true idempotentHint: false openWorldHint: true |
| Prompt | Description |
|---|---|
| submit_to_neeed | Drafts a Neeed listing from a product's website, has the maker approve it, and submits it with submit_product. Argument: website. |
Sign in to manage your listings
search, fetch, list_categories, list_products, submit_product work without signing in. my_listings, check_badge, update_listing, upgrade_listing need a signed-in maker.
The assistant signs in with OAuth. Neeed Directory shows which app is asking and what it may do, and nothing is shared until the maker approves.
MCP clients that support OAuth sign in with a client metadata document or by registering themselves the first time they connect. The approval page names a self-registered app as unverified, since its name comes from the app.
listings:read(See your listings): Needed bymy_listings,check_badge. The assistant sees the account's listings with their status, plan, review feedback and badge status.listings:write(Edit your listings): Needed byupdate_listing,upgrade_listing. With it,submit_productsubmits the listing straight to the account. Without it, the tool saves a draft with a claim link.offline_access(Stay connected between visits): Lets the assistant renew its access without asking again.
Access tokens last one hour. Refresh tokens last 30 days, are replaced on every use and exist only with "Stay connected between visits".
A connected assistant can change a listing's name, punchline, description, category, pricing model and X handle under the dashboard's review rules, create a listing through submit_product, and get a Checkout link for Premium. It cannot pay, publish without the automatic check, see other makers' listings, or read the account's email address, billing details or password. Payment always happens in the browser on Stripe.
To disconnect an assistant, open Settings, then Connected apps, and choose Disconnect. Access stops at once.
After a disconnect, an assistant that still holds its expired sign-in is refused on every call, reads included, until the maker signs in again or clears the sign-in stored in the assistant.
Submitting through MCP
Without sign-in, submit_product saves a private draft and returns a claim link. Nothing is published or charged until the maker opens the link, signs in and submits through the normal form at https://neeed.directory/submit, and every listing then goes through the automatic check.
Signed in with permission to edit listings, it submits the listing to the maker's account through the same automatic check and answers live or in_review. It answers already_listed for a site that is live on Neeed Directory, already_submitted for a site the account already submitted, and a claim link when the site offers no usable cover image or icon. Premium returns a Stripe Checkout link the maker pays in the browser.
The claim link works once and expires in 7 days. Without sign-in, each IP can save 5 drafts in any 24 hours; signed in, each account can save 5. Both sit under an overall cap over the same window, so a refused call succeeds once earlier drafts are 24 hours old.
submit_product takes
| Name | Type | Description |
|---|---|---|
| website | string | The product's website, https. At most 2048 characters |
| name | string | The product's name. At most 80 characters |
| punchline | string | One line on what it does. At most 200 characters |
| description | string | What it does and for whom, factual, in plain text. The 1000-character limit counts &, < and > as several characters each |
| category | string | One category slug from list_categories. At most 100 characters |
| product_type | "software" | "digital_product" | software, or digital_product for templates, courses, ebooks and the like |
| pricing_model | "free" | "freemium" | "paid" | "open_source" | How the maker charges for it |
| planoptional | "free" | "premium" | The plan the maker wants; nothing is charged until they submit. Default "free" |
Feeds
Neeed Directory: new listings
The 50 newest listings, across every category.
Neeed Directory: the blog
Category guides and Neeed Weekly, the editorial recap of what arrived on the directory each week.
Category feeds
/categories/{slug}/feed.xml
The listings feed narrowed to one category.
The data is the public catalog: when you show a listing elsewhere, link to its page. Rate limits apply and can change. Use of the API, the MCP server and the feeds falls under the Terms of Service. Questions or a higher limit? Write to us.