# POST /v1/domains/gap

Keyword gap between two domains: opportunity, shared, or unique rankings, volume-enriched.

**Credits:** 15 credits per call.

**Timeout:** 60s

## Parameters

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `domains` | array | yes | — | Put your own domain first -- every mode is defined relative to domains[0]. Array of string. Max 2 items. |
| `mode` | string | no | "gap" | gap: keywords domains[1] ranks for that domains[0] does not. shared: both rank. unique: only domains[0] ranks. Optional, default "gap". |
| `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". |
| `date_from` | string | no | — | ISO YYYY-MM-DD, inclusive. Optional, default 730 days ago -- sets how far back in the archive the comparison reaches. |
| `limit` | integer | no | 100 | Optional, default 100. 1-1000. |
| `offset` | integer | no | 0 | Optional, default 0. 0-10000. |

## Request

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

{
  "domains": [
    "semrush.com",
    "ahrefs.com"
  ],
  "mode": "gap",
  "engine": "google",
  "location": 2840,
  "language": "en",
  "device": "desktop",
  "limit": 100,
  "offset": 0
}
```

## 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_ymjerx7b2jv6d2pvyszaahiq",
  "request_id": "req_a47ll7lfavaulfejso36d4ut3m",
  "object": "domain_gap",
  "created_at": "2026-08-09T08:31:02Z",
  "elapsed_ms": 180,
  "cache": "miss",
  "credits": {
    "charged": 15,
    "balance": 9857
  },
  "data": {
    "mode": "gap",
    "domains": [
      "semrush.com",
      "ahrefs.com"
    ],
    "items": [
      {
        "keyword": "search engine optimization",
        "volume": 18100,
        "ranks": [
          null,
          {
            "rank": 27,
            "url": "https://ahrefs.com/blog/what-is-seo/",
            "date": "2026-06-28"
          }
        ]
      },
      "…"
    ],
    "totals": [
      2834,
      1315
    ],
    "matched_count": 409,
    "coverage": {
      "from": "2026-06-28",
      "observations": 409
    },
    "limit": 100,
    "offset": 0
  }
}
```

## Response fields

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

| Field | Description |
| --- | --- |
| `data.mode` | Comparison mode that was applied: gap, shared, or unique. |
| `data.domains` | The two domains you compared, in the order you sent them. |
| `data.items` | One row per matching keyword, up to limit. |
| `data.items[].keyword` | The keyword. |
| `data.items[].volume` | Search volume for the keyword. |
| `data.items[].ranks` | Two-element array positionally aligned to domains: ranks[0] is domains[0]'s rank, ranks[1] is domains[1]'s rank on this keyword. Each entry is null (that domain doesn't rank) or {rank, url, date}. |
| `data.totals` | Two-element array positionally aligned to domains: total ranked-keyword count for each domain. |
| `data.matched_count` | Total keywords matching mode, before limit/offset truncation. |
| `data.coverage` | How much of the rankings archive this comparison drew from. |
| `data.coverage.from` | Earliest observation date among the returned items, floor-clamped to date_from. |
| `data.coverage.observations` | Same as matched_count. |
| `data.limit` | Page size actually used (echoes the request). |
| `data.offset` | Pagination offset actually used (echoes the request). |
