Data and API
Everything on this site is available as JSON, read only, without a key. 6 endpoints, CC BY 4.0, one rule that shapes all of it.
No field is ever a boolean. "iso_27001": true would be the sentence “this vendor is certified” – a statement of fact about a real company. What you get instead is found, not_found or not_measured, each with the address we read it at and the date. not_found means we did not find it at the addresses listed in checked; it is not a statement that the vendor has none.
Endpoints
| Endpoint | Answers |
|---|---|
/api/v1/criteria | What do you measure, and how is it weighted? The full catalogue with the weight of each criterion. This is the same source the methodology page reads. |
/api/v1/categories | Which categories exist? Every category we have measured at least once, with the number of vendors in it. |
/api/v1/categories/crm | Who is in a category, and in what order? The ranking of one category: rank, score, and how many criteria we could measure for each vendor. |
/api/v1/vendors/stripe.com | What did you find at one vendor? One vendor by domain, with every criterion, its source, the addresses we checked, and the quotes we read. |
/api/v1/details | What values did you read off their pages? The catalogue of measured details: certifications named, hosting locations, uptime figures, subprocessors, third-party hosts. |
/api/v1/details/certifications | Which vendors name a given value? One detail type with every value that has enough vendors to have a page. |
All of them are GET. Anything else answers 405: corrections go through the form so a human reads them. Access-Control-Allow-Origin: * is set, so you can call them from a browser.
Fields every response carries
| Field | What it means |
|---|---|
snapshot | The id of the data snapshot this response was rendered from, for example r42.71638. Two figures are only comparable when they carry the same id. |
snapshot_run | The highest completed measurement run that contributed to the snapshot. |
snapshot_results | How many results the snapshot holds. It moves when a partial run lands, even if the run number does not. |
snapshot_measured_through | The newest measurement timestamp in the snapshot. |
license | The licence the figures are published under: CC BY 4.0. Attribution is the link back. |
score_version | The version of the transparency score. It is frozen, and a new criterion does not change an existing figure. |
classifier_version | The current confidence classifier for stored certification and hosting quotes. It does not date or replace their original measurement method. Measured precision and recall are on /data/quality. |
classifier_scope | current_quote_confidence: only the confidence of current stored quotes is re-evaluated. Original subjects, measurement dates, criteria, scores and historical series keep their recorded state. |
The three states, and the fourth thing that is not a state
| Value | What it means | Who it describes |
|---|---|---|
found | We found it, and source is where. | the vendor |
not_found | We reached at least one address and none of them carried it. checked lists which. | the vendor |
not_measured | We reached no address at all, or robots.txt asked us not to. It falls out of the numerator and the denominator of the score. | us |
A quoted detail carries a fourth thing, and it is not a state but a subject: whose sentence it is. anbieter (the vendor itself), infrastruktur (their data centres or infrastructure), dienstleister (a named provider of theirs), erwaehnt (named without a certification claim), unklar (unclear from the sentence). Only anbieter is aggregated into a value page – a sentence about a vendor's data centre does not belong in a list called “vendors that name ISO 27001 for themselves”.
The score
The score is Transparency Score v1: the share of the summed weights of 12 criteria that we found. It is frozen. Adding a criterion would change every figure ever published, including the one in a procurement file from last week, without the vendor doing anything; new criteria are therefore measured and returned as facts and do not enter the figure. Below 9 measured criteria there is no score at all – score is null, never 0.
| Band | Score |
|---|---|
A | 85 to 100 |
B | 70 to 84 |
C | 55 to 69 |
D | 40 to 54 |
E | 25 to 39 |
F | 0 to 24 |
The 12 criteria
| Key | Name | Weight | Scored |
|---|---|---|---|
dpa | Data processing agreement | 3 | yes (v1) |
subprozessoren | Subprocessor list | 3 | yes (v1) |
datenregion | Data location stated | 3 | yes (v1) |
statusseite | Status page with history | 2 | yes (v1) |
preise | Public pricing | 2 | yes (v1) |
zertifizierungen | Certifications named | 1 | yes (v1) |
sla | Uptime SLA with a figure | 1 | yes (v1) |
transport | HTTPS enforced with HSTS | 2 | yes (v1) |
tracker_vor_einwilligung | No third-party tracking before consent | 2 | yes (v1) |
security_txt | security.txt (RFC 9116) | 1 | yes (v1) |
subprozessor_meldung | Notice before subprocessors change | 2 | yes (v1) |
datenschutzkontakt | Named privacy contact | 1 | yes (v1) |
The 10 detail types
| Key | Name | From criterion | Has value pages |
|---|---|---|---|
zertifikat | Certification named | zertifizierungen | yes |
datenregion | Hosting location named | datenregion | yes |
sla_prozent | Uptime figure | sla | yes |
subprozessor | Subprocessor named | subprozessoren | yes |
meldefrist_tage | Subprocessor notice period | subprozessor_meldung | yes |
datenschutzkontakt_art | Privacy contact | datenschutzkontakt | yes |
drittanbieter | Third-party host on first load | tracker_vor_einwilligung | yes |
hsts_frist | HSTS max-age | transport | no |
hsts_option | HSTS option | transport | no |
security_txt_ablauf | security.txt expiry | security_txt | no |
The machine-readable description
/openapi.json is an OpenAPI 3.1 document for this interface. It is generated from the same catalogues as the answers, not written alongside them: the endpoints, the shared fields, the twelve criteria, the detail types, the subjects and the three states all come from the code that produces the responses. A description written by hand goes stale exactly when someone adds a field, and a description that says something other than the interface is worse than none.
Responses carry an ETag, so a conditional request with If-None-Match gets a 304 and no body. There is no key and no quota beyond ordinary fair use; if you plan a large run, read how our own crawler behaves and do the same.
A response, in full
{
"snapshot": "r178.108284",
"snapshot_run": 178,
"score_version": "v1",
"license": "CC BY 4.0",
"domain": "example.com",
"score": 68,
"measured": 12,
"of": 12,
"criteria": {
"dpa": {
"result": "found",
"source": "https://example.com/legal/dpa",
"checked": [
{
"url": "https://example.com/dpa",
"outcome": "404"
},
{
"url": "https://example.com/legal/dpa",
"outcome": "200"
}
],
"note": null,
"measured_on": "2026-09-04T02:41:18.000Z"
},
"security_txt": {
"result": "not_found",
"source": null,
"checked": [
{
"url": "https://example.com/.well-known/security.txt",
"outcome": "404"
}
],
"note": null,
"measured_on": "2026-09-04T02:41:24.000Z"
}
},
"details": [
{
"key": "zertifikat",
"name": "Certification named",
"value": "iso-27001",
"subject": "anbieter",
"quote": "We are ISO/IEC 27001 certified, audited annually.",
"source": "https://example.com/trust",
"measured_on": "2026-09-04T02:41:20.000Z",
"path": "/data/certifications/iso-27001"
}
]
}
Bulk: the whole dataset as one file
Every result we hold, gzipped, at /data/dump/. It is written once per measurement run and handed out by the web server, so it costs you one request and us nothing. manifest.json names the snapshot, the row count and a SHA-256 per file: two downloads with the same snapshot identifier are the same data.
results.ndjson.gzandresults.csv.gz– one row per vendor and criterion: domain, criterion, one of the three states above, the address we read it at, the date.vendors.ndjson.gz– one row per vendor: domain, name, categories, score, and how many criteria that score rests on.
Generating this per request would serialise around 90 MB on a two-core machine that also serves everything else here, which is why it is a file and not an endpoint.
Bulk: the evidence pack for one vendor
Every vendor profile carries a downloadable pack for a procurement file, in two formats:
/<category>/<vendor>/evidence.csv– two tables in one file: 13 columns per criterion (vendor,domain,category,criterion,criterion_key,weight,result,source_url,addresses_checked,note,measured_on,snapshot,score_version), then the quoted details (vendor,domain,detail,detail_key,value,subject,quote,source_url,measured_on)./<category>/<vendor>/evidence.json– the same, nested, with the full list of addresses we checked.
Both carry the snapshot id and the disclaimer. There is no PDF: the container that serves this page has no browser to render one, and the page itself prints.
For an agent: MCP
The same data over Model Context Protocol at https://saas-ranking.com/mcp, so a model can ask instead of fetching. Stateless: one JSON-RPC request per POST, no session, no key, no account. 6 tools, all read-only:
| Tool | What it answers |
|---|---|
criteria | The twelve public signals we check, with their weight in the score and why each one matters. Start here if you do not know what this dataset contains. |
search | Search vendors and categories by name or domain, tolerant of typos. Returns names and paths, not measurements: use "vendor" for what we measured. |
vendor | All twelve criteria for one vendor, each with the addresses we checked, the date, and the source where we found it. Takes a domain (example.com). Also returns the quoted details we read off their pages. |
categories | Every category we have measured, with how many vendors it holds. Paginated. |
category | The vendors of one category with their transparency score and rank. A score of null means too few criteria could be measured to rank the vendor, never a bad score. |
detail_values | For one kind of detail (for example hosting locations or named certifications): which values we read on vendor pages, and at how many vendors. Every value links to a page that lists them with the sentence we read it from. |
Every tool runs the same function as the REST endpoint beside it, and test/mcp.test.js compares both answers field by field – two paths would be two truths. Every answer carries the snapshot id and one sentence: a result describes our measurement, never a property of the vendor.
Rate limits, and what we ask instead
There is no key and no counted quota. What we ask is the thing a rate limit would enforce badly: take the snapshot once and work from it, rather than fetching ten thousand vendor documents one at a time. That is exactly what the bulk download is for.
If your use needs something we do not offer – a webhook, an older snapshot – tell us. We would rather build it than watch someone scrape the pages.
Versioning
The path carries the version: /api/v1/. Inside a version we add fields and never repurpose one. A field that would have to mean something else gets a new name, and the old one keeps its meaning until the next version. The score has its own version, printed in every response, for the same reason.