Developer Docs
TypeScript SDK · REST API · Model Selection · MCP Server · FNI Badge
All endpoints are free, no authentication required
Quickstart
One end-to-end flow, runnable with no internal knowledge and no API key. The canonical workflow is always the same:
- Search for what you need (
/api/v1/search). - Take the ids from the returned results β never hard-code a catalog id.
- Inspect the evidence for a candidate via its entity response (FNI factors + specs).
- Compare two or more candidates (
/api/v1/compare). - You (the caller/agent) make the final decision. Free2AI returns evidence; it does not choose for you.
Note for REST users: there is no separate REST /explain endpoint. Evidence inspection is the entity response itself β its returned FNI factors and associated notes. (MCP clients have a free2aitools_explain tool; see below.)
Use the TypeScript SDK
Typed TypeScript client (@free2aitools/sdk) that wraps the same REST API β TypeScript types, retries, and typed errors out of the box. See the SDK quickstart below.
Use the REST API
For scripts, apps, and direct HTTP integrations. Plain JSON over HTTPS β curl, Node, Python, anything, with no additional dependency.
Use the MCP server
For MCP-compatible agents and clients (Claude, Cursor, Windsurf, etc.). Same data, exposed as an Agent/tool protocol.
Use the SDK, REST, or MCP according to your integration environment; all three serve the same catalog and the same evidence. None of them route, rank-by-preference, or decide which model you should use. The SDK is a typed REST client, so it follows the REST defaults. Note: the surfaces use different default result limits β MCP search/rank default to 10 results, REST (and therefore the SDK) defaults to 5; all clamp to a max of 20. Pass an explicit limit if you need a specific count.
Official TypeScript SDK
Initial public release — version 0.1.0, available on npm. A typed client for the existing Free2AI public API (same catalog, same evidence); the REST API and MCP server remain fully supported alternatives.
npm install @free2aitools/sdk import { Free2AIClient } from "@free2aitools/sdk";
// No authentication is currently required for the public API.
const client = new Free2AIClient();
// search() returns a typed SearchResponse; `results` is SearchResult[].
const res = await client.search({ q: "code generation", limit: 5 });
for (const r of res.results) {
console.log(r.name, r.fni_score); // real, typed fields
}
// Free2AITools provides structured discovery and evidence;
// the caller or Agent makes the final decision.
No authentication is currently required for the public API. The SDK is a typed REST client, so it follows the REST defaults (search returns 5 results unless you pass limit). Package: @free2aitools/sdk.
curl (bash)
Requires jq. Set F2AI_BASE to override the base URL.
#!/usr/bin/env bash
# Canonical workflow: search -> derive ids from results -> inspect entity ->
# compare candidates -> the CALLER decides. No id is hard-coded; all ids come
# from the search response. Prerequisite: jq (https://jqlang.github.io/jq/).
set -euo pipefail
BASE="${F2AI_BASE:-https://free2aitools.com}"
# 1) Search. --fail-with-body surfaces 4xx/5xx; we branch on the status code.
http_code=$(curl -sS -o /tmp/f2ai_search.json -w '%{http_code}' \
"$BASE/api/v1/search?q=code+generation&limit=5") || true
case "$http_code" in
200) ;; # ok
400) echo "bad request (400) - fix the query"; exit 1 ;;
404) echo "not found (404)"; exit 1 ;;
429|503) echo "transient ($http_code) - retry after Retry-After seconds"; exit 75 ;;
*) echo "server error ($http_code)"; exit 1 ;;
esac
# 2) Derive ids from the result set. Fail honestly if fewer than 2 are returned
# (do NOT silently fall through to an empty compare).
mapfile -t IDS < <(jq -r '.results[].id' /tmp/f2ai_search.json)
if [ "${#IDS[@]}" -lt 2 ]; then
echo "search returned ${#IDS[@]} result(s); need >= 2 to compare. Stopping."
exit 1
fi
# 3) Inspect one candidate (evidence is in the entity response: FNI factors +
# specs). REST has no separate /explain endpoint. URL-encode '/' in the id.
ID_ENC=$(printf '%s' "${IDS[0]}" | jq -sRr @uri)
curl -sS "$BASE/api/v1/entity/$ID_ENC" | jq '.entity.fni.factors'
# 4) Compare the first two candidates side by side.
curl -sS "$BASE/api/v1/compare?ids=${IDS[0]},${IDS[1]}" | jq '.'
# 5) The caller/agent makes the final selection from this evidence.
echo "Review the factors above and choose. Free2AI does not decide for you." JavaScript / TypeScript (Node 18+)
Built-in fetch, no dependencies.
// Node 18+ (built-in fetch) β the direct REST path, no additional dependency.
// You can also use the official TypeScript SDK: npm install @free2aitools/sdk (see the SDK section above).
// Canonical workflow: search -> ids from results -> entity (evidence) ->
// compare -> the caller decides. Retries ONLY 429/503, max 2, honors Retry-After.
const BASE = process.env.F2AI_BASE || "https://free2aitools.com";
async function call(path, { timeoutMs = 10000 } = {}) {
let attempt = 0;
for (;;) {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
let res;
try {
res = await fetch(BASE + path, { signal: ctrl.signal });
} finally {
clearTimeout(timer);
}
// Status check BEFORE parsing the body.
if (res.status === 400 || res.status === 404) {
throw new Error(`non-retryable ${res.status} for ${path}`);
}
if ((res.status === 429 || res.status === 503) && attempt < 2) {
const ra = Number(res.headers.get("retry-after"));
const delay = Number.isFinite(ra) && ra > 0 ? ra * 1000 : 500 * (attempt + 1);
await new Promise((r) => setTimeout(r, Math.min(delay, 5000)));
attempt++;
continue;
}
if (!res.ok) throw new Error(`request failed ${res.status} for ${path}`);
return res.json();
}
}
export async function pickCandidates(query = "code generation") {
const search = await call(`/api/v1/search?q=${encodeURIComponent(query)}&limit=5`);
const results = Array.isArray(search.results) ? search.results : [];
if (results.length < 2) {
throw new Error(`need >= 2 results to compare; got ${results.length}`);
}
// Use the ids the server returned. Preserve null (not-measured) vs 0 (measured).
const ids = results.map((r) => r.id);
const entity = await call(`/api/v1/entity/${encodeURIComponent(ids[0])}`);
const factors = entity?.entity?.fni?.factors ?? null; // may be null; no fabrication
const comparison = await call(`/api/v1/compare?ids=${ids[0]},${ids[1]}`);
// The caller/agent reasons over this evidence and makes the final choice.
return { ids, factors, comparison };
} Python (requests)
# python -m pip install requests
# Any standards-compliant HTTP client may be used.
# Canonical workflow: search -> ids from results -> entity (evidence) ->
# compare -> the caller decides. Retries ONLY 429/503, max 2, honors Retry-After.
import os
import time
import requests
BASE = os.environ.get("F2AI_BASE", "https://free2aitools.com")
def call(path, timeout=(5, 10)):
attempt = 0
while True:
resp = requests.get(BASE + path, timeout=timeout)
if resp.status_code in (400, 404):
resp.raise_for_status() # non-retryable client error
if resp.status_code in (429, 503) and attempt < 2:
ra = resp.headers.get("Retry-After")
try:
delay = float(ra) if ra is not None else 0.5 * (attempt + 1)
except ValueError:
delay = 0.5 * (attempt + 1)
time.sleep(min(delay, 5.0))
attempt += 1
continue
resp.raise_for_status() # raises on remaining 5xx; no indefinite retry
return resp.json()
def pick_candidates(query="code generation"):
search = call(f"/api/v1/search?q={requests.utils.quote(query)}&limit=5")
results = search.get("results") or []
if len(results) < 2:
raise RuntimeError(f"need >= 2 results to compare; got {len(results)}")
ids = [r.get("id") for r in results] # use server-returned ids
entity = call(f"/api/v1/entity/{requests.utils.quote(ids[0], safe='')}")
# null-safe: factors may be missing/None; never fabricate a default.
factors = (entity.get("entity") or {}).get("fni", {}).get("factors")
comparison = call(f"/api/v1/compare?ids={ids[0]},{ids[1]}")
# The caller/agent reasons over this evidence and makes the final choice.
return {"ids": ids, "factors": factors, "comparison": comparison} /openapi.json. Point a code generator or an agent at it to consume the API contract directly.
REST API
GET /api/v1/search
Search AI models, tools, datasets, and papers. Search results are ordered by a relevance score based on the FNI and, where term-match data is available, how well the entry matches the query. The score used for ordering may differ from the fni_score field returned in the response. The result set is bounded.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| q | string | - | Search query (required) |
| limit | number | 5 | Maximum results per request (1β20) |
| type | string | all |
Filter by entity type. Canonical values: model, tool, dataset, paper.
Common source-prefixed aliases also accepted: hf-model, gh-model, arxiv-paper, gh-tool, etc. (auto-mapped to canonical).
|
Example
curl "https://free2aitools.com/api/v1/search?q=code+generation&limit=2" Response
{
"version": "fni_v2.0",
"results": [
{
"id": "<id-from-result-1>",
"name": "<name>",
"type": "model",
"fni_score": 87.2
},
{
"id": "<id-from-result-2>",
"name": "<name>",
"type": "model",
"fni_score": 83.5
}
],
"meta": {
"elapsed_ms": 42,
"total": 2
}
}
Search may return a retryable transient 503 under cold-path or fallback budget limits. Retry according to the Retry-After header.
Pagination: page is 1-based and defaults to 1; combine it with limit. total_count is not the total number of matches and must not be used to estimate remaining pages. The dataset refreshes daily, so results may change between requests; no cursor or snapshot consistency is promised.
POST /api/v1/select
Filter the catalog by declared metadata; returns FNI-ranked entries. Constraints are metadata/heuristic filters, not verified compatibility analysis β the caller is responsible for final model selection.
Request Body (JSON)
{
"task": "text-generation",
"constraints": {
"max_vram_gb": 24,
"license": "commercial"
},
"limit": 5
} Constraints (all optional)
| Field | Type | Description |
|---|---|---|
| task | string | Task name or alias ("llm", "code", "embeddings") |
| max_vram_gb | number | Maximum VRAM in GB |
| max_params_b | number | Maximum parameters in billions |
| license | string | "commercial", "apache-2.0", "mit", or "any" |
| min_context_length | number | Minimum context window (tokens) |
| limit | number | Max results (1-20, default 5) |
curl
curl -X POST https://free2aitools.com/api/v1/select \
-H "Content-Type: application/json" \
-d '{"task":"text-generation","constraints":{"max_vram_gb":24}}' Response
{
"task_interpreted": "text-generation",
"entries": [
{
"rank": 1,
"model_id": "<model_id-from-response>",
"name": "<name>",
"fni_score": 49.3,
"params_billions": 20.87,
"vram_estimate_gb": 17,
"fni_factors": {
"semantic": null,
"semantic_note": "query-time baseline; scored live at search; not a per-entity value",
"authority": 0,
"popularity": 70.5,
"recency": 98.1,
"quality": 65
},
"fni_summary": "FNI 49.3 catalog entry; leading factor recency (98.1); 21B params."
}
]
} GET /api/v1/compare
Side-by-side model comparison with FNI factor decomposition.
Parameters
| Param | Type | Description |
|---|---|---|
| ids | string | Comma-separated entity IDs (2-25). Use model_id from the Select API or id from Search. |
curl (HF-native id form, internal form, or slug β all accepted)
# <ID_1>,<ID_2> = two id values taken from /api/v1/search results. curl "https://free2aitools.com/api/v1/compare?ids=<ID_1>,<ID_2>"
Obtain at least two ids from search first; see Quickstart for a runnable end-to-end flow.
GET /api/v1/entity/:id
Full structured metadata for a single entity. Use this after search to fetch the complete detail you need to make a decision: FNI factors, technical specs, VRAM estimates, license, links, relations.
Accepted id forms
Grammar templates only (<...> are placeholders, not live ids). In practice take the id or slug straight from a /api/v1/search result and pass it back unchanged.
- HuggingFace-native:
<author>/<name> - Bare name (auto-prefixes common sources):
<name> - Internal canonical:
hf-model--<author>--<name> - Slug form (the
slugfield of a search response):<author>--<name>
Case-insensitive. Lookup probes the matching shards in parallel and returns the first hit.
Query parameters
| Param | Default | Description |
|---|---|---|
| include | (empty) | Comma list. include=body adds the rendered README (can be up to 250KB). Default response is lean. |
Status codes
200β entity found404β no entity matches any candidate form (genuine miss; don't retry)503β all probed shards errored (transient infra; retry after a short delay)400 / 500β bad request / unexpected server error
curl (id template β substitute an id obtained from search)
# <ID_FROM_SEARCH> = the id field of a /api/v1/search result (URL-encode '/'). curl "https://free2aitools.com/api/v1/entity/<ID_FROM_SEARCH>"
For a copy-paste runnable version that derives the id automatically, see Quickstart below.
Response shape (lean default)
{
"version": "fni_v2.0",
"entity": {
"id": "<id-from-search>",
"slug": "<slug-from-search>",
"type": "model",
"name": "<name>",
"author": "<author>",
"fni": {
"score": 87.5,
"factors": {
"semantic": null,
"authority": 95,
"popularity": 88,
"recency": 85,
"quality": 80
}
},
"specs": {
"params_billions": 8,
"context_length": 8192,
"vram": {
"fp16_gb": 16
},
"ollama_compatible": true
},
"stats": {
"downloads": 1234567,
"last_modified": "..."
},
"links": {
"detail_url": "...",
"badge_url": "..."
},
"relations": {
"datasets_used": [],
"related": []
}
},
"meta": {
"elapsed_ms": 142,
"candidates_tried": 3
}
} Field semantics: 0 means measured-zero, null means not-measured. Treat them differently when scoring downstream.
/openapi.json (OpenAPI request/response contract), /llms.txt (plain markdown index per llmstxt.org), /.well-known/mcp.json (MCP manifest with tool catalog).
MCP Server
Free2AI exposes an MCP server so AI agents (Claude, Cursor, Windsurf, etc.) can discover and rank AI tools automatically.
free2aitools_search
Search AI tools, models, datasets, and papers. Search results are ordered by a relevance score based on the FNI and, where term-match data is available, how well the entry matches the query. The score used for ordering may differ from the fni_score field returned in the response. The result set is bounded.
free2aitools_rank
Keyword-search AI entities using the task text as query input. Returns matching catalog entries. Search results are ordered by a relevance score based on the FNI and, where term-match data is available, how well the entry matches the query. The score used for ordering may differ from the fni_score field returned in the response. The result set is bounded. Does not perform task-fit recommendation or compatibility analysis.
free2aitools_explain
Explain why a specific entity received its FNI ranking score with factor breakdown.
free2aitools_select_model
Filter the catalog by declared metadata; returns FNI-ranked entries with an optional per-entry fni_summary (factual FNI factor/spec facts). Constraints are metadata/heuristic filters, not verified compatibility analysis.
free2aitools_compare
Compare 2-25 AI models side-by-side with FNI factor decomposition.
free2aitools_search first, take the ids from its results, pass those ids to free2aitools_explain for the factor breakdown and to free2aitools_compare, then decide outside Free2AI. Do not pass invented ids; use the ones search returned.
Setup
Claude Code / Claude Desktop
Claude Code: add the remote server to .mcp.json (project) or ~/.claude.json (user). A remote entry needs an explicit type:
{
"mcpServers": {
"free2aitools": {
"type": "http",
"url": "https://free2aitools.com/api/mcp"
}
}
} Claude Desktop: add it as a Custom Connector — Settings > Connectors > Add custom connector — with the URL https://free2aitools.com/api/mcp. (The claude_desktop_config.json file is for local stdio servers; remote HTTP servers use Connectors.)
Cursor
Add the server to ~/.cursor/mcp.json (global) or .cursor/mcp.json (project). Cursor infers Streamable HTTP from the URL:
{
"mcpServers": {
"free2aitools": {
"url": "https://free2aitools.com/api/mcp"
}
}
} Windsurf
Go to Cascade > Plugins > Add MCP Server, enter:
URL: https://free2aitools.com/api/mcp
Transport: Streamable HTTP Any MCP Client / Auto-Discovery
Endpoint: POST https://free2aitools.com/api/mcp
Protocol: JSON-RPC 2.0 (MCP Spec 2025-03-26). Supports initialize, tools/list, tools/call.
Machine-readable server manifest: https://free2aitools.com/.well-known/mcp.json
Add Free2AITools to your Agent
Wire Free2AITools into an AI agent in one place. Your agent calls the tools; Free2AITools returns FNI-ranked candidates and their evidence, and the agent (or the person using it) makes the final decision. Free2AITools does not route, rank-by-preference, select, or decide on the caller's behalf.
- Connect over MCP. Point your MCP client at
POST https://free2aitools.com/api/mcpusing the setup snippets above (Claude Code, Claude Desktop, Cursor, or any MCP client). The tool catalog is described at https://free2aitools.com/.well-known/mcp.json. No API key is required. - Or call REST / the SDK directly. Agents that are not MCP-based can use the REST API or the TypeScript SDK (
@free2aitools/sdk) — same catalog, same evidence. - Follow the search-first flow. Call
free2aitools_search(orGET /api/v1/search), take the ids from the results, pass them tofree2aitools_explain/free2aitools_compare, then let the agent reason over the evidence and choose. Do not pass invented ids; use the ones search returned.
FNI Badge
Embed a live FNI score badge in your README, docs, or website. The badge updates automatically as scores change.
Endpoint
GET https://free2aitools.com/api/v1/badge/{umid} Returns an SVG image. Color-coded: green (90+), blue (70+), yellow (50+), red (<50). Cached 1 hour at CDN edge.
Markdown (README)
 HTML
<img src="https://free2aitools.com/api/v1/badge/YOUR_UMID" alt="FNI Score" /> id returned by search as the canonical entity identifier for entity, compare, and badge requests. The response also exposes canonical_id, which has the same value. UMID is a separate derived 16-character hexadecimal digest of the canonical ID; callers do not need to compute it for these endpoints.
Open Data
For bulk access and offline analysis, download FNI rankings as Apache Parquet files. Compatible with DuckDB, Pandas, Spark, and any columnar data tool.
View Open Data DownloadsOpen-model metadata normalization
Free2AITools indexes open-model metadata from public sources. The example below documents the currently audited Hugging Face model path and shows how selected source fields become a Free2AITools record.
One Free2AITools record represents one source-qualified source record. Canonical identity includes the source namespace. The UMID is derived from that canonical identity. Cross-source entity fusion is not implemented, and records from different source namespaces are not silently declared identical.
Records retain source-level attribution and source-qualified identity. The current public output does not expose complete per-field provenance or confidence for every normalized, derived, estimated or projected value.
Ingestion and normalization are automated. Schema and normalization rules are manually governed, and systemic defects may be repaired through uniform rule changes rather than per-model edits. No manual per-model field editing was found in the audited path.
Under the project governance contract, sponsor support cannot alter producer-side metadata, normalization, evidence values or FNI ranking. No sponsor-controlled input or multiplier was found in the audited producer path.
Free2AITools provides source-attributed and source-qualified normalization, with known projection losses in the current implementation. Some fields are retained, some are normalized, some are derived or estimated, and some are currently lost during projection. This should not be read as complete retention or independent verification.
FNI is a structured evidence signal, not truth and not a final choice. The caller decides. On public search responses, fni_s is currently null and carries the shared evidence note because genuine query-time semantic/ANN ranking is not currently provided. Numeric FNI values are not model-quality certification.
Hugging Face safetensors.total is a source-reported parameter count, not file bytes. When that source is used, params_billions is the source-reported value divided by 1e9. It is not independently verified. A name-based fallback, where used, is estimated. The selected parameter-count source and its confidence are not yet represented publicly.
REST, MCP and @free2aitools/sdk are alternative consumption surfaces. MCP is optional and is not the product identity. Current surfaces may differ in field names, nullability and types; this documentation does not promise byte-identical parity.
Free2AITools does not claim cross-source fusion, independent verification, per-field certification, external adoption, endorsement or partnership.
Known limitations (open at observation time)
These are current audit references, not a stable public API contract, and none are resolved. Each is self-documented in the JSON asset below.
- G1 β A single canonical ID may currently resolve to inconsistent duplicate records across the entity and compare surfaces.
- G3 β The served last_modified currently reflects Free2AITools projection/harvest timing rather than reliably preserving the upstream source last-modified value.
- G4 β Hugging Face likes are not exposed as a served field, and stars/forks may be served as 0 where null would be more honest.
- G5 β The published SDK has known nullability and percentile-type drift.
- G6 β Some search fields currently use 0 where entity surfaces use null.
- G7 β Some context, VRAM, local-run, category or architecture values may be derived or estimated rather than directly reported.
- G14 β Parameter-count source selection and confidence are not yet represented publicly.
Observed transformation example
Illustrative excerpt (openai/whisper-large-v3), re-probed at authoring time. Values may vary slightly between observations. The full bounded example, with a per-field classification table, is published as a static asset: /data/model-metadata-normalization-example.json.
{
"model": "openai/whisper-large-v3",
"source_excerpt": {
"safetensors_total_parameter_count": 1543490560,
"cardData_license": "apache-2.0",
"likes": 5935,
"downloads": 5963543
},
"normalized_excerpt": {
"id": "hf-model--openai--whisper-large-v3",
"license": "Apache-2.0",
"specs": {
"params_billions": 1.54
},
"fni": {
"score": 39.6
}
}
} FNI Score
Every entity is ranked by the Free2AITools Nexus Index (FNI) β a composite score from 0 to 99.9 based on five factors:
Trust, Versions & Lifecycle
Security contact
Report a security issue via the GitHub repository's security advisories, or open a GitHub issue. A machine-readable copy is published at /.well-known/security.txt (RFC 9116).
Version domains (distinct, not a single version)
Free2AItools exposes several independent components, each with its own version domain. These are distinct and are not kept in numeric equality β do not assume one number applies to another.
- SDK package (
@free2aitools/sdk):0.1.0 - MCP server:
2.0.1(manifest at/.well-known/mcp.json) - OpenAPI document:
2.0.0(served at/openapi.json) β independent of the MCP server version above - Application / root package:
2.1.0 - FNI / data contract:
fni_v2.0(theversionfield in API/MCP responses)
claude_desktop_config.json / .mcp.json or your client's MCP settings) and restart the client. No server-side de-registration step is required, because nothing about your client is stored.
Build with Free2AI
656,000+ AI entities · FNI-ranked · Updated daily