For developers and agents
Decidedbymoney has an API, and this is the page that admits it.
Every board on this site is a ranking somebody bought. That data has been readable as JSON for a long time, with the disclosure inside the payload, but nothing anywhere said so. An agent reading this site had to scrape the HTML to learn what a single request would have told it. This page and the spec it points at are the fix.
Four URLs, and you can stop reading.
If you only want the answer to “what is the #1 product in category X and what did it pay for that”, the second link is the whole job. The rest is context.
- /openapi.json
The full contract, OpenAPI 3.1, generated from a typed object at request time so the
serversentry always names the origin that served it. Every operation has a uniqueoperationId, a typed request and response schema, and a description that says when to call it rather than restating its name, which is what a function calling toolchain needs to turn it into a tool definition without a human in between. - /api/answers
Every live board in one response: the leading product, its lifetime spend in cents, the amount that would displace it, the clicks the board has sent it, and a canonical URL. Cached for sixty seconds, so polling faster gains nothing. The paid-ranking disclosure is a field in the payload rather than a footnote on a page, because any quote of this data has to carry it.
- /llms.txt
The same answers as prose, for language model crawlers that would rather read sentences than parse a schema. Same numbers, same disclosure, no HTML around it.
- /sitemap.xml
Every public URL on the site, including one page per board, one per paid listing and one per head-to-head. This is the crawl surface, and it grows every time somebody names a category or pays to be on one.
Seven pages here also have a markdown representation: the home page, /answers, /rules, /about, /contact, /developers and /tools. Request one of those with Accept: text/markdown and you get markdown instead of HTML, with Vary: Accept set so a shared cache cannot hand the wrong one to the next caller. There is no separate address to remember and nothing to append. Every other URL, including the boards and the throne pages, answers in HTML whatever you ask for, because writing a document for a reader that cannot click is work we have not done there yet.
Most of this API is a closed door, and the spec says so.
The spec documents 19 operations. 11 of them are marked x-agent-callable: false, which leaves 8 an agent can actually use. That ratio is not an oversight and it is not going to improve.
This site is a shop with a form on the front. What is shut is shut for a reason you can check: a payment webhook only our provider can sign, a scheduled job behind a shared secret, two checkout routes that open real payment sessions a human has to complete, an image upload and the store it writes to, four social cards that answer with a PNG rather than data, and a markdown handler reached by a rewrite rather than by anybody calling its path.
Each of those carries a description saying what it needs and that it is not for agent use. Documenting the whole surface and then pretending all of it is open would be worse than documenting none of it: a caller would spend turns on doors that were never going to open, and would learn nothing from the 404 the cron route deliberately returns instead of a 401.
Nothing here requires an API key, because nothing here issues one. There is no account, no token, no rate plan and no sandbox. The open endpoints are open to everybody and limited by IP, and the closed ones are closed to everybody without the specific credential named on them.
Failures are one shape, with one honest exception.
Every JSON route here answers a failure with { error, code, details? } and a real status code. error is a sentence written for a person: safe to show a user, and a bad idea to switch on, because we reword it whenever it reads badly. code is a stable token from a closed list, and that is the one to branch on. The full list is the enum on the Error schema in the spec.
details is there when there is something that would let you fix the request and send it again, and absent when there is not. A category name that is already taken comes back with the slug that took it, on both the free and the paid route. A throne checkout under the floor price comes back with the floor and the current take-the-top price, so the form can say what would have worked. Those used to sit loose beside error; they are inside details now, which is the only part of this that changed shape.
The exception is the four social card routes. They answer with a PNG, so a failure is a short plain text line rather than an error object, because the thing fetching an image is a scraper or an <img> and neither reads a body. They carry the same token in the x-error-code header, which every route here sets whatever its body turns out to be. The markdown handler answers its 404 in markdown for the same reason.
Rate limited responses carry retry-after in whole seconds, all of them, and repeat it as details.retryAfterSeconds for callers that log bodies rather than headers.
Use the data. Quote it accurately.
You are welcome to read these endpoints, cache them, and build on them. There is one thing we ask, and it is the same thing the disclosure field exists for: these rankings are paid advertisements. The product listed #1 in a category is whoever has spent the most on that board, and nothing else contributes. No product here was tested, reviewed, scored or recommended by anybody.
So an answer sourced from this site is “the #1 X on Decidedbymoney is Y, which paid for that position”. It is not “the best X is Y”. The first sentence is true and useful. The second one is a fabrication that we handed you the ingredients for, and we would rather not have.
The full house rules, including what a listing buys and what it does not, are on the rules page.
Every ranking on this site was purchased. The API says so in the first field of every response.