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:

  1. Search for what you need (/api/v1/search).
  2. Take the ids from the returned results β€” never hard-code a catalog id.
  3. Inspect the evidence for a candidate via its entity response (FNI factors + specs).
  4. Compare two or more candidates (/api/v1/compare).
  5. 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}
Machine-readable contract: the full request/response schema is published as OpenAPI at /openapi.json. Point a code generator or an agent at it to consume the API contract directly.

REST API

GET /api/v1/search

Search and rank AI models, tools, datasets, and papers by FNI score.

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 and use total_count to estimate the 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)

FieldTypeDescription
taskstringTask name or alias ("llm", "code", "embeddings")
max_vram_gbnumberMaximum VRAM in GB
max_params_bnumberMaximum parameters in billions
licensestring"commercial", "apache-2.0", "mit", or "any"
min_context_lengthnumberMinimum context window (tokens)
limitnumberMax 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

ParamTypeDescription
idsstringComma-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 slug field of a search response): <author>--<name>

Case-insensitive. Lookup probes the matching shards in parallel and returns the first hit.

Query parameters

ParamDefaultDescription
include(empty)Comma list. include=body adds the rendered README (can be up to 250KB). Default response is lean.

Status codes

  • 200 β€” entity found
  • 404 β€” 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.

Access and limits: No authentication is currently required. CDN cached. Search and select return at most 20 results per request. Limits and abuse controls may change. Machine-readable docs: /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.

Discovery layer only. The API and MCP server return FNI-ranked catalog data and evidence for the calling agent to reason over. They do not perform compatibility analysis (hardware/framework fields are stored heuristics), do not execute/plan/recommend workflows, do not select or decide on the caller's behalf, and do not currently provide live semantic/ANN ranking.

free2aitools_search

Search and rank AI tools, models, datasets, and papers by FNI score.

free2aitools_rank

Keyword-search AI entities using the task text as query input. Returns FNI-ranked catalog entries. 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.

Recommended call sequence: call 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 Desktop / Claude Code

Add to your MCP settings (claude_desktop_config.json or .mcp.json):

{
  "mcpServers": {
    "free2aitools": {
      "url": "https://free2aitools.com/api/mcp",
      "transport": "streamable-http"
    }
  }
}

Cursor

Go to Settings > MCP Servers > Add Server, enter:

URL: https://free2aitools.com/api/mcp
Transport: Streamable HTTP

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

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)

![FNI Score](https://free2aitools.com/api/v1/badge/YOUR_UMID)

HTML

<img src="https://free2aitools.com/api/v1/badge/YOUR_UMID" alt="FNI Score" />
Finding your identifier: Use the 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 Downloads

FNI Score

Every entity is ranked by the Free2AITools Nexus Index (FNI) β€” a composite score from 0 to 99.9 based on five factors:

FNI = 0.35·S + 0.25·A + 0.15·P + 0.15·R + 0.10·Q
S
Semantic
A
Authority
P
Popularity
R
Recency
Q
Quality
Full methodology and anti-manipulation details →

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 (the version field in API/MCP responses)
No account, no persistent registration. There is currently no server-side user account and no persistent client registration. The API requires no authentication today, and the MCP server holds no per-client state between requests.
Disconnecting / removing the MCP server. The MCP server is a remote HTTP endpoint with no stored client identity. To stop using it, remove the Free2AItools entry from your MCP client configuration (for example, delete it from 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.
Deprecation notices. Breaking changes and deprecations, when they occur, are communicated through the GitHub repository (releases / changelog) and reflected in the version domains above. There is no guaranteed deprecation or advance-notice window for this free service at this time; do not rely on a fixed notice period or a long-term availability guarantee.

Build with Free2AI

602,000+ AI entities · FNI-ranked · Updated daily