# Domain HTTP API

Source: https://modulify.ai/docs/publish/domain-http-api

Check which domains are free and what each extension costs from any website, script or tool, with no key.

The domain search of the [Domains page](https://modulify.ai/docs/publish/buy-a-domain) can be read from anywhere, such as a landing page with a domain checker, a pricing table or a script. It is a small, read-only API with two `GET` endpoints. There is no key and no sign in, any website can call it from the browser, and it answers with the prices a workspace pays. Every domain in an answer also carries a link that opens the Domains page with that domain searched, ready to buy.

The base address is `https://api.modulify.ai/domains`.

A service that needs DNS records on a domain bought here can hand its owner a link that fills them in for one-click approval, see [Add records with a link](https://modulify.ai/docs/publish/add-records-with-a-link).

## Check a name

`GET /availability` checks whether domains are free and what they cost.

```bash
curl "https://api.modulify.ai/domains/availability?name=acme&tld=com"
```

| Query | Value |
|---|---|
| `name` | Required. A name such as `acme`, or a full domain such as `acme.com`. A leading `https://`, a `www.` and anything after the domain are ignored. |
| `tld` | Optional. An extension such as `com` or `co.uk`, with or without its leading dot. It is added to the first part of `name`, so `name=acme.com&tld=net` checks `acme.net`. |

With a bare name, it checks the extensions Modulify features, such as `.com`, `.net`, `.org`, `.co`, `.io` and `.ai`. With an extension, on `name` or in `tld`, it checks that domain first and the featured extensions after it. Modulify sells the extensions Name.com registers, apart from those whose registry needs extra details Modulify cannot collect yet, such as `.us` or `.eu`.

```json
{
    "success": true,
    "message": "Domains were searched successfully.",
    "data": {
        "Label": "acme",
        "Requested": "acme.com",
        "UnsupportedTld": null,
        "Currency": "USD",
        "Results": [
            {
                "Name": "acme.com",
                "Tld": "com",
                "Available": false,
                "Premium": false,
                "PriceCents": null,
                "RenewalPriceCents": null,
                "Years": 1,
                "RenewalYears": 1,
                "Currency": "USD",
                "BuyUrl": "https://modulify.ai/dashboard/domains?name=acme&tld=com"
            },
            {
                "Name": "acme.studio",
                "Tld": "studio",
                "Available": true,
                "Premium": false,
                "PriceCents": 2999,
                "RenewalPriceCents": 3499,
                "Years": 1,
                "RenewalYears": 1,
                "Currency": "USD",
                "BuyUrl": "https://modulify.ai/dashboard/domains?name=acme&tld=studio"
            }
        ]
    },
    "code": 200
}
```

Treat a call as failed unless the HTTP status is ok **and** `success` is `true`, and read the answer from `data`. The envelope also carries `version`, the build of the API, which you can ignore.

| Field | Meaning |
|---|---|
| `Label` | The name that was checked, without its extension |
| `Requested` | The full domain you asked for, when its extension is on sale, otherwise `null` |
| `UnsupportedTld` | The extension you asked for when Modulify does not sell it, otherwise `null`. The results then cover the featured extensions |
| `Currency` | `USD`. Every price is in US cents |
| `Results` | One entry per domain, the one you asked for first |

Each entry in `Results`:

| Field | Meaning |
|---|---|
| `Name` | The full domain |
| `Tld` | Its extension |
| `Available` | `false` means Name.com says the name cannot be registered, almost always because someone has registered it. Otherwise it is `true`, and the name can be bought whenever `PriceCents` is set |
| `Premium` | `true` when the registry prices the name above its usual rate |
| `PriceCents` | What registering it costs for `Years` years. `null` when it is taken, or when it cannot be bought here right now: Name.com could not check it or gave no price for it, or the name has characters other than a to z, 0 to 9 and hyphens |
| `RenewalPriceCents` | What renewing it costs for `RenewalYears` years, or `null` when it is taken or not on sale |
| `Years` | The shortest term the extension is sold for, such as `2` for `.ai` |
| `RenewalYears` | The shortest term it renews for |
| `BuyUrl` | Opens the Domains page with this domain searched, see [Link to a search](https://modulify.ai/docs/publish/buy-a-domain#link-to-a-search) |

An answer can be up to 10 minutes old, so a domain registered a moment ago can still show as available, and the answer is sent with `Cache-Control: public, max-age=60`. The price is worked out again when someone buys the domain, so what they pay is current.

## List prices

`GET /prices` lists what each extension costs.

```bash
curl "https://api.modulify.ai/domains/prices?tlds=com,io,ai"
```

| Query | Value |
|---|---|
| `tld` | One extension, such as `com` |
| `tlds` | Several extensions, separated by commas or spaces, up to 100 in one call |
| `featured` | `true` for the featured extensions only |

Leave them all out to list every extension on sale. The list is sorted by extension, apart from `featured=true`, which lists the featured extensions in the order a bare name is checked against them, starting with `com`.

```json
{
    "success": true,
    "message": "Domain prices were listed successfully.",
    "data": {
        "Currency": "USD",
        "Extensions": [
            {
                "Tld": "ai",
                "Featured": true,
                "Registration": { "PriceCents": 15998, "Years": 2 },
                "Renewal": { "PriceCents": 15998, "Years": 2 }
            },
            {
                "Tld": "com",
                "Featured": true,
                "Registration": { "PriceCents": 1299, "Years": 1 },
                "Renewal": { "PriceCents": 1499, "Years": 1 }
            }
        ]
    },
    "code": 200
}
```

| Field | Meaning |
|---|---|
| `Tld` | The extension |
| `Featured` | `true` when a bare name is checked against it |
| `Registration` | `PriceCents` for registering a name for `Years` years, the shortest term the extension is sold for |
| `Renewal` | `PriceCents` for renewing it for `Years` years. `Renewal` is always there, and its `PriceCents` is `null` when Name.com gives no renewal price |

These are the prices of an ordinary name. A premium name costs more, and only [Check a name](https://modulify.ai/docs/publish/domain-http-api#check-a-name) gives its price. An extension you ask for that is not on sale is left out of `Extensions`, and asking for a single one that is not on sale returns `404`, for example `.local domains are not for sale!` The answer is sent with `Cache-Control: public, max-age=300`.

## From a website

Both endpoints answer every origin, so a page can call them straight from the browser. They take `GET` only and never read cookies or credentials.

```javascript
const CheckDomain = async (name) => {
    const response = await fetch(`https://api.modulify.ai/domains/availability?name=${encodeURIComponent(name)}`)
    const { success, data, message } = await response.json()

    if (!response.ok || !success) throw new Error(message)

    return data.Results.filter(result => result.PriceCents !== null)
}
```

Send people to a result's `BuyUrl` to buy it. Someone who is signed out signs in first, and the search runs once they are in.

## Limits

Answers Modulify already holds cost nothing. Only lookups that have to ask Name.com count, toward 20 a minute from one network and 60 a minute shared by every caller. Past either, `/availability` answers `429` with a `Retry-After` header giving the seconds to wait, repeated in `data.retryAfter`. It answers the same way, with a wait of 60 seconds, while Name.com itself is too busy to answer. `/prices` never asks Name.com, so it has no limit.

Searches from the Domains page and from AI clients never use this allowance.

## Errors

| Status | Cause | Message |
|---|---|---|
| `400` | `name` is missing or is not a domain name | "Pass a name like acme in name, with an extension like com in tld or on the name itself!" |
| `400` | `tld` is not an extension | "Pass an extension like com or co.uk in tld!" |
| `400` | An extension in `tld` or `tlds` on `/prices` is not an extension | "Pass extensions like com or co.uk in tld or tlds!" |
| `400` | More than 100 extensions on `/prices` | "Ask for up to 100 extensions at once!" |
| `404` | The single extension asked for on `/prices` is not on sale | Names it, for example ".local domains are not for sale!" |
| `503` | `/prices` could not load the price list, for example before Modulify first read it from Name.com | "Domain prices are not available right now. Please try again!" |
| `429` | Your network used its lookups of the minute | "Too many domain lookups are coming from your network. Try again in 12 seconds!" |
| `429` | Every caller together used the lookups of the minute | "Domain search is busy right now. Try again in 12 seconds!" |
| `429` | Name.com is too busy to answer | "The registrar is busy right now. Try again in a moment!" |
| `502` | Name.com could not be reached | "Domain search is not available right now. Please try again!" |
| `503` | Modulify could not count the lookup | "Domain search is not available right now. Please try again!" |
| `503` | Modulify is not selling domains right now | "Domain search is not available right now!" or "Domain prices are not available right now!" |
| `500` | Something unexpected went wrong | "Something went wrong while searching for domains!" or "Something went wrong while listing domain prices!" |

`429`, `502` and `503` are worth retrying after a pause. Never retry in a tight loop.