# POST /v1/domains/competitors

Competitor discovery from the SERP archive: domains that rank for the same keywords, ranked by overlap.

**Credits:** 30 credits per call.

**Timeout:** 60s

## Parameters

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `domain` | string | yes | — | Domain to find competitors for (bare hostname, lowercased). Required, max 255 chars, no spaces. |
| `engine` | string | no | "google" | Optional, default "google". Same enum as /v1/search. |
| `location` | integer | no | 2840 | Google Ads geotarget ID. Optional, default 2840 (United States). Look one up with /v1/locations. |
| `language` | string | no | "en" | ISO language code. Optional, default "en". |
| `device` | string | no | "desktop" | One of: desktop, mobile. Optional, default "desktop". |
| `limit` | integer | no | 25 | Number of competitors to return. Optional, default 25. 1-100. |
| `date_from` | string | no | — | ISO YYYY-MM-DD, inclusive. Optional, default 730 days ago -- sets how far back in the archive the comparison reaches. |

## Request

```http
POST /v1/domains/competitors HTTP/1.1
Authorization: Bearer sof_live_your_key_here
Content-Type: application/json

{
  "domain": "semrush.com",
  "engine": "google",
  "location": 2840,
  "language": "en",
  "device": "desktop",
  "limit": 25
}
```

## Response

Wrapped in the standard envelope (id, object, created_at, elapsed_ms, cache, credits) -- documented once on The Contract. The body below is a real envelope with this endpoint's `data` shape; values vary per request, and a `…` marks an array cut short for display.

```json
{
  "id": "doma_nvutaccvrn47zko3p5k2r2x5",
  "request_id": "req_5k6bntzg7re3poixv56n7ugrly",
  "object": "domain_competitors",
  "created_at": "2026-08-09T08:27:48Z",
  "elapsed_ms": 180,
  "cache": "miss",
  "credits": {
    "charged": 30,
    "balance": 9857
  },
  "data": {
    "domain": "semrush.com",
    "keywords_total": 2834,
    "keywords_considered": 200,
    "total_competitors": 1743,
    "competitors": [
      {
        "domain": "reddit.com",
        "shared_keywords": 106,
        "keywords_total": 114768,
        "avg_rank": 3.9,
        "target_avg_rank": 1,
        "overlap": 0.53,
        "shared_volume": 370910,
        "sample_keywords": [
          {
            "keyword": "google search console",
            "volume": 368000
          },
          "…"
        ]
      },
      "…"
    ],
    "coverage": {
      "from": "2026-05-20",
      "observations": 200
    }
  }
}
```

## Response fields

What each field in `data` (above) means.

| Field | Description |
| --- | --- |
| `data.domain` | The domain you requested. |
| `data.keywords_total` | Total keywords the domain ranks for (unbounded by the archive window). |
| `data.keywords_considered` | Number of the domain's archived keyword rankings actually considered for this comparison (bounded by the archive window). |
| `data.total_competitors` | Total number of competing domains found, before limit truncation. |
| `data.competitors` | Competing domains, ranked by keyword overlap, up to limit. |
| `data.competitors[].domain` | The competitor's domain. |
| `data.competitors[].shared_keywords` | Number of keywords both domains rank for. |
| `data.competitors[].keywords_total` | Total keywords the competitor ranks for. |
| `data.competitors[].avg_rank` | Competitor's average rank across the shared keywords. |
| `data.competitors[].target_avg_rank` | Your domain's average rank across the same shared keywords. |
| `data.competitors[].overlap` | shared_keywords / keywords_considered, rounded to 2dp; null when keywords_considered is 0. |
| `data.competitors[].shared_volume` | Combined search volume of the shared keywords. |
| `data.competitors[].sample_keywords` | A few example shared keywords, each with its search volume. |
| `data.competitors[].sample_keywords[].keyword` | The sample keyword. |
| `data.competitors[].sample_keywords[].volume` | Search volume for the sample keyword. |
| `data.coverage` | How much of the rankings archive this comparison drew from. |
| `data.coverage.from` | Earliest observation date the comparison could have used. |
| `data.coverage.observations` | Same as keywords_considered. |
