# TLD catalogue

> Every extension you can sell, with its periods, prices, features and required extra fields, in one free call. Fetch it once and cache it.

## GET /api/v1/products/tlds

**The single most useful endpoint in the API.** It answers how many years you can sell an extension for, whether it supports privacy, DNSSEC and transfers, which extra fields are required, and what it costs — all in one call.

:::tip[This endpoint is free]
It is exempt from rate limiting and never costs quota points. Even so, **cache it on your side** and refresh daily — it changes rarely and the response is large.
:::

**Request**

```http
GET /api/v1/products/tlds?maxResultCount=500&currency=USD
```

| Parameter | Default | Notes |
| --- | --- | --- |
| `filterText` | — | Filter by extension name. |
| `currency` | — | Express prices in this currency, e.g. `USD`, `TRY`. |
| `skipCount` | 0 |   |
| `maxResultCount` | 10 | **Raise this** — the default of 10 will surprise you. The full catalogue is several hundred entries. |
| `sorting` | — | `field asc` or `field desc`. Must end in `asc` or `desc`. |

**Response — 200, one entry abridged**

```json
{
  "totalCount": 487,
  "items": [
    {
      "name": "com",
      "minRegistrationPeriod": 1,
      "maxRegistrationPeriod": 10,
      "registrationPeriods": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
      "renewalPeriods": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
      "addGracePeriod": 5,
      "autoRenewGracePeriod": 45,
      "deletionRestorablePeriod": 30,
      "deletionHoldPeriod": 5,
      "transferPeriod": 5,
      "isSupportTransfer": true,
      "isTransferCheckSupported": true,
      "isRequiredAuthCode": true,
      "isTransferLockPossible": true,
      "isPrivacyAvailable": true,
      "isDnssecAvailable": true,
      "isSupportIdn": true,
      "isIpv6Available": true,
      "isGlueRecordAllowed": true,
      "isHostObjectAccepted": true,
      "registryDnsCheck": false,
      "isRequireDocument": false,
      "isSupportTrustee": false,
      "isTrusteeRequired": false,
      "isHandleUpdateAvailable": true,
      "isSaleAllowed": true,
      "availableInTestEnvironment": true,
      "prices": [
        {
          "priceGroup": "Standard",
          "register": [
            { "period": 1, "price": 11.40, "currency": "USD" },
            { "period": 2, "price": 22.80, "currency": "USD" }
          ],
          "renew": [
            { "period": 1, "price": 12.90, "currency": "USD" }
          ],
          "transfer": [
            { "period": 1, "price": 11.40, "currency": "USD" }
          ],
          "restore": { "period": 1, "price": 85.00, "currency": "USD" },
          "backorder": { "period": 1, "price": 25.00, "currency": "USD" }
        }
      ],
      "attributes": []
    }
  ]
}
```

### Every field, and what to do with it

This response is the contract for what an extension will and will not accept. The fields fall into four groups.

**1 · Terms you may sell — in years**

| Field | Meaning and use |
| --- | --- |
| `name` | The extension itself, without a leading dot: `com`, `com.tr`, `co.uk`. |
| `registrationPeriods` | **The exact terms you may send as `period` when registering.** A list, not a range — `[1,2,3,5,10]` means 4 is not sellable. Anything else gives `Domain:10023`. |
| `renewalPeriods` | The same list for renewal. It is not always identical to `registrationPeriods`. |
| `minRegistrationPeriod`<br>`maxRegistrationPeriod` | The smallest and largest entry in `registrationPeriods`, provided for convenience. Use them for a "from N years" label; use the list itself to build the selector. |
| `renewalPeriod` | The shortest renewal term. Useful as the default in a renewal form. |
| `transferPeriod` | The shortest transfer term. **May be `0`** when the extension does not publish transfer terms in a form we can read — treat `0` as "not stated" and rely on `/transfers/check` and the transfer price list instead. |
| `registerPeriod` | Reserved. Currently always `0` — ignore it. |

**2 · Capability flags — check before you call**

Each of these tells you whether an operation is possible at all. Checking first turns a failed API call into a disabled button.

| Flag | What it gates, and the error if you ignore it |
| --- | --- |
| isSupportTransfer | Whether domains can be transferred in at all. `false` → `Domain:10011` on `/domains/transfer`. |
| isTransferCheckSupported | Whether the registry offers a pre-transfer check. `false` → `Domain:10025` on `/transfers/check`; that is not a failure, just skip the check and call `/transfer` directly. |
| isRequiredAuthCode | Whether an authorisation code is mandatory to transfer in. `true` means you must collect it from the customer before you start. |
| isTransferLockPossible | Whether the registrar transfer lock can be toggled. `false` means `/domains/lock` and `/unlock` have nothing to act on. |
| isPrivacyAvailable | Whether WHOIS privacy is sold. `false` → `Tld:10001` on `/domains/privacy`. Hide the privacy option in your order form. |
| isDnssecAvailable | Whether DNSSEC is supported. `false` → every DNSSEC write gives `Dnssec:10001`. |
| isSupportIdn | Whether internationalised (non-ASCII) names are accepted. `false` → registering one gives `TldAttribute:10004`. |
| isIpv6Available | Whether IPv6 glue addresses are accepted. `false` → sending a `v6` address gives `Dns:10026`; send only `v4`. |
| isGlueRecordAllowed | Whether glue records are accepted at all. `false` → host operations give `Dns:10017`. |
| isHostObjectAccepted | How name servers under the domain are modelled. `true` → independent host objects; `false` → glue records carried on the domain. Changes what you can share and delete — see [Host (glue) records](https://www.domainnameapi.com/docs/name-servers/hosts/). |
| isHandleUpdateAvailable | Whether contacts can be re-pointed by handle. `false` → use `/domains/contacts/update` with full details instead of `/update-with-handle`. |
| isSupportTrustee | Whether a trustee / local-presence service is available to buy. |
| isSaleAllowed | Whether domains on this extension may be listed for sale. |
| availableInTestEnvironment | Whether the extension exists in the test environment. **Check this before writing an integration test** against a specific extension. |

**3 · Flags that change your workflow**

These do not block anything — they mean the happy path has an extra step. Design for them before you sell the extension, not after the first support ticket.

| Flag | What changes when it is `true` |
| --- | --- |
| isRequireDocument | **Registration does not complete on its own.** The domain comes back in a waiting state and stays there until you [upload documents](https://www.domainnameapi.com/docs/domains/documents/) and the registry approves them. Your order flow needs a document step and a status screen. |
| isTrusteeRequired | **You must send `useTrusteeContact: true`** or registration fails with `Trustee:10006`. The trustee fee is added to the order, so quote the customer the higher price. |
| registryDnsCheck | The registry queries your name servers and checks they really answer for the domain **before** accepting the registration. Point the domain at name servers that are already live — using a provider you have not configured yet will have the registration refused. |

**4 · Lifecycle periods — in calendar days**

These describe what the registry does to a domain as it ages. They are the numbers behind the renewal and restore rules, and they differ by extension.

| Field | Meaning and use |
| --- | --- |
| `addGracePeriod` | Days after a **new registration** during which deleting the domain is refunded by the registry. Commonly around 3–5 days. This is your window to undo a typo'd registration at no cost. |
| `autoRenewGracePeriod` | **Days after expiry during which the registry's automatic renewal can still be reversed.** The domain is auto-renewed on the first day after expiry and sits in this window — which is exactly why a second renewal inside it returns `Domain:10020`. Commonly two to three weeks, up to 45 days. |
| `deletionRestorablePeriod` | **The redemption window.** How many days `POST /domains/restore` keeps working after the domain is deleted — typically 30. Drive your "you can still recover this" messaging off it. |
| `deletionHoldPeriod` | Days between redemption lapsing and the domain actually being released to the public. Nothing can be done during it; the domain is beyond recovery. |
| `finalizationPeriod` | Days until a pending renewal becomes irreversible at the registry. |
| `failurePeriod` | The point after expiry at which the domain can no longer be renewed at all and moves into deletion. **Often a negative number**, counted relative to the expiry date. |
| `paymentPeriod` | Days during which the registry accepts payment for an upcoming renewal. |

:::tip[How the lifecycle fields fit together]
```text
registration ──► addGracePeriod            delete here = refunded
                      │
                      ▼
                   active ... ... ... ... ... expiry date
                                                 │
                                                 ▼
                              autoRenewGracePeriod   registry auto-renews;
                                                     second renewal refused (10020)
                                                 │
                                                 ▼
                                   failurePeriod     renewal no longer possible
                                                 │
                                                 ▼
                         deletionRestorablePeriod    restore still works
                                                 │
                                                 ▼
                              deletionHoldPeriod     released to the public
```

Two practical uses: send renewal reminders before `failurePeriod` is reached, and show "recoverable until" dates from `deletionRestorablePeriod` instead of assuming 30 days everywhere.
:::

### Prices

Read the `prices` entry whose `priceGroup` matches your account's group — which you can find from `GET /api/v1/deposit/accounts/me` as `resellerGroupName`.

:::caution[These are list prices, and premium domains are not here]
- If you have a negotiated rate, the amount actually charged can be **lower** than what this endpoint shows. Treat catalogue prices as your sell-price baseline, not as your cost.
- **Premium domain prices never appear here.** They come from `POST /domains/search`, `/renew/check` or `/transfers/check`, and they can be many times the standard price. Always check `isPremium` before showing a price to a customer.
:::

### Extra fields (attributes)

The `attributes` array lists the registry-specific extra fields for the extension. Each entry has:

| Field | Meaning |
| --- | --- |
| `key` | **The key to send in `tldAttributes`.** |
| `description` | Label to show the customer. |
| `type` | `Text`, `DropdownList`, `RadioButton`, `Checkbox`, `CheckboxWithContract`, `FileUpload` or `Label`. `Label` entries are informational only; nothing is sent for them. |
| `isRequired` | Whether the registration fails without it. |
| `options` | The permitted values, each as `{ "value": "..." }`. Send one of the `value`s verbatim. Empty for free-text fields. |

```json
"attributes": [
  {
    "key": "TRABISDOMAINCATEGORY",
    "description": "Domain category",
    "isRequired": true,
    "type": "DropdownList",
    "options": [{ "value": "Corporate" }, { "value": "Owner" }]
  }
]
```

:::tip[Build your registration form from this]
Rather than hard-coding "`.com.tr` needs an identity number", render the form dynamically from `attributes`. New extension requirements then work without a code change, and you never send a key the registry does not recognise. Every extension's fields are also listed on its page under [TLDs](https://www.domainnameapi.com/docs/tlds/).
:::

**Errors**

| Code | Meaning and fix |
| --- | --- |
| `Dna.ProductService.Product:10002` | Invalid `sorting` value. The format is `field asc` or `field desc`. |
