Anonymous · read-only · v1

Public API

Normalized, attributable facts about AI model pricing, provider availability, capabilities and source freshness. You choose workloads, weights, ranking and cost/quality trade-offs.

Base URL: https://aiforless.ai/api/v1 · No API key required · Raw OpenAPI 3.1 JSON

Quick start

Request only the providers you need; the full catalog can be several megabytes.

curl --header "Accept: application/json" \
  "https://aiforless.ai/api/v1/provider-catalog?providers=anthropic-direct,openai-direct"

Endpoints

MethodPathPurposeMain parametersCache
GET/api/v1/provider-catalogNormalized provider/model catalogproviders60 s + revalidation
GET/api/v1/modelsCanonical model listlimit, offset, filters60 s + revalidation
GET/api/v1/models/{slug}One canonical model and its offersslug60 s + revalidation
GET/api/v1/providersPublic provider directory60 s + revalidation
GET/api/v1/sources/statusPublic freshness and collection health30 s + revalidation

Provider filters are comma-delimited, trimmed and de-duplicated. Empty, malformed, unknown or inactive selections fail closed. See OpenAPI for every model-list filter and bound.

Pricing

Raw normalized components are authoritative. Money uses exact decimal strings (nanoUsd, usd and display text), never floating-point JSON numbers.

listed

A non-zero canonical Money value is present.

free

State is free and canonical Money is exactly zero.

not_listed

Price is null because the source does not list that component.

Never interpret numeric zero alone as free. State and Money must satisfy the contract together. standardComparablePricing is a deterministic convenience projection, not a replacement for raw components. It selects one coherent service/context tier and never mixes input and output tiers; cache variants remain separate.

Cost Index reference

The machine-readable reference basket is $0.05 input and $0.40 output per 1M tokens. The API mandates no workload weights.

index = (input_price * input_weight + output_price * output_weight)
      / (reference_input * input_weight + reference_output * output_weight)

2:1

Weighted reference cost: $0.50

10:1

Weighted reference cost: $0.90

1:5

Weighted reference cost: $2.05

The website may use a display workload; API consumers choose their own and should use exact decimal arithmetic.

Capabilities

capabilities.effective is the resolved value; per-field provenance states whether it came from canonical evidence, direct-provider consensus, hosted-provider consensus or remains unknown. Image input, reasoning and tools are tri-state booleans: true, false or null. Context window and maximum output are positive integers or null. Unknown is not No, and clients must not infer capability from a model name.

Freshness & provenance

Public pricing identifies the source, parser version, observation and successful verification. Freshness describes age; collection status describes whether recent collection is healthy, degraded or unavailable. A degraded attempt does not invalidate the last successfully verified published fact. Application health/readiness is a separate operational concern and is not part of the data-freshness API.

Caching

Stable success responses carry a strong ETag over exact serialized bytes, Last-Modified from represented-data time and Cache-Control with no-transform. Store the ETag and send it in If-None-Match; a current representation returns an empty 304 Not Modified. If-Modified-Since is a fallback. Filtered URLs are distinct representations, and 304 requests still consume quota.

curl -i -H 'If-None-Match: "stored-validator"' \
  'https://aiforless.ai/api/v1/providers'

Rate limits

Anonymous limits are per effective client IP. Read X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset from each response; honor Retry-After on 429. Do not hardcode a quota into client logic.

Errors

CodeMeaning
invalid_requestParameters failed validation
unknown_providerProvider filter contains unknown or inactive slugs
not_foundResource does not exist
rate_limitedClient quota exceeded
internal_errorSanitized unexpected failure
data_unavailableRepresentation cannot currently be published

Errors include request metadata for support correlation; stable success metadata deliberately does not include a request ID.

Versioning

/api/v1 promises semantic and structural compatibility. Additive optional fields, models and providers are normal v1 evolution. Clients should skip unknown future component/tier values rather than interpret them as known rates. Critical state and Money semantics do not silently change; breaking semantics require a new major API version. The transitional catalog field arenaOverall is deprecated; use quality.arenaOverall.

The API is suitable for server-side clients and same-origin use. Cross-origin browser access is not currently a guaranteed v1 contract.

Examples

JavaScript / TypeScript

// Node.js or same-origin browser code
const url = "/api/v1/provider-catalog?providers=anthropic-direct,openai-direct";
const first = await fetch(url, { headers: { Accept: "application/json" } });
if (!first.ok) throw new Error(`HTTP ${first.status}`);
const etag = first.headers.get("ETag");
const catalog = await first.json();
// Money is authoritative decimal text: catalog.data[0].models[0].components[0].price.usd

const next = await fetch(url, { headers: { Accept: "application/json", "If-None-Match": etag ?? "" } });
if (next.status === 304) console.log("Cached representation is current");
else if (next.ok) console.log(await next.json());
else throw new Error(`HTTP ${next.status}`);

Python standard library

from decimal import Decimal
import json, urllib.request, urllib.error

url = "https://aiforless.ai/api/v1/provider-catalog?providers=anthropic-direct,openai-direct"
request = urllib.request.Request(url, headers={"Accept": "application/json"})
with urllib.request.urlopen(request) as response:
    etag = response.headers.get("ETag")
    catalog = json.load(response)
    usd = Decimal(catalog["data"][0]["models"][0]["components"][0]["price"]["usd"])

try:
    urllib.request.urlopen(urllib.request.Request(url, headers={"If-None-Match": etag or ""}))
except urllib.error.HTTPError as error:
    if error.code != 304:
        raise

Try the API

The explorer sends only fixed same-origin v1 routes, never arbitrary URLs or credentials. It does not run automatically.

/api/v1/provider-catalog?providers=anthropic-direct%2Copenai-direct

Choose an endpoint and send a request.

Machine-readable contract

Download the deterministic OpenAPI 3.1 document. It contains exactly the five stable data resources; operational and future product routes are intentionally excluded.