> For the complete documentation index, see [llms.txt](https://help.rankability.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.rankability.com/api/api-optimize-endpoint.md).

# Optimize API endpoint

Use Optimize to score one existing public page against current search competitors for a target keyword. Agent API and MCP runs are asynchronous and durable: the start request returns an `optimization_id` immediately, and the result remains available through get and list operations.

This is separate from a persistent Copywriter existing-content project. Optimize saves the scoring run and its evidence, but it does not create or edit a Copywriter draft.

## Scope and usage

The key needs `optimize:run`. Before starting work, read `GET /api/agent/v1/usage` and estimate `optimize_page` with `GET /api/agent/v1/usage/estimate?operation=optimize_page`.

```http
POST https://app.rankability.com/api/agent/v1/optimize
```

A stable `Idempotency-Key` is required. Repeating the same logical request with the same key returns the same durable run and cannot create a second reservation or provider run.

## Start a run

```bash
curl -X POST https://app.rankability.com/api/agent/v1/optimize \
  -H "Authorization: Bearer rk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: optimize-example-seo-guide-v1" \
  -d '{
    "url": "https://example.com/seo-guide",
    "keyword": "seo guide",
    "language": "en-US",
    "client_id": "4746d28b-0a38-43f9-a1ca-5e8402ef73d4"
  }'
```

| Field       | Required | Behavior                                                           |
| ----------- | -------- | ------------------------------------------------------------------ |
| `url`       | Yes      | Public HTTP or HTTPS page to score                                 |
| `keyword`   | Yes      | Target query used for competitor research and scoring              |
| `language`  | No       | Language code up to 10 characters; defaults to `en-US`             |
| `client_id` | No       | Organization-owned client used to attribute and filter run history |

Private IP ranges, localhost, link-local addresses, `.local` or `.internal` hosts, and cloud metadata targets are rejected before a reservation or job is created.

The accepted response is `202`:

```json
{
  "optimization_id": "opt-0123456789abcdef",
  "status": "queued",
  "client_id": "4746d28b-0a38-43f9-a1ca-5e8402ef73d4",
  "url": "https://example.com/seo-guide",
  "keyword": "seo guide",
  "language": "en-US",
  "created_at": "2026-08-24T20:00:00.000Z",
  "started_at": null,
  "completed_at": null,
  "error": null
}
```

## Poll and retrieve the result

Use the status view while work is queued or running:

```http
GET /api/agent/v1/optimizations/:optimization_id?view=status
```

Statuses are `queued`, `running`, `complete`, or `failed`. After completion, request `view=summary` for the score, source word count, competitor count, duration, and billing finalization state. Use `view=full` only when the complete entity, category, scoring, and competitor evidence is needed.

```http
GET /api/agent/v1/optimizations/:optimization_id?view=full
```

The full response returns the saved upstream Optimize result under `result`. A successful result can include `rankabilityScore`, `categoryScores`, `scoreDetails`, `entities`, `sourceWordCount`, `competitorCount`, `competitors`, and `duration`.

Competitor pages that cannot be fetched are omitted. A smaller comparison set means less evidence; it does not mean Rankability verified every result page. Scores are dated observations because search results and page content change.

## List past runs

```http
GET /api/agent/v1/optimizations?client_id=:clientId&status=complete&limit=25&offset=0
```

History is organization-scoped, newest first, and capped at 100 rows per page. Optional filters are `client_id`, `status`, `keyword`, and `url`. List results are summaries and do not repeat full result payloads.

## Failure and retry behavior

Failed provider work releases the run's reservation. Successful work finalizes that same reservation once. If finalization needs reconciliation after a usable result exists, the artifact remains available with `billing_status: reconciliation_pending`; the system does not create an unrelated second debit.

Do not repeatedly poll the full view. Poll status with a bounded interval, then retrieve the full result once. A terminal failed run remains auditable; use a new idempotency key only when the user approves a genuinely new attempt.

| Status   | Code or state                                  | Action                                                                    |
| -------- | ---------------------------------------------- | ------------------------------------------------------------------------- |
| `400`    | `validation_error`                             | Correct invalid or unsafe input                                           |
| `400`    | `idempotency_key_required`                     | Supply one stable retry key                                               |
| `401`    | `unauthorized`                                 | Replace or correct the key                                                |
| `403`    | `forbidden`                                    | Grant `optimize:run`                                                      |
| `404`    | `not_found`                                    | Confirm the run belongs to the active organization                        |
| `429`    | `usage_limit_reached` or `rate_limit_exceeded` | Wait until the returned recovery time                                     |
| terminal | `failed`                                       | Read the saved error and start a separately approved retry with a new key |

See [API usage, rate limits, and errors](/api/api-credits-rate-limits-and-errors.md) for the shared approval and usage contract.
