> ## Documentation Index
> Fetch the complete documentation index at: https://devs.surferseo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Make your first requests: create a Content Editor, read its guidelines and score, and update its content.

You need an API key stored in an environment variable named `SURFER_API_KEY` ([Authentication](/api/authentication)), an `active` workspace, and one Content Editor credit, which step 2 spends. A workspace is a brand or site you manage in Surfer, and every request below names one in its URL.

## Create and score a Content Editor

**1. Pick a workspace**

List the active workspaces your key can reach; `-g` keeps curl from reading the square brackets as a pattern:

```bash theme={"system"}
curl -g "https://app.surferseo.com/api/v2/workspaces?filter[state]=active" \
  -H "API-KEY: $SURFER_API_KEY"
```

The `200` lists each workspace under `data`. This sample, like every sample on this page, is abridged to the fields the step uses:

```json theme={"system"}
{
  "data": [
    {
      "id": 123,
      "name": "My Workspace",
      "state": "active"
    }
  ]
}
```

Pick an `id` from `data`; the rest of this page uses `123`.

**2. Create a Content Editor**

This request spends the Content Editor credit ([Rate limits and credits](/api/rate-limits#credits)). A Content Editor is the document Surfer builds around one keyword: the guidelines derived from that keyword's search results, your content, and the scores that grade it. Create one for the keyword `seo tools`:

```bash theme={"system"}
curl -X POST "https://app.surferseo.com/api/v2/workspaces/123/content_editors" \
  -H "API-KEY: $SURFER_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-seo-tools-1" \
  -d '{"main_keyword": "seo tools"}'
```

Reuse this key when retrying the create; see the [idempotency rules](/api/requests#idempotent-creates).

The `201` comes back at once and carries the editor's current `state`, `scheduled` in this example:

```json theme={"system"}
{
  "id": 456,
  "workspace_id": 123,
  "state": "scheduled",
  "main_keyword": "seo tools",
  "location": "United States",
  "device": "mobile"
}
```

Keep the `id` (`456` here) for the remaining steps. Without `location` and `device`, Surfer analyzes the `United States` search results on `mobile`; [Create Content Editor](/api/v2/content-editors/create-content-editor) lists the other fields.

**3. Wait for the editor to complete**

Poll the editor until `state` is `completed` or `failed`:

```bash theme={"system"}
curl "https://app.surferseo.com/api/v2/workspaces/123/content_editors/456" \
  -H "API-KEY: $SURFER_API_KEY"
```

The `200` returns the same fields as the create response; once the analysis finishes it reads:

```json theme={"system"}
{
  "id": 456,
  "state": "completed"
}
```

Until `state` is `completed`, the endpoints under the editor answer `409`; [Content Editor states](/api/async-and-webhooks#content-editor-states) explains each state. On `completed`, continue. On `failed`, stop and read `error.reason` and `error.message`.

**4. Read the terms**

Read the recommended terms of the completed editor:

```bash theme={"system"}
curl "https://app.surferseo.com/api/v2/workspaces/123/content_editors/456/seo_guidelines/terms" \
  -H "API-KEY: $SURFER_API_KEY"
```

The `200` lists the main keyword first, then the terms Surfer extracted from competitor pages and any custom terms you add:

```json theme={"system"}
{
  "data": [
    { "item": "seo tools", "included": true, "target_range": { "min": 5, "max": 9 } },
    { "item": "keyword research", "included": true, "target_range": { "min": 3, "max": 7 } },
    { "item": "backlinks", "included": false, "target_range": { "min": 1, "max": 3 } }
  ]
}
```

Each term says whether it counts toward the SEO score (`included`) and its recommended range of uses (`target_range`). [List SEO Guidelines terms](/api/v2/seo-guidelines/list-seo-guidelines-terms) describes every field. [Get SEO Guidelines](/api/v2/seo-guidelines/get-seo-guidelines) returns terms, structure, competitors, and topics in one large response; the sub-endpoints are the better default.

**5. Read the score**

Read the current SEO score:

```bash theme={"system"}
curl "https://app.surferseo.com/api/v2/workspaces/123/content_editors/456/seo_guidelines/score" \
  -H "API-KEY: $SURFER_API_KEY"
```

The `200` carries `score` (0 to 100), `status`, and `calculated_at`, the time of the current `score`. On a new editor nothing has been scored yet, so `score` and `calculated_at` are `null` and `status` reads `calculating` until the first calculation completes:

```json theme={"system"}
{
  "score": null,
  "status": "calculating",
  "calculated_at": null
}
```

Writing content in the next step starts that calculation.

**6. Write the content**

Send this HTML document with `Content-Type: text/html` and `Accept: text/html`:

```bash theme={"system"}
curl -X PUT "https://app.surferseo.com/api/v2/workspaces/123/content_editors/456/content" \
  -H "API-KEY: $SURFER_API_KEY" \
  -H "Content-Type: text/html" \
  -H "Accept: text/html" \
  --data-binary '<h1>SEO tools compared</h1><p>The right SEO tools cover keyword research, on-page optimization, and rank tracking.</p>'
```

A `204` with no body confirms the write. [Document endpoints](/api/requests#document-endpoints) covers Markdown bodies, the size limit, and how the stored document can differ from what you sent.

**7. Read the updated score**

The write starts a recalculation in the background. Repeat the score request until `status` is `ready` and `calculated_at` has changed, with a deadline on the loop ([Recognizing a fresh score](/api/async-and-webhooks#recognizing-a-fresh-score) explains a recalculation that stalls):

```bash theme={"system"}
curl "https://app.surferseo.com/api/v2/workspaces/123/content_editors/456/seo_guidelines/score" \
  -H "API-KEY: $SURFER_API_KEY"
```

A completed recalculation returns the new score:

```json theme={"system"}
{
  "score": 78,
  "status": "ready",
  "calculated_at": "2026-05-27T10:23:11Z"
}
```

## Next steps

You now have a completed Content Editor with scored content, and every other operation builds on it.

* Check [what each costs](/api/rate-limits#credits) first, then generate an [AI Article](/api/v2/ai-articles/create-ai-article) or run [Auto-Optimize](/api/v2/auto-optimize/start-auto-optimize) on the editor.
* [Async operations and webhooks](/api/async-and-webhooks) shows how to replace the polling in steps 3 and 7 with webhook events.
* [Rate limits and credits](/api/rate-limits) covers the per-endpoint request limits.
* [Use cases](/api/use-cases) has multi-step recipes built on these requests.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.