{
  "openapi": "3.1.0",
  "info": {
    "title": "Best Domain Registrars: public API",
    "version": "1.0.0",
    "summary": "Verified domain-registrar pricing, reviews and ownership data.",
    "description": "Free, keyless, and open under CC BY 4.0.\n\nEvery price carries the URL it was read from and the date it was read. Rows are all-in:\nany ICANN fee a registrar itemises separately is included, so figures are comparable.\nUnverified rows are excluded by default and are never ranked against verified ones: a\ncheapest result can never quietly mean cheapest among figures nobody checked.",
    "license": {
      "name": "CC BY 4.0",
      "url": "https://creativecommons.org/licenses/by/4.0/"
    },
    "contact": {
      "url": "https://www.best-domain-registrars.com/contact/"
    }
  },
  "servers": [
    {
      "url": "https://www.best-domain-registrars.com"
    }
  ],
  "externalDocs": {
    "description": "Data catalogue",
    "url": "https://www.best-domain-registrars.com/data/index.json"
  },
  "paths": {
    "/api/pricing/": {
      "get": {
        "operationId": "queryPricing",
        "summary": "Query verified registrar pricing",
        "description": "Filter by TLD and/or registrar. Called with no parameters, returns its own documentation and current coverage rather than an error.",
        "parameters": [
          {
            "name": "tld",
            "in": "query",
            "schema": {
              "type": "string",
              "example": ".com"
            },
            "description": "TLD, with or without the leading dot."
          },
          {
            "name": "registrar",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "porkbun"
            },
            "description": "Registrar id."
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "first_year",
                "renewal",
                "transfer"
              ],
              "default": "renewal"
            }
          },
          {
            "name": "include_unverified",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "Include placeholder rows. They are labelled and never ranked against verified rows."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 500
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching price rows, cheapest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PricingResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/rdap/": {
      "get": {
        "operationId": "rdapLookup",
        "summary": "Look up a domain's registrar of record",
        "description": "Proxies an authoritative RDAP query via the IANA bootstrap and normalises the response. Live third-party lookup; the queried domain is not stored.",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": false,
            "description": "Omit it and the endpoint describes itself instead of erroring.",
            "schema": {
              "type": "string",
              "example": "example.com"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Normalised RDAP record, or, with no `domain`, a description of this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RdapResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed domain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "More than 30 lookups a minute from one caller. Each lookup costs a registry a request. Carries `Retry-After`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/mcp/": {
      "get": {
        "operationId": "describeMcp",
        "summary": "Describe the Model Context Protocol endpoint",
        "description": "Human- and machine-readable description of the MCP server, including its tool list.",
        "responses": {
          "200": {
            "description": "Endpoint description.",
            "content": {
              "application/json": {}
            }
          }
        }
      },
      "post": {
        "operationId": "callMcp",
        "summary": "Model Context Protocol (JSON-RPC 2.0)",
        "description": "Methods: initialize, tools/list, tools/call, ping. Tools: best_registrar, cheapest_price, registrar_profile, compare_registrars, com_price_increase, verification_status, israel_registrars, price_history, registrars_for_country. Note the trailing slash: a request to /mcp answers with a 308 redirect, which not every client follows.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              },
              "example": {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/call",
                "params": {
                  "name": "cheapest_price",
                  "arguments": {
                    "tld": ".com"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted: the message was a notification, which has no response."
          }
        }
      }
    },
    "/data/registrar-incidents.json": {
      "get": {
        "operationId": "getDataRegistrarIncidentsJson",
        "summary": "Registrar incident log",
        "description": "Dated registrar outages and service failures with duration, root cause, affected services and sources. Registrar status pages are wiped after each event, so no consumer-facing record of who failed and for how long otherwise exists.\n\nVerification: public_signal. Each incident is sourced to the registrar's own communication or established reporting. The log begins 2026-08 and is NOT a complete industry history: an absent registrar means we have logged nothing for it, never that nothing happened. No reliability score is derived from it.",
        "responses": {
          "200": {
            "description": "Registrar incident log",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/registrar-ownership.json": {
      "get": {
        "operationId": "getDataRegistrarOwnershipJson",
        "summary": "Registrar ownership map",
        "description": "Which corporate group owns which retail registrar brand. Network Solutions, Domain.com and Register.com share an owner; so do Namecheap and Spaceship. Each group carries its own sources, and registrars with no sourced parent are recorded as independent rather than guessed.\n\nVerification: public_signal. Compiled from corporate announcements and industry reporting, linked per group. Ownership changes without notice to customers; treat last_updated as when it was last checked.",
        "responses": {
          "200": {
            "description": "Registrar ownership map",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/com-price-increase-2026.json": {
      "get": {
        "operationId": "getDataComPriceIncrease2026Json",
        "summary": ".com price increase 2026: wholesale history and pass-through baseline",
        "description": "Verisign's .com wholesale registry price history, the cost floor a registrar cannot sell below, and every tracked registrar's verified retail renewal recorded before the 1 November 2026 increase: the baseline that makes the after-the-fact comparison evidence rather than assertion.\n\nVerification: mixed. Wholesale figures are sourced to the .com Registry Agreement and Domain Name Wire's reporting of each Verisign price notice. Retail rows are verified-only, each carrying the registrar page it was read from and the date.",
        "responses": {
          "200": {
            "description": ".com price increase 2026: wholesale history and pass-through baseline",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/registrars.json": {
      "get": {
        "operationId": "getDataRegistrarsJson",
        "summary": "Registrar profiles",
        "description": "Canonical registrar records: name, website, HQ, founding year, ICANN accreditation, WHOIS-privacy policy, 2FA, API, support channels, and a per-registrar pricing_verification object.\n\nVerification: mixed. Registrar-level facts (HQ, accreditation, WHOIS-privacy policy, 2FA, API, support) are sourced from each registrar's public site. Pricing rows carry a per-registrar verified flag; see verification-status.json for the machine-readable state.",
        "responses": {
          "200": {
            "description": "Registrar profiles",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/pricing.csv": {
      "get": {
        "operationId": "getDataPricingCsv",
        "summary": "Registrar pricing",
        "description": "First-year, renewal, and transfer prices per registrar and TLD, each row carrying a source_url and last_checked date.\n\nVerification: mixed. Rows are verified where fetched from the registrar's official pricing page and confirmed against published values; the rest are sample/placeholder pending a JS-rendering crawl. Each row's verified state follows its registrar's flag in registrars.json.",
        "responses": {
          "200": {
            "description": "Registrar pricing",
            "content": {
              "text/csv": {}
            }
          }
        }
      }
    },
    "/data/tld-pricing.csv": {
      "get": {
        "operationId": "getDataTldPricingCsv",
        "summary": "TLD-first pricing",
        "description": "TLD-first pricing comparison rows, including verified ccTLD pricing, with source_url and last_checked per row.\n\nVerification: mixed. Same verification model as pricing.csv: verified rows are sourced from primary pricing pages; others are sample pending testing.",
        "responses": {
          "200": {
            "description": "TLD-first pricing",
            "content": {
              "text/csv": {}
            }
          }
        }
      }
    },
    "/data/freshness.json": {
      "get": {
        "operationId": "getDataFreshnessJson",
        "summary": "Data freshness",
        "description": "Per-registrar age of the pricing behind this site against the published refresh SLA, with the state each registrar is in and when its next refresh is due. Ages are computed at build time: trust the generated field over your own clock.\n\nVerification: status. Derived from the last_checked dates on the pricing rows themselves, not from a separately maintained field that could disagree with them.",
        "responses": {
          "200": {
            "description": "Data freshness",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/pipeline-status.json": {
      "get": {
        "operationId": "getDataPipelineStatusJson",
        "summary": "Pipeline status",
        "description": "How much of this site's pricing is verified against a primary source and how much is not, with denominators throughout: registrars tracked vs verified vs tested, rows verified vs sample, extensions priced vs tracked, source strategies by status, and the snapshot count.\n\nVerification: status. Counted from the data files at build time. Nothing here is asserted separately from the data it describes.",
        "responses": {
          "200": {
            "description": "Pipeline status",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/glossary.json": {
      "get": {
        "operationId": "getDataGlossaryJson",
        "summary": "Domain industry glossary",
        "description": "Definitions of domain-industry terms, each stating whether a primary source defines it (ICANN's glossary or policy pages, an IETF RFC, an IANA register, the company itself) and the headword that source uses: which is often not the industry's word.\n\nVerification: mixed. Every term carries a basis. Cited terms link a primary source; editorial terms are our own definitions of trade usage nothing authoritative defines and carry no source, rather than a citation that does not define the word.",
        "responses": {
          "200": {
            "description": "Domain industry glossary",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/operational-signals.json": {
      "get": {
        "operationId": "getDataOperationalSignalsJson",
        "summary": "Operational signals",
        "description": "Point-in-time HTTP reachability and latency of the endpoints this site's pricing collection depends on, appended weekly. Explicitly not uptime or a reliability score: one request from one machine cannot measure those, and the dataset says so in its own _meta.\n\nVerification: verified. Every record is an HTTP response observed directly from the endpoint named in it, nothing here is inferred. `as_expected` accounts for the registrars that refuse a plain fetch by design and are collected with a headless browser, so a documented 403 is not recorded as a failure.",
        "responses": {
          "200": {
            "description": "Operational signals",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/registrar-scores.json": {
      "get": {
        "operationId": "getDataRegistrarScoresJson",
        "summary": "Category and overall scores",
        "description": "Per-registrar category and overall scores on a 0–10 scale, derived from the published methodology weights.\n\nVerification: sample. The file self-labels as sample scoring data for the MVP, to be replaced with verified scoring before publishing. Treat as illustrative, not a verified rating.",
        "responses": {
          "200": {
            "description": "Category and overall scores",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/israel-registrars.json": {
      "get": {
        "operationId": "getDataIsraelRegistrarsJson",
        "summary": "Israel's accredited registrars",
        "description": "The fourteen registrars ISOC-IL accredits for .il, each joined with what its own storefront showed: interface language, billing currency, the .il namespaces its page lists and the figure it printed, plus the .il registry record from IANA. No numeric price field by design.\n\nVerification: mixed. Accreditation is the registry's published list, read 2026-09-02. Storefront facts were read from each registrar's own site on 2026-09-20: 11 of 14 were readable and 3 declined an automated request. The quoted figures are evidence that a namespace is sold and a currency is charged, not verified price rows, and this site has tested none of these registrars so none carries a score.",
        "responses": {
          "200": {
            "description": "Israel's accredited registrars",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/country-eligibility.json": {
      "get": {
        "operationId": "getDataCountryEligibilityJson",
        "summary": "Country eligibility for domain registrars",
        "description": "For each of the 60 covered countries and each tracked registrar: whether it runs a storefront in an official language of the country, bills in the country's currency, and sells the country's primary registrable namespace, each condition as yes/no/unknown with the page it was read from and the date. A registry publishes who may sell its namespace and a registrar publishes its own languages and currencies; no source publishes the join, which is what this is.\n\nVerification: mixed. Every yes and every no cites the registrar's or registry's own page. 'unknown' means not established either way and is never written as false: several registrars render their language and currency switchers in JavaScript, which the read behind this file cannot see, and 6 refused the latest read (2026-09-29) with an anti-bot response and stay unassessed. Every tracked registrar has been attempted at least once across 2 sweeps.",
        "responses": {
          "200": {
            "description": "Country eligibility for domain registrars",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/agent-readiness.json": {
      "get": {
        "operationId": "getDataAgentReadinessJson",
        "summary": "Agent Readiness Index",
        "description": "The Agent Readiness Index: per-registrar sub-scores across ten criteria, the published weights (sum 100), evidence notes, and sources; overalls (0–100) recompute from the sub-scores.\n\nVerification: public_signal. v0.1 is a public-signal assessment: sub-scores interpret each registrar's published API docs, pricing, and security/privacy policies (cited per registrar). It is not fabricated, and it is not yet an end-to-end live agent test (planned for v0.2).",
        "responses": {
          "200": {
            "description": "Agent Readiness Index",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/api-access.json": {
      "get": {
        "operationId": "getDataApiAccessJson",
        "summary": "Registrar API access tiers",
        "description": "Per-registrar API access facts - tier (ungated / account-gated / no-public-api), auth model, scoped tokens, OAuth, webhooks, rate limit, account gating, sandbox and OpenAPI URLs - plus the tier definitions and the source dataset version.\n\nVerification: public_signal. Facts mirror Open Domain Data registrar_api_capabilities (version above), each field sourced; Porkbun's public pricing endpoint was confirmed with a live unauthenticated request. The access-tier framing is this site's editorial interpretation, disclosed in the file's _meta.",
        "responses": {
          "200": {
            "description": "Registrar API access tiers",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/registrars/index.json": {
      "get": {
        "operationId": "getDataRegistrarsIndexJson",
        "summary": "Per-registrar agent profiles",
        "description": "Discovery index of one JSON profile per registrar (/data/registrars/<slug>.json). Each profile composes the published data into a single record: identity and verdict, editorial category scores, Agent Readiness sub-scores, API-access facts, transfer-in/out mechanics, and pricing-verification status, each section citing its source.\n\nVerification: mixed. Profiles compose existing published datasets and inherit each section's verification state; they introduce no new facts. Registrars not yet covered by a source dataset carry an explicit covered:false section instead of guessed values.",
        "responses": {
          "200": {
            "description": "Per-registrar agent profiles",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/domain-registrar-mcp.json": {
      "get": {
        "operationId": "getDataDomainRegistrarMcpJson",
        "summary": "Official registrar MCP servers",
        "description": "Which registrars publish an official, first-party MCP server - per-registrar has_official_mcp, kind (management vs search-only), endpoint, auth, method coverage, sandbox, and community-wrapper notes, with sources.\n\nVerification: public_signal. The official-MCP signal is grounded in Open Domain Data agent_capability_signals (version above); every positive capability claim (NameSilo management MCP, GoDaddy read-only MCP) was re-verified against the registrar's own MCP docs on the last_updated date.",
        "responses": {
          "200": {
            "description": "Official registrar MCP servers",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/country-recommendations.json": {
      "get": {
        "operationId": "getDataCountryRecommendationsJson",
        "summary": "Per-country recommendations",
        "description": "Per-country registrar recommendations with currency, local-presence (nexus) rules, popular ccTLDs, tracked registrars, and notes, each with a last_checked date.\n\nVerification: public_signal. Country rules and ccTLD detail are sourced from registry/registrar public information; recommendations are the site's editorial interpretation.",
        "responses": {
          "200": {
            "description": "Per-country recommendations",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/snapshots/index.json": {
      "get": {
        "operationId": "getDataSnapshotsIndexJson",
        "summary": "Verified price snapshots (history archive)",
        "description": "Index of dated, immutable weekly snapshots of verified registrar pricing. Each entry links a CSV of the verified prices as they stood on that date (/data/snapshots/<date>.csv). This archive can only be accumulated, never back-filled: it is the raw material for per-TLD price history.\n\nVerification: verified. Snapshots contain verified rows only: prices sourced from each registrar's own published pricing and confirmed against published values. Sample/placeholder prices are never archived, so the history never records a number nobody checked.",
        "responses": {
          "200": {
            "description": "Verified price snapshots (history archive)",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/local-storefronts.json": {
      "get": {
        "operationId": "getDataLocalStorefrontsJson",
        "summary": "Accredited local registrars, read (Netherlands, Germany, Switzerland, Norway)",
        "description": "SIDN-, DENIC-, SWITCH- and Norid-accredited retail registrars read from their own storefronts on one date: language, billing currency, VAT wording, the local namespace and the price printed for it, ranked by standing price including VAT with promotions shown beside it. Every registrar read without a comparable price is listed with the reason, and the ones that could not be read are named.\n\nVerification: mixed. Storefront facts are quotes from each registrar's own page on the sweep date. The VAT-inclusive figure is as printed where the page includes VAT and computed from the stated standard rate where it does not, and each row says which. No registrar here is tested or scored.",
        "responses": {
          "200": {
            "description": "Accredited local registrars, read (Netherlands, Germany, Switzerland, Norway)",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/geo-questions.json": {
      "get": {
        "operationId": "getDataGeoQuestionsJson",
        "summary": "Question universe (every question the site answers)",
        "description": "Every question this site claims to answer, mapped to its canonical page, Markdown twin, MCP tool and dataset, with the answer the site's data gives today and the MCP tool's own reply reduced to registrar names. Generated from the same inventories the pages render from; the build fails when the surfaces disagree.\n\nVerification: status. The expected answers are read from the data layer at build, not typed; each names the surface it was checked against.",
        "responses": {
          "200": {
            "description": "Question universe (every question the site answers)",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/price-history/index.json": {
      "get": {
        "operationId": "getDataPriceHistoryIndexJson",
        "summary": "Price history (per TLD, per registrar)",
        "description": "The snapshot archive read back as series: one file per TLD at /data/price-history/<tld>.json with each registrar's verified first-year, renewal and transfer figures on every snapshot date, and the change from the first snapshot to the latest. The index lists every TLD file and what the archive holds. Also the MCP tool price_history.\n\nVerification: verified. Built only from snapshot rows, which were verified on their date. A registrar missing from a snapshot leaves a gap, never an interpolated point.",
        "responses": {
          "200": {
            "description": "Price history (per TLD, per registrar)",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/price-history/moves.json": {
      "get": {
        "operationId": "getDataPriceHistoryMovesJson",
        "summary": "Price moves (every change between snapshots)",
        "description": "Every change to a verified first-year, renewal or transfer price between two consecutive weekly snapshots, newest first, with the before and after figures, the dates and the percentage. A row that appears or disappears is coverage changing, not a move, and is not listed.\n\nVerification: verified. Computed from consecutive snapshots that both carry the row; nothing is inferred across a gap.",
        "responses": {
          "200": {
            "description": "Price moves (every change between snapshots)",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/tld-registry-facts.json": {
      "get": {
        "operationId": "getDataTldRegistryFactsJson",
        "summary": "TLD registry facts (IANA)",
        "description": "IANA Root Zone Database facts for every tracked TLD: exact IANA operator string, IANA classification (generic / country-code / generic-restricted), record URL, and whether a verified registrar publishes pricing for it.\n\nVerification: verified. Generated by scripts/build-tld-facts.mjs from the IANA Root Zone Database (primary source); operator strings are IANA's own, never from memory. Restrictions/DNSSEC/IDN are deliberately absent here: IANA's index does not state them, and the site only shows them where hand-verified.",
        "responses": {
          "200": {
            "description": "TLD registry facts (IANA)",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/cctlds.json": {
      "get": {
        "operationId": "getDataCctldsJson",
        "summary": "Complete ccTLD index",
        "description": "Every country-code TLD in the IANA root (316: 255 ASCII + 61 IDN) - suffix, country, exact IANA registry operator, IANA record URL, second-level conventions, and confirmed registrar support.\n\nVerification: verified. Generated from the IANA Root Zone Database (the primary source); the build validator re-fetches IANA and fails if any ccTLD in the root is missing from this file. Registrar-support flags cover the tracked registrars only.",
        "responses": {
          "200": {
            "description": "Complete ccTLD index",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/methodology.json": {
      "get": {
        "operationId": "getDataMethodologyJson",
        "summary": "Scoring methodology",
        "description": "The machine-readable scoring methodology: scoring weights, testing criteria, selection criteria, affiliate policy, corrections policy, and update frequency.\n\nVerification: policy. This is the site's own published scoring policy, not a claim about any registrar. It is the reference for how every score on the site is produced.",
        "responses": {
          "200": {
            "description": "Scoring methodology",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/verification-status.json": {
      "get": {
        "operationId": "getDataVerificationStatusJson",
        "summary": "Pricing verification status",
        "description": "Machine-readable verification state per registrar: verified vs sample counts, the tracked TLDs, the most recent last_checked date, and the per-registrar pricing_verification detail.\n\nVerification: status. Derived from the pricing data itself. GET this endpoint to programmatically read counts.verified, counts.sample, and per-registrar status before relying on any pricing row.",
        "responses": {
          "200": {
            "description": "Pricing verification status",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/sync-manifest.json": {
      "get": {
        "operationId": "getDataSyncManifestJson",
        "summary": "Sync manifest",
        "description": "Integrity record for the whole data surface: per-dataset version and/or last_updated, record count, and sha256 of the canonical source as committed, plus the Open Domain Data dataset versions the site's imported facts derive from, and a generated_at timestamp.\n\nVerification: status. Generated by scripts/build-sync-manifest.mjs on every build; the validator fails the build when any hash disagrees with the file it describes, so a served manifest cannot silently drift from the data.",
        "responses": {
          "200": {
            "description": "Sync manifest",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/dataset-releases.json": {
      "get": {
        "operationId": "getDataDatasetReleasesJson",
        "summary": "Dataset releases",
        "description": "One release record per dataset: a sequential version (r1, r2, ...), the date the content last changed, the sha256 of that content and the full release history. Cut at build whenever a dataset's canonical source changes, so a consumer can cite 'pricing.csv r7 (2026-09-29)' and confirm the file it holds is that release. Also served as the data-releases Atom feed.\n\nVerification: status. Derived at build from the sync manifest's hashes; the history is append-only and never edited by hand.",
        "responses": {
          "200": {
            "description": "Dataset releases",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/scoreboard.json": {
      "get": {
        "operationId": "getDataScoreboardJson",
        "summary": "GEO scoreboard",
        "description": "The metrics of the site's generative-engine programme (verified registrars, pricing SLA, snapshots, countries at the Israel standard, local-language surfaces, MCP tools, dataset releases, AI citations), each recomputed at build beside the plan's baseline and targets. A metric nobody measures yet reads 'not measured'.\n\nVerification: status. Every value is read at build from the endpoint named in its source field; only the plan's baselines and targets are typed in, dated to the plan.",
        "responses": {
          "200": {
            "description": "GEO scoreboard",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/data/tld-page-audit.json": {
      "get": {
        "operationId": "getDataTldPageAuditJson",
        "summary": "TLD page audit",
        "description": "Per-page measure of how much of each TLD page is its own rather than template: own text, verified sellers priced, hand-verified registry rules, DNSSEC fact, accredited-registrar list, price history, real FAQ answers; a stated score and a class (defended or thin) per page. The report the fold-or-enrich decision is made from; it folds nothing itself.\n\nVerification: status. Measured over the built pages by scripts/audit-tld-pages.mjs with the weights stated in _meta.method; re-run after any change to the TLD template.",
        "responses": {
          "200": {
            "description": "TLD page audit",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PriceRow": {
        "type": "object",
        "properties": {
          "registrar_id": {
            "type": "string"
          },
          "registrar_name": {
            "type": "string"
          },
          "tld": {
            "type": "string"
          },
          "price": {
            "type": "number",
            "description": "All-in price for the requested type."
          },
          "price_type": {
            "type": "string",
            "enum": [
              "first_year",
              "renewal",
              "transfer"
            ]
          },
          "first_year": {
            "type": "number"
          },
          "renewal": {
            "type": "number"
          },
          "transfer": {
            "type": "number"
          },
          "currency": {
            "type": "string",
            "example": "USD"
          },
          "verified": {
            "type": "boolean",
            "description": "False means placeholder data. Do not present as fact."
          },
          "last_checked": {
            "type": "string",
            "format": "date",
            "example": "2026-08-19"
          },
          "source_url": {
            "type": "string",
            "format": "uri",
            "description": "The page this figure was read from."
          }
        },
        "required": [
          "registrar_id",
          "tld",
          "price",
          "verified",
          "last_checked",
          "source_url"
        ]
      },
      "PricingResponse": {
        "type": "object",
        "properties": {
          "query": {
            "type": "object"
          },
          "count": {
            "type": "integer"
          },
          "prices_last_checked": {
            "type": "string",
            "format": "date",
            "example": "2026-08-19"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PriceRow"
            }
          },
          "caveat": {
            "type": [
              "string",
              "null"
            ]
          },
          "license": {
            "type": "object"
          }
        }
      },
      "RdapResult": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string"
          },
          "registrar_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "iana_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "tracked_slug": {
            "type": [
              "string",
              "null"
            ],
            "description": "Our registrar slug, when the registrar of record is one we cover."
          },
          "nameservers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "statuses": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ],
            "description": "Omit for a notification."
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "tools/list",
              "tools/call",
              "ping"
            ]
          },
          "params": {
            "type": "object"
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "type": [
              "string",
              "integer",
              "null"
            ]
          },
          "result": {
            "type": "object"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    }
  }
}