# getdomaindata.com API v1

Base URL: https://getdomaindata.com/api/v1

Look up what any domain runs, over HTTPS, in JSON. Every response is `{"data": ...}` on success or `{"error": "<code>"}` on failure, with standard HTTP status codes. Nothing here describes how a figure is measured; the response fields are the interface.

## Getting access

API access comes with a paid plan (free accounts have 0 lookups). Create an account at https://getdomaindata.com/signup, choose a plan at https://getdomaindata.com/pricing, then create a key on https://getdomaindata.com/account. The full key is shown once. There is no charge for extra keys, and keys belong to the plan, not to a seat.

## Authentication

Send `Authorization: Bearer gdd_live_YOUR_KEY` on every request. Keys look like gdd_live_ followed by 48 hex characters. Never put a key in a URL or a spreadsheet cell: it lands in logs and in anyone's hands who sees the sheet. A revoked key stops on the next request.

## Lookups and limits

One lookup is one domain queried, however many technologies come back. A batch of 100 domains spends 100 lookups, found or not. Technology counts and the catalog are free.

| Plan | Lookups a month | Requests a minute |
|---|---|---|
| Pro | 2,000 | 60 |
| Business | 50,000 | 300 |
| Business Plus | 250,000 | 600 |

Extra lookups come in packs of 25,000. Lookups reset on the first of the month. Every response carries X-Quota-Limit, X-Quota-Remaining and X-Quota-Reset. At the limit the API answers 402 until the month rolls over or a pack is added; it never throttles silently.

## Errors

| Status | Code | Meaning | What to do |
|---|---|---|---|
| 401 | invalid_key | The key is missing, malformed, or revoked. | Check the Authorization header and the key on your account page. Revoked keys stop on the next request. |
| 402 | quota_exhausted | This month's lookups are spent. | Wait for the first of the month, or add a pack from the account page. The API never throttles silently. |
| 429 | rate_limited | Too many requests this minute for your plan. | Back off until the reset time in the body, then retry. |
| 404 | not_found | We do not hold that domain or technology. | Nothing to retry. The lookup is still spent for domain calls. |
| 400 | bad_domain | We could not read a hostname out of what you sent. No lookup was spent. | Send a hostname or a URL. We drop the scheme, path, port and a leading www. before the lookup, so https://www.acme.com/about and acme.com are the same request. |
| 400 | bad_host | Same as bad_domain: nothing hostname-shaped in the input. No lookup was spent. | Same rule as bad_domain. |
| 400 | bad_batch | The body is not {"domains": [...]} with 1 to 100 entries we can read as hostnames. No lookup was spent. | Send a JSON body with a domains array of at most 100 hostnames or URLs. The response names up to ten entries we could not read, in an invalid array. |
| 503 | metering_unavailable | The quota could not be read, so nothing was served. | Retry in a moment. No lookup was spent. |

## Endpoints

### GET /domain/{domain}

Everything we serve for one hostname: its technologies, network, platform, identity fields, and the latest traffic and employee estimates where we hold them.

Costs: 1 lookup.

Parameters:

- `domain` (path, string, required): A hostname or a URL, for example allbirds.com, shop.example.co.uk or https://www.allbirds.com/pages/about. We drop the scheme, path, port and a leading www. Profiles are per hostname, so a subdomain is its own record.

Request:

```bash
curl https://getdomaindata.com/api/v1/domain/allbirds.com \
  -H "Authorization: Bearer gdd_live_YOUR_KEY"
```

Response (real row, abridged where marked):

```json
{
  "data": {
    "domain": "allbirds.com",
    "resolves": true,
    "technologies": [
      "Amazon SES",
      "Apple",
      "Cloudflare",
      "DigiCert/Azure",
      "DocuSign",
      "Google",
      "Google Tag Manager",
      "HSTS",
      "..."
    ],
    "network": "Shopify",
    "platform": "shopify",
    "registrar": null,
    "age_days": null,
    "country_iso": null,
    "lang_norm": "en",
    "http_status": null,
    "redirect_to": null,
    "traffic": {
      "estimated_monthly_visits": 2137979,
      "band": "1M-10M",
      "as_of": "2026-08"
    },
    "employees": {
      "apex": "allbirds.com",
      "low": 501,
      "high": 2000,
      "point": null,
      "as_of": "2026-09-10"
    }
  }
}
```

Fields:

- `domain` (string): The hostname we looked up: your input lower-cased, with any scheme, path, port and leading www. removed.
- `resolves` (boolean | null): Whether the hostname resolved when last checked.
- `technologies` (string[] | null): Technology names we detected on the site, alphabetical.
- `network` (string | null): The network or provider the site is served from.
- `platform` (string | null): The site platform when it is one we classify (for example shopify).
- `registrar` (string | null): Registrar name where known.
- `age_days` (number | null): Days since registration where known.
- `country_iso` (string | null): Two-letter country code where we could place the site.
- `lang_norm` (string | null): Primary language code where known.
- `http_status` (number | null): Status code from the last fetch of the homepage where known.
- `redirect_to` (string | null): Where the homepage redirected, where it did.
- `traffic` (object | null): Latest traffic estimate: estimated_monthly_visits, band, as_of (YYYY-MM). Null when we hold none.
- `employees` (object | null): Latest employee estimate for the organization behind the apex: apex, low, high (null = open-ended), point (a specific figure when we hold one), as_of. Null when we hold none.

Errors: invalid_key, rate_limited, quota_exhausted, bad_domain, not_found, metering_unavailable.

Note: A not_found answer still spends the lookup: the question was asked and answered. A bad_domain answer spends nothing: the question could not be asked.

### POST /domains/batch

Up to 100 profiles in one call, each with the same fields as the single-domain profile, including the latest traffic and employee estimates. Hostnames we do not hold come back in a missing array.

Costs: 1 lookup per domain, found or not.

Parameters:

- `domains` (body, string[], required): 1 to 100 hostnames or URLs. Each is cleaned the same way as the single-domain path, and duplicates are collapsed before they are counted, so https://www.acme.com/ and acme.com are one lookup.

Request:

```bash
curl https://getdomaindata.com/api/v1/domains/batch \
  -H "Authorization: Bearer gdd_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domains":["allbirds.com","example.invalid"]}'
```

Response (real row, abridged where marked):

```json
{
  "data": [
    {
      "domain": "allbirds.com",
      "resolves": true,
      "technologies": [
        "Amazon SES",
        "Apple",
        "Cloudflare",
        "DigiCert/Azure",
        "DocuSign",
        "Google",
        "Google Tag Manager",
        "HSTS",
        "..."
      ],
      "network": "Shopify",
      "platform": "shopify",
      "registrar": null,
      "age_days": null,
      "country_iso": null,
      "lang_norm": "en",
      "http_status": null,
      "redirect_to": null,
      "traffic": {
        "estimated_monthly_visits": 2137979,
        "band": "1M-10M",
        "as_of": "2026-08"
      },
      "employees": {
        "apex": "allbirds.com",
        "low": 501,
        "high": 2000,
        "point": null,
        "as_of": "2026-09-10"
      }
    }
  ],
  "missing": [
    "example.invalid"
  ]
}
```

Fields:

- `data` (object[]): One profile per hostname we hold, same shape as the domain endpoint.
- `missing` (string[]): Hostnames from your list that we do not hold. Each still counted as one lookup.

Errors: invalid_key, rate_limited, quota_exhausted, bad_batch, metering_unavailable.

### POST /enrich

The profile, the latest traffic estimate and the latest employee estimate for one domain as a single flat object, built for tools that map JSON paths onto table columns: Clay's HTTP API column, n8n, Zapier, a spreadsheet. Every key is always present and null where we hold nothing. A hostname we hold nothing on is a 200 with found false, not a 404, so a table run does not stop on it.

Costs: 1 lookup.

Parameters:

- `input` (body, string, required): A domain, a URL or a work email address. We drop the scheme, path, port, a leading www. and the part before an @ ourselves. The same call works as GET /enrich?input=... for tools that only send GET.

Request:

```bash
curl https://getdomaindata.com/api/v1/enrich \
  -H "Authorization: Bearer gdd_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input":"https://www.gymshark.com/"}'
```

Response (real row, abridged where marked):

```json
{
  "data": {
    "domain": "gymshark.com",
    "found": true,
    "resolves": true,
    "platform": "shopify",
    "network": "Shopify",
    "country_iso": null,
    "lang_norm": "en",
    "technologies": [
      "Adobe",
      "Adobe Acrobat Sign",
      "Afterpay",
      "Algolia",
      "Amazon CloudFront",
      "Amazon Web Services",
      "Anthropic",
      "Apple",
      "Atlassian",
      "Cloudflare",
      "Contentful",
      "Facebook Pixel",
      "GlobalSign",
      "Google",
      "Google Tag Manager",
      "HSTS",
      "HTTP/3",
      "Linkedin Insight Tag",
      "Mailchimp",
      "Meta",
      "Microsoft 365",
      "Mixpanel",
      "mParticle",
      "Next.js",
      "OneTrust",
      "Open Graph",
      "OpenAI",
      "PayPal",
      "Proofpoint",
      "PWA",
      "React",
      "SendGrid",
      "Shopify",
      "Webpack",
      "Zoom"
    ],
    "technologies_text": "Adobe, Adobe Acrobat Sign, Afterpay, Algolia, Amazon CloudFront, Amazon Web Services, Anthropic, Apple, Atlassian, Cloudflare, Contentful, Facebook Pixel, GlobalSign, Google, Google Tag Manager, HSTS, HTTP/3, Linkedin Insight Tag, Mailchimp, Meta, Microsoft 365, Mixpanel, mParticle, Next.js, OneTrust, Open Graph, OpenAI, PayPal, Proofpoint, PWA, React, SendGrid, Shopify, Webpack, Zoom",
    "technology_count": 35,
    "tech_ecommerce": [
      "Shopify"
    ],
    "tech_payments": [
      "Afterpay",
      "PayPal"
    ],
    "tech_website_platforms": [
      "Contentful"
    ],
    "tech_wordpress": null,
    "tech_analytics": [
      "Google Tag Manager"
    ],
    "tech_advertising": [
      "Facebook Pixel",
      "Linkedin Insight Tag"
    ],
    "tech_email_marketing": [
      "Mailchimp"
    ],
    "tech_marketing": null,
    "tech_chat_support": null,
    "tech_sales": null,
    "tech_forms": null,
    "tech_business_email": [
      "Microsoft 365",
      "Proofpoint"
    ],
    "tech_business_collaboration": [
      "Adobe",
      "Atlassian",
      "Zoom"
    ],
    "tech_communications": [
      "SendGrid"
    ],
    "tech_hosting": [
      "Amazon Web Services"
    ],
    "tech_cdn": [
      "Amazon CloudFront",
      "Cloudflare"
    ],
    "tech_security": [
      "Apple",
      "GlobalSign",
      "HSTS"
    ],
    "tech_privacy": [
      "OneTrust"
    ],
    "tech_web_servers": [
      "HTTP/3"
    ],
    "tech_languages_frameworks": null,
    "tech_frontend_frameworks": [
      "Next.js",
      "PWA",
      "React"
    ],
    "tech_javascript_libraries": [
      "Webpack"
    ],
    "tech_databases": null,
    "tech_performance": null,
    "tech_media": null,
    "tech_social_media_community": [
      "Meta"
    ],
    "tech_seo": [
      "Google",
      "Open Graph"
    ],
    "tech_fonts_icons": null,
    "tech_developer_tools": null,
    "tech_other": [
      "Adobe Acrobat Sign",
      "Algolia",
      "Anthropic",
      "Mixpanel",
      "mParticle",
      "OpenAI"
    ],
    "estimated_monthly_visits": 12676275,
    "traffic_band": "10M-100M",
    "traffic_month": "2026-09",
    "estimated_employees_low": 501,
    "estimated_employees_high": 2000,
    "employees_range": "501 to 2,000",
    "employees_as_of": "2026-09-10",
    "employees_apex": "gymshark.com"
  }
}
```

Fields:

- `domain` (string): The hostname we looked up, after cleanup.
- `found` (boolean): False when every other field is null: no profile, no traffic estimate for the hostname, no employee estimate for its registrable domain. The lookup is spent either way.
- `resolves` (boolean | null): Whether the hostname resolved when last checked.
- `platform` (string | null): The site platform when it is one we classify (for example shopify).
- `network` (string | null): The network or provider the site is served from.
- `country_iso` (string | null): Two-letter country code where we could place the site.
- `lang_norm` (string | null): Primary language code where known.
- `technologies` (string[] | null): Every technology we detected on the site, alphabetical.
- `technologies_text` (string | null): The same list as one comma-separated string, for CRM fields and filters.
- `technology_count` (number | null): How many technologies we detected.
- `tech_<category>` (string[] | null): The technologies in one of our 30 categories, one key per category, always present: tech_ecommerce, tech_payments, tech_website_platforms, tech_wordpress, tech_analytics, tech_advertising and so on through tech_other. Null when the stack has nothing in that category.
- `estimated_monthly_visits` (number | null): Our estimated visits for the latest month, for this hostname only.
- `traffic_band` (string | null): The range the estimate falls in, for example 10M-100M.
- `traffic_month` (string | null): The month the estimate is for, YYYY-MM.
- `estimated_employees_low` (number | null): Lower bound of the employee band for the organization behind the hostname.
- `estimated_employees_high` (number | null): Upper bound; null with a low value means the top band is open-ended.
- `employees_range` (string | null): The band as the site prints it, for example 501 to 2,000 or 20,000+.
- `employees_as_of` (string | null): Date of the employee estimate.
- `employees_apex` (string | null): The registrable domain the employee estimate belongs to.

Errors: invalid_key, rate_limited, quota_exhausted, bad_domain, metering_unavailable.

Note: Same figures as /domain, /traffic and /employees, under flat names. The traffic history and the exact employee figure are not in this row; the standalone endpoints keep them.
Note: A 429 carries a Retry-After header in seconds. Tools that honor it (Clay, n8n, Zapier) back off on their own.

### GET /traffic/{host}

Our estimated monthly visits for one hostname, with the month-by-month history behind the current figure. The basic profile already carries the latest figure; use this when you want the series.

Costs: 1 lookup.

Parameters:

- `host` (path, string, required): A hostname or a URL. Estimates are per hostname and do not carry across subdomains; a leading www. is folded into the bare domain, which is the row we hold.

Request:

```bash
curl https://getdomaindata.com/api/v1/traffic/allbirds.com \
  -H "Authorization: Bearer gdd_live_YOUR_KEY"
```

Response (real row, abridged where marked):

```json
{
  "data": {
    "host": "allbirds.com",
    "estimated_monthly_visits": 2137979,
    "band": "1M-10M",
    "as_of": "2026-08",
    "history": [
      {
        "month": "2026-06",
        "visits": 2050000
      },
      {
        "month": "2026-07",
        "visits": 2110000
      },
      {
        "month": "2026-08",
        "visits": 2137979
      }
    ]
  }
}
```

Fields:

- `host` (string): The hostname asked for.
- `estimated_monthly_visits` (number): Our estimate for the latest month.
- `band` (string): The range the estimate falls in, for example 1M-10M.
- `as_of` (string): The latest month, YYYY-MM.
- `history` ({month, visits}[]): Earlier months where we hold a figure, oldest first.

Errors: invalid_key, rate_limited, quota_exhausted, bad_host, not_found, metering_unavailable.

Note: The history values in this example are illustrative of the shape; the current figure and band are the live row.

### GET /employees/{host}

Our estimated headcount for the organization behind a hostname. Keyed by the registrable domain, so every host under an apex returns the same figure.

Costs: 1 lookup.

Parameters:

- `host` (path, string, required): Any hostname or URL under the organization's domain.

Request:

```bash
curl https://getdomaindata.com/api/v1/employees/allbirds.com \
  -H "Authorization: Bearer gdd_live_YOUR_KEY"
```

Response (real row, abridged where marked):

```json
{
  "data": {
    "host": "allbirds.com",
    "apex": "allbirds.com",
    "estimated_employees": {
      "low": 501,
      "high": 2000,
      "point": null
    },
    "as_of": "2026-09-10"
  }
}
```

Fields:

- `apex` (string): The registrable domain the estimate belongs to.
- `estimated_employees.low` (number): Lower bound of the band.
- `estimated_employees.high` (number | null): Upper bound; null means the top band is open-ended.
- `estimated_employees.point` (number | null): A specific figure when we hold one. Still an estimate.
- `as_of` (string | null): Date of the estimate.

Errors: invalid_key, rate_limited, quota_exhausted, bad_host, not_found, metering_unavailable.

### GET /technology/{name}

How many domains we detected running one technology. Aggregates only; the domain lists themselves are a download, not an API surface.

Costs: Free.

Parameters:

- `name` (path, string, required): The technology name exactly as it appears on its page, URL-encoded, for example Google%20Tag%20Manager.

Request:

```bash
curl https://getdomaindata.com/api/v1/technology/Shopify \
  -H "Authorization: Bearer gdd_live_YOUR_KEY"
```

Response (real row, abridged where marked):

```json
{
  "data": {
    "tech": "Shopify",
    "domains": 7407815
  }
}
```

Fields:

- `tech` (string): The technology name.
- `domains` (number): Domains we detected it on, per hostname.

Errors: invalid_key, not_found, metering_unavailable.

### GET /meta/technologies

Every technology we track with its domain count, grouped by category. Cache it; it changes with each data release.

Costs: Free.

Request:

```bash
curl https://getdomaindata.com/api/v1/meta/technologies \
  -H "Authorization: Bearer gdd_live_YOUR_KEY"
```

Response (real row, abridged where marked):

```json
{
  "data": [
    {
      "category": "ecommerce",
      "technologies": [
        {
          "tech": "Shopify",
          "domains": 7407815
        },
        {
          "tech": "WooCommerce",
          "domains": 0
        },
        "..."
      ]
    },
    "..."
  ]
}
```

Fields:

- `data[].category` (string): Category slug.
- `data[].technologies[]` ({tech, domains}[]): Technologies in the category with their domain counts.

Errors: invalid_key.

Note: The WooCommerce count above is a placeholder in the example only; the live answer carries the real figure.

## Google Sheets and Excel

Google Sheets' built-in IMPORTDATA cannot send a header, so do not put the key in a URL. Use the Apps Script custom functions at https://getdomaindata.com/docs/api/spreadsheets: the key lives in Script Properties, calls carry the Authorization header, and results are cached for six hours so recalculation does not spend lookups again. Excel's Power Query sends headers natively.

## Clay and other table tools

For a tool that maps JSON paths onto columns (Clay's HTTP API column, n8n, Zapier), use GET https://getdomaindata.com/api/v1/enrich?input=<domain, URL or work email> with the Authorization header. One lookup per call. The answer is one flat object with every key always present and null where we hold nothing; a hostname we hold nothing on is a 200 with found: false, not a 404, so a table run does not stop. Setup steps and the column list: https://getdomaindata.com/docs/api/clay.

## Contact

contact@getdomaindata.com. Terms: https://getdomaindata.com/terms. Data license for bulk downloads: https://getdomaindata.com/data-license.
