Rankings start every offer at zero and can only demote. There is no signal a vendor can acquire, lobby for, or buy — there is nothing to add.
Every surface that presents vendors as a recommendation — the best-of pages, the alternatives on every vendor page, the /api/stack endpoint and the plan_stack MCP tool — resolves through one shared module. There is no second scorer, no per-page ordering, no vendor allowlist, pin, boost or override anywhere in the selection path. Nothing derived from our own editorial copy is an input: an offer cannot rank higher because we wrote more words about it.
A gate removes an offer from ranked surfaces entirely. It still appears on its category page and its vendor page; it is simply not something we would put in front of you as a recommendation.
| Gate | Meaning |
|---|---|
eligibility_restricted | The offer is not generally available — it requires accelerator, student, open-source, startup or similar qualification. Such offers appear on the category page, not on a ranked recommendation surface. |
not_a_free_offer | The stated tier is not a free offer (see the tier classification below). |
offer_expired | The offer's own stated expiry date has already passed. |
verification_lapsed | We have not been able to confirm the offer for more than 180 days. This is a floor, not a filter: no offer currently trips it. |
Tier names are free text: 89 distinct values across 1580 offers. We classify them by published rule rather than by an allowlist, so a tier we have not seen before is never silently dropped — the worst it can do is be treated as an ordinary free tier until someone classifies it.
Not a free offer (gated out entirely):
| Rule | Reason |
|---|---|
/^paid$/i | no free offer at all |
/^freemium$/i | paid product with a trial-shaped entry point, not a stated free tier |
/^pay[-\s]?as[-\s]?you[-\s]?go$/i | usage-billed from the first request |
/^pay[-\s]?per[-\s]?use\b/i | usage-billed from the first request |
/^conditional$/i | availability is not stated in terms we can check |
/^exempt\s*\/\s*paid$/i | free only by case-by-case exemption |
Time-limited (kept, but demoted — the free part runs out):
| Rule | Reason |
|---|---|
/credit/i | a credit grant that runs out |
/\btrial\b/i | a trial that expires |
/scholarship/i | a scholarship award, not an ongoing tier |
/\bbeta\b|preview|sandbox/i | a beta/preview/sandbox allowance that may end without notice |
Every eligible offer starts at zero. Points are only ever subtracted, and every point traces to a specific recorded fact with a date, shown on the page next to the offer it demoted.
| Demerit | Points | Trigger |
|---|---|---|
free_tier_withdrawn | −3 | A recorded free-tier removal, open-source licence change or product deprecation within the last 365 days. |
link_gone | −3 | The vendor's own server answers 410 for the pricing page we cite, stating the page is permanently gone. Recorded on the first such check, with no grace period. |
time_limited_offer | −2 | The stated tier is a credit grant, trial, scholarship, beta, preview or sandbox allowance — the free part runs out. |
link_unreachable | −2 | The pricing page we cite did not resolve on our check — a 404 answered by a full request, or a hostname with no address — and has still not resolved 14 days after the last date it was reachable. Being refused (403, 429, 5xx, timeout) is evidence about our checker and never produces this. |
stale_verification | −1 | We have not confirmed the offer against the vendor's own pricing page for more than 90 days. This measures our confidence, not the vendor. |
expiring_soon | −1 | The offer's own stated expiry date falls within 90 days. |
Weights are integers, so the tie band is exactly zero — no epsilon, no float noise pretending to be signal. The property that buys: one offer can only rank below another if we can name at least one specific recorded fact about it that the other does not have.
A recorded limit reduction, pricing restructure or new restriction is shown on every surface where the offer appears, with its date and summary — but it does not move rank. We have change records for only 16% of vendors, and disproportionately for well-known ones, so ranking on them would demote the vendors we happen to watch most closely.
stale_verification means we have not been able to confirm the offer recently. That is material — you should know when we cannot vouch for a record — but it is a statement about our confidence, and it is labelled as such wherever it appears. It is never presented as something the vendor did.
Vendor pages carry a stable / caution / risky label. It moves no order on this site — the ranking module cannot read it, and flipping every label leaves every listing we publish in the same order.
It is decided by the type of a recorded change, never by how many records we hold. A vendor that expanded its free tier, postponed a fee, added a tier or changed its name cannot be labelled caution for any of those. A caution or risky label always renders together with the single dated record that produced it, on the same page and next to the label. Where we cannot show the reason, we do not show the label.
The honest limit: stable means we hold no record of a free tier removal, a limit reduction or a pricing restructure for that vendor. It is a statement about our records, not a clean bill of health — a vendor we have never had cause to examine reads the same as one with a long clean history. Until August 2026 the label was derived from a count of records of any type, which inverted that: the vendors we watched most closely were the ones it flagged, and several were flagged for good news. That is fixed, and this paragraph is here so the next version of it is checkable.
Verification asks whether an offer's terms are still right. A separate daily check asks the cheaper question of whether its link still resolves at all, and it runs over every record regardless of how recently that record was verified.
Where the link has not resolved for 14 days, or the server has answered 410 Gone, we withhold the stable label and the verification date and publish the date the link was last reachable instead. A green label and a recent date over a destination that no longer answers is the most confident-looking thing on the page and the least true. A vendor is allowed an outage, which is what the 14 days are for.
The distinction that does the work: being refused is not evidence about a vendor. A 403, a 429, a gateway error or a timeout tells you that our checker was turned away from a datacentre address, and it changes nothing we publish in either direction — we do not withhold the label, and we do not claim the link is dead. Only an answer about the destination counts: 404 confirmed by a full request, 410, or a hostname with no address. This is the same rule as stale_verification above and the risk label before it, and it keeps arriving because each surface used to implement its own version.
We think that is the honest answer and a better thing to publish than a manufactured winner. It is also the strongest form of “our recommendations are not for sale”: there is no top slot, so there is nothing to buy. A vendor cannot improve its position by talking to us — only by improving its offer, or by us being able to verify it.
Tied offers are ordered by a permutation seeded on the UTC date and the query key, and nothing else. No vendor name, slug, id, index or offer field is an input, so an offer's position is uniform regardless of what it is called or where it sits in our file. The order rotates daily; the membership of the list does not.
seed = sha256(utc_date + '|' + query_key + '|p' + demerit_total); order = Fisher-Yates over the tied set driven by mulberry32(first 4 bytes of seed). No vendor name, slug, id, index or offer field is an input.
Every ranked page publishes the date, query_key, seed and tie_count it used, and the JSON APIs return the same block. You can recompute today's order yourself and check it against what we served, without asking us. That is the point: not for sale should be auditable, not merely asserted.
We rank on offer terms, verification recency and recorded adverse changes. We do not model technical fit — whether a particular product suits a particular role in your architecture. A vector database and a relational database sit in the same category here. If you are asking us which of two products fits your app, we are the wrong source; if you are asking whose free tier we could confirm this week and whose was withdrawn in March, that is exactly what this is for.
Section 4 is about fit and it still stands. This is a different thing: factual properties of a product, of the same kind as its category and read from the vendor's own words. They answer whether a product could stand in for another at all, not whether it would suit you better.
| Property | What it means |
|---|---|
local_dev_only — Runs only on a developer machine | The product is a local stand-in for a service you would otherwise run in production. It is not an alternative to the hosted service it stands in for. |
addon — Extends another product | The product adds a capability to another product rather than replacing it. It is not an alternative to the thing it augments. |
not_yet_classified — We have not classified this record | The offer is listed in a category whose subtypes we publish, and we have not yet read it against them. We therefore hold no basis for offering it as an alternative to anything here. This states what we have not done rather than a finding about the product, and it stops applying the day we classify the record. |
not_in_taxonomy — None of this category's subtypes apply | The offer is listed in the category, but it is not one of the kinds of product the category's subtypes describe, so it is not an alternative to any of them. |
subtype_mismatch — Shares no subtype with this product | Two offers in a category are alternatives when their subtype sets share at least one member, or when both carry a subtype from one of the category's membership groups. This offer does neither with the vendor the list is about. |
A gate removes an offer from an alternatives list only when the vendor the list is about does not carry the same gate. One local emulator is still an alternative to another; one add-on is still an alternative to another.
These gates decide membership of alternatives, related-vendor, risk and role-recommendation lists. They are not scores, they never change the order of anything, and they never filter a category page, a best-of page or a search result — those are inventory, and the caller asked for the category.
No property here is available on request. A vendor cannot ask to be classified, declassified, or exempted, for the same reason there is no top slot to buy. Every gated offer publishes the URL on the vendor's own site that the classification was read from, and the sentence it was read from. A vendor who believes we have read it wrong can point at the same page.
The honest limit: we have classified 10 of 1580 records, 7 of which carry a gate. A record with no classification has not been reviewed — it does not mean we have checked and found it hosted. Absence of the property publishes nothing in either direction and gates nothing, which is the same rule we use for a link we were merely refused. Subtypes are the deliberate exception, and the next section says why.
A category is a coarse property. Where we have published a subtype taxonomy for a category, the same discipline applies one level finer: a subtype is what the vendor's own copy says the product is, it is multi-label, and it decides membership only. Two offers in the same category are alternatives when their subtype sets share at least one member, or when both carry a subtype from one of the membership groups below. A record we have not classified is offered no substitutes, and is offered as one only where a person wrote the pair down by name. It stays listed in its category, in search, and on best-of pages.
| Subtype | What it means | Group |
|---|---|---|
relational | tables, rows and joins under SQL — Postgres, MySQL, SQLite family | — |
document | schemaless JSON/BSON documents as the stored unit | — |
vector | stores embeddings and answers similarity queries as its primary access path | — |
kv_cache | key to value, no query language over the value | — |
graph | nodes and edges are the primary model, with a traversal query language | — |
timeseries | rows are time-indexed measurements, retention is a first-class setting | — |
analytical | columnar warehouse for aggregate scans, not row-level transactions | — |
backend_platform | bundles a database with auth, functions or storage as one product | — |
| Subtype | What it means | Group |
|---|---|---|
static_site | serves prebuilt files from a CDN; there is no server process you control | ✓ |
serverless_function | your code runs per request with no long-lived instance, billed by invocation or CPU time | ✓ |
container_app | you deploy an app or image and the platform keeps a process running for it | ✓ |
shared_web_hosting | a directory on a managed server, reached by FTP or SSH, with a bundled MySQL or Postgres | — |
managed_cms_hosting | hosting specialised to one CMS or documentation framework, provisioned and updated by the host | — |
site_builder | you author the site in the vendor's own editor and it is served from there | — |
backend_as_a_service | an API bundling a datastore with auth, push or realtime; there is no server you deploy | — |
agent_sandbox | isolated execution environments provisioned programmatically, sold for AI agent workloads | — |
static_site, serverless_function, container_app are one membership group. These three describe how your code is executed, not what can replace what. A reader leaving one of them is asking where else they can deploy the thing they have, and all three answer that question, so two offers that each carry one are alternatives whether or not they carry the same one. Every other subtype in this category keeps the shared-member rule unchanged.
| Subtype | What it means | Group |
|---|---|---|
uptime_check | polls a URL or host from outside your infrastructure on a fixed interval and alerts when it stops answering; it runs no code of yours | — |
synthetic_check | runs a check you author — a scripted browser session, a multi-step API call, or an assertion on a response body — on a schedule from outside your infrastructure, and reports whether the behaviour it asserts still holds | — |
host_metrics | an agent you install on a machine reports CPU, memory, disk and process state for that machine | ✓ |
apm_traces | instruments application code and reports per-request latency, throughput and spans across services | ✓ |
log_management | ingests, stores and queries log lines; the line is the stored unit and retention is a priced dimension | ✓ |
metrics_backend | stores time-indexed measurements and serves queries over them; dashboards and alerting are built on top rather than sold as the product | ✓ |
dashboards | renders charts, dashboards and alert rules over telemetry held in stores it does not own; what is sold is the view, not the store | ✓ |
error_tracking | captures unhandled exceptions with stack traces and groups repeat occurrences into one issue | — |
cron_monitor | waits for a scheduled job to check in and alerts on the absence of the check-in rather than on a failure it observed | — |
status_page | publishes a page your own users read to see whether your service is up | — |
upstream_status_watch | watches status pages or endpoints belonging to third parties you depend on, and reports to you rather than to your users | — |
on_call_response | routes a firing alert to a named human by schedule and escalation policy | — |
page_change_watch | fetches a web page on a schedule and notifies you when its content differs from last time | — |
host_metrics, apm_traces, log_management, metrics_backend, dashboards are one membership group. A reader leaving one of these is asking where else to send telemetry, what will store it and what will show it, and all five answer some part of that, so two offers that each carry one are alternatives whether or not they carry the same one. error_tracking sits outside this group deliberately: a reader leaving Sentry wants exception grouping, and every platform that also offers it carries the error_tracking label itself. Every other subtype in this category keeps the shared-member rule unchanged.
| Subtype | What it means | Group |
|---|---|---|
llm_api | the vendor runs language or multimodal models on its own infrastructure and sells calls to them by model name; you send a prompt and receive a completion | ✓ |
llm_observability | records the prompts, responses, latency, cost and tool calls an application sent to a model, for reading afterwards | — |
llm_evaluation | scores model or agent output against a dataset, a rubric or a judge model on runs you trigger | — |
model_gateway | one endpoint that reaches models run by several independent providers; what is sold is the routing, key management and fallback, not the weights | ✓ |
model_hosting | you supply or select a model and the platform serves it behind an endpoint on hardware you choose | — |
embeddings_api | returns a vector for text or media so it can be compared to other vectors; the vector is the output, not a completion | — |
gpu_compute | sells accelerator time for code you write; the unit billed is machine time, not a model call | — |
speech_api | converts recorded or streamed speech to text, or text to speech | — |
document_extraction | turns a document or a scan into text or structured fields | — |
image_video_generation | produces images or video from a prompt or a source image | — |
vector_store | stores embeddings and answers nearest-neighbour queries as its primary access path | — |
data_labeling | produces labelled or annotated training data | — |
experiment_tracking | records training or evaluation runs, their metrics and artifacts, and keeps a registry of model versions | — |
web_search_api | answers a query with current web results shaped for a model to read | — |
agent_sandbox | isolated execution environments provisioned programmatically for code an agent writes | — |
agent_tool_access | supplies an agent with authenticated connections to third-party applications it can call as tools | — |
llm_api, model_gateway are one membership group. A reader leaving one of these is asking where else to send a prompt and get a completion back. Whether the vendor runs the weights itself or routes the call to a provider that does is a procurement question, not a different product, so two offers that each carry one are alternatives whether or not they carry the same one. Every other subtype in this category keeps the shared-member rule unchanged.
A group answers whether a product could substitute at all. Which one is the better answer is ordering, and ordering is a separate, seeded, published concern that reads none of this.
Subtypes are published on every classified vendor page with the source URL and the sentence they were read from, exactly as the properties above are. We have classified 41 of 45 in Databases, 56 of 60 in Cloud Hosting, 59 of 59 in Monitoring, 64 of 70 in AI / ML; the other 1360 records carry no subtype, so they are offered no substitutes, and are offered as one only where a person wrote the pair down by name. 14 of them sit in a category we have started, where that gate binds on them today. Classifying a category restores both directions.
Where a taxonomy is published, silence is not a pass. A record we have not read against it is held out of that category's alternatives lists and named there, rather than offered on the strength of a reading we never did. That is the opposite of how the properties above treat absence, and it is deliberate: those describe a product we looked at, while a missing subtype describes only us.
A curated alternative is a pair a person wrote down for this vendor by name. That is a stronger claim than a subtype match, so subtypes never remove one; the product-role gates still do, because a local emulator does not replace a hosted service whoever names it.
Agents can report which vendor they recommended, at /api/signal. Those counts never affect any order we publish — not now, not behind a flag. A self-reported counter that influenced placement would be a purchasable ranking with extra steps, and the whole reason to trust this index is that there is no such thing.
And we do not publish per-vendor signal counts, because a visible counter is a signal a vendor could acquire. Anyone can fire that endpoint without authenticating — including a vendor, at itself, all day. So there is no per-vendor figure to publish and no table anyone could screenshot as “AgentDeals’ most-recommended vendor”. The signal page documents the call and publishes no counts at all.
Not every list on this site is a recommendation. Where a page is an inventory — every referral programme we know of, every comparison pair we generate — it is labelled as such and ordered by the same unbiased permutation rather than by the alphabet or by our commercial interest. Where we hold a referral link and may earn a commission, that is stated on the section itself, not only in a footnote. See our affiliate disclosure.
Every demotion names a dated, recorded fact. If a fact is wrong or out of date, it is correctable: the record behind it is shown on the vendor's page with its source. Correcting it changes the ranking automatically, because the ranking has no other input. There is no editorial override to appeal to — deliberately, because an override is the thing that would be sold.
This page is generated from the same constants the ranking uses, so it cannot drift from the code. Last computed 2026-09-01.