# Ranked keywords

Canonical page: https://agentsky.dev/tools/dataforseo/labs_google_ranked_keywords_live
JSON record: https://gateway.agentsky.dev/v1/endpoints/dataforseo/labs_google_ranked_keywords_live

Ranked keywords is a tool from DataForSEO, callable through the AgentSky API as dataforseo/labs_google_ranked_keywords_live and billed per call (—).

Takes a website, subdomain, or single page and returns the queries it currently holds rankings for. Every keyword comes with its search metrics — monthly volume, competition, cost per click, bid ranges — alongside the result that earned the position, including the exact position, ranking URL, and result type. Aggregate metrics summarise the whole profile: how many results fall into each position band, an estimated monthly organic traffic figure, and what that traffic would cost to buy through ads instead.

Position-band counts and traffic estimates for the target, plus one row per ranking keyword with its metrics and ranking result.

## Facts

- **Id:** `dataforseo/labs_google_ranked_keywords_live`
- **Provider:** DataForSEO
- **Kind:** tool
- **Billed per:** call
- **Timeout:** 3 min
- **Max request:** 1 MB

## Call it

`POST https://gateway.agentsky.dev/v1/run` with `{"provider": "dataforseo", "endpoint": "labs_google_ranked_keywords_live", "input": {…}}` and `Authorization: Bearer $AGENTSKY_API_KEY`.

### Input fields

| Field | Type | Required | Description |
|---|---|---|---|
| `target` | string | yes | Domain name or page url. the domain name of the target website, subdomain or URL of the target webpage; the domain name must be specified without https:// or www. ; the subdomain must be specified without https:// ; the webpage URL must be specified with https:// or www. |
| `location_code` | integer | no | Location code. Optional. |
| `language_code` | string | no | Language code. Optional. |
| `location_name` | string | no | Which market to report on; omit for every market on record. |
| `language_name` | string | no | Which language to report on; omit for every language on record. |
| `limit` | integer | no | How many rows to return. Default: 100. Max: 1000. |
| `order_by` | array | no | Sort rules for the returned rows. |
| `filters` | array | no | Up to eight conditions the returned rows must meet. |

## Example

### curl

```bash
# Set once per job; reuse this key and the identical body after a timeout.
: "${AGENTSKY_IDEMPOTENCY_KEY:?Set a unique, saved key for this job}"
curl --fail-with-body https://gateway.agentsky.dev/v1/run \
  -H "authorization: Bearer $AGENTSKY_API_KEY" \
  -H "content-type: application/json" \
  -H "Idempotency-Key: $AGENTSKY_IDEMPOTENCY_KEY" \
  -d '{"provider":"dataforseo","endpoint":"labs_google_ranked_keywords_live","input":{"target":"example.com"}}'
# If status is READY/RUNNING, poll GET /v1/runs/{runId} with the same API key.
```

### JavaScript

```js
// Node.js 20+. Save one unique key per job; reuse it with the identical body.
const key = process.env.AGENTSKY_IDEMPOTENCY_KEY;
if (!key || !process.env.AGENTSKY_API_KEY) {
  throw new Error("Set AGENTSKY_API_KEY and a saved AGENTSKY_IDEMPOTENCY_KEY first");
}
const base = "https://gateway.agentsky.dev";
const headers = {
  authorization: `Bearer ${process.env.AGENTSKY_API_KEY}`,
  "content-type": "application/json",
  "Idempotency-Key": key,
};
const body = JSON.stringify({
  "provider": "dataforseo",
  "endpoint": "labs_google_ranked_keywords_live",
  "input": {
    "target": "example.com"
  }
});
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function request(path, options = {}) {
  for (let attempt = 0; attempt < 3; attempt++) {
    let response;
    let data;
    try {
      response = await fetch(base + path, {
        ...options, headers, signal: AbortSignal.timeout(180_000),
      });
      const text = await response.text();
      try { data = JSON.parse(text); } catch { /* Proxy timeouts may return HTML. */ }
    } catch (error) {
      if (attempt === 2) throw error; // Resume with the same key and body.
      await sleep(2000);
      continue;
    }
    // Failed runs can have non-2xx HTTP status. Never regenerate them automatically.
    if (data?.runId && data?.status) return data;
    if (response.ok && data) throw new Error("Unexpected response: missing runId/status");
    if (attempt < 2 && (response.status >= 500 || response.status === 408 || response.status === 429)) {
      await sleep(2000);
      continue; // POST retries keep the original key and body.
    }
    throw new Error(`HTTP ${response.status}: ${JSON.stringify(data ?? "Non-JSON response; retry with the same key and body")}`);
  }
}

let run = await request("/v1/run", { method: "POST", body });
console.log("Save runId:", run.runId);
const deadline = Date.now() + 30 * 60_000;
while (["READY", "RUNNING"].includes(run.status)) {
  if (Date.now() >= deadline) {
    throw new Error(`Still pending: poll /v1/runs/${run.runId} later; do not create a new job`);
  }
  await sleep(5000);
  run = await request(`/v1/runs/${encodeURIComponent(run.runId)}`);
}
console.log("Status:", run.status, "Charge:", run.price?.amount);
if (run.status !== "COMPLETED") {
  throw new Error(`Run ${run.runId}: ${JSON.stringify(run.error ?? run.status)}`);
}
console.log(run.output); // Output shape depends on the endpoint.
// READY/RUNNING price is provisional. Use the terminal receipt for reconciliation.
```

## Pricing

— per call

Reported usage is billed under your account plan. Read the final customer charge from price.amount.value after the run finishes. A RUNNING response is not the final bill.

## Questions

### What does Ranked keywords do?

Takes a website, subdomain, or single page and returns the queries it currently holds rankings for. Every keyword comes with its search metrics — monthly volume, competition, cost per click, bid ranges — alongside the result that earned the position, including the exact position, ranking URL, and result type. Aggregate metrics summarise the whole profile: how many results fall into each position band, an estimated monthly organic traffic figure, and what that traffic would cost to buy through ads instead.

### What does Ranked keywords take?

target is required; the rest of the fields are optional. Send them in the input object of a POST to /v1/run.

### What are the limits on Ranked keywords?

The catalog lists an execution timeout of 3 min. A request can return READY or RUNNING before completion; poll GET /v1/runs/{runId} with the same API key until it reaches a terminal status. An HTTP timeout does not prove the generation stopped.

### How do I retry safely and check the final charge?

Save a unique Idempotency-Key for each job. After a network error or HTML timeout, resend the identical POST body with the same key and API key to recover the existing run, then poll its runId. A new key can start another billable generation. An HTTP error or timeout does not establish the run outcome or final charge. Pending runs can still settle; check the terminal price.amount and billing state, and retain the runId for reconciliation. Do not automatically create a new job after a terminal failure.
