# Modules

Modules outside the health score (rankings, Search Console, backlinks, security, logs). They carry trends, never a score.

## Read rank tracking

`GET /v1/projects/{project}/positions`

Tracked keywords and their latest position for the project domain, with the
period summary. A keyword with `measured: false` was never checked; a
keyword with `measured: true` and `position: null` was checked and is out of
the top 100. Requires a plan that includes the module.


- Required scopes: `positions:read`
- Rate-limit cost: 5 unit(s)

### Parameters

- `project` (path, string (uuid), required)
- `period` (query, string, optional)

### Responses

- `200`: OK.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/positions" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "status": "unconfigured",
        "tracker": {
            "location": "string",
            "country": "string",
            "language": "string",
            "device": "string",
            "active": true,
            "last_tracked_on": "2026-10-01"
        },
        "period": {
            "preset": "string",
            "from": "2026-10-01",
            "to": "2026-10-01"
        },
        "summary": {
            "average_position": 0.5,
            "ranking_keywords": 42,
            "measured_keywords": 42,
            "tracked_keywords": 42,
            "top3": 42,
            "top10": 42,
            "visibility_percent": 0.5,
            "improved": 42,
            "declined": 42,
            "entered": 42,
            "exited": 42
        },
        "keywords": [
            {
                "keyword": "string",
                "active": true,
                "measured": true,
                "position": 42,
                "url": "https://exemple.fr/",
                "delta_1d": 42,
                "delta_7d": 42,
                "delta_30d": 42,
                "search_volume": 42,
                "keyword_difficulty": 42,
                "search_intent": "string",
                "serp_features": [
                    "string"
                ]
            }
        ],
        "quota": {
            "used": 42,
            "limit": 100,
            "remaining": 100
        }
    }
}
```

## Track keywords

`POST /v1/projects/{project}/tracked-keywords`

Puts keywords under daily rank tracking (at most 100 per request). A keyword is
identified by its canonical form (lowercase, single spaces), which the response
returns. A keyword already known to the project as a candidate is adopted. This
opens a recurring daily spend: it counts against the plan pool
`positions.tracked_keywords` (`plan_limit.positions.tracked_keywords`) and the
per-project ceiling (`keywords.project_limit`), so an `Idempotency-Key` is
required.


- Required scopes: `positions:write`
- Rate-limit cost: 1 unit(s)
- Idempotency-Key required
- Consumes a plan quota

### Parameters

- `project` (path, string (uuid), required)

### Request body

- `keywords` (string[], required)

### Responses

- `200`: OK.
- `400`: Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`).
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `409`: The request conflicts with the current state (a crawl already running, an idempotency key in use, a project not ready).
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl -X POST "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/tracked-keywords" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"keywords":["string"]}'
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "tracked": [
            "string"
        ],
        "already_tracked": [
            "string"
        ]
    }
}
```

## Stop tracking keywords

`POST /v1/projects/{project}/tracked-keywords/untrack`

Removes keywords from tracking without deleting them: their paid history stays,
and their slots are freed. Never refused by billing. Unknown or untracked keywords
are ignored; `untracked` lists those that changed.


- Required scopes: `positions:write`
- Rate-limit cost: 1 unit(s)
- Idempotency-Key accepted

### Parameters

- `project` (path, string (uuid), required)

### Request body

- `keywords` (string[], required)

### Responses

- `200`: OK.
- `400`: Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`).
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `409`: The request conflicts with the current state (a crawl already running, an idempotency key in use, a project not ready).
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl -X POST "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/tracked-keywords/untrack" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"keywords":["string"]}'
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "untracked": [
            "string"
        ]
    }
}
```

## Pause tracked keywords

`POST /v1/projects/{project}/tracked-keywords/pause`

Suspends the daily check of tracked keywords, which frees their slots in the
plan pool. `keywords.not_tracked` if one of them is not tracked.


- Required scopes: `positions:write`
- Rate-limit cost: 1 unit(s)
- Idempotency-Key accepted

### Parameters

- `project` (path, string (uuid), required)

### Request body

- `keywords` (string[], required)

### Responses

- `200`: OK.
- `400`: Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`).
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `409`: The request conflicts with the current state (a crawl already running, an idempotency key in use, a project not ready).
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl -X POST "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/tracked-keywords/pause" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"keywords":["string"]}'
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "active": true,
        "changed": [
            "string"
        ]
    }
}
```

## Resume tracked keywords

`POST /v1/projects/{project}/tracked-keywords/resume`

Resumes the daily check of paused keywords. Resuming takes a slot back in the
plan pool (`plan_limit.positions.tracked_keywords`), so an `Idempotency-Key` is
required.


- Required scopes: `positions:write`
- Rate-limit cost: 1 unit(s)
- Idempotency-Key required
- Consumes a plan quota

### Parameters

- `project` (path, string (uuid), required)

### Request body

- `keywords` (string[], required)

### Responses

- `200`: OK.
- `400`: Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`).
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `409`: The request conflicts with the current state (a crawl already running, an idempotency key in use, a project not ready).
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl -X POST "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/tracked-keywords/resume" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"keywords":["string"]}'
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "active": true,
        "changed": [
            "string"
        ]
    }
}
```

## Check a keyword now

`POST /v1/projects/{project}/tracked-keywords/check`

Queues an immediate live check of one tracked keyword (`202`); the position
appears in the rank tracking read once measured. One immediate check per keyword
per day (`keywords.already_checked_today`), within the plan daily quota
(`plan_limit.positions.live_checks_per_day`) and monthly spend cap.


- Required scopes: `positions:write`
- Rate-limit cost: 1 unit(s)
- Idempotency-Key required
- Consumes a plan quota

### Parameters

- `project` (path, string (uuid), required)

### Request body

- `keyword` (string, required)

### Responses

- `202`: OK.
- `400`: Malformed request, such as an invalid pagination cursor (`pagination.invalid_cursor`).
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `409`: The request conflicts with the current state (a crawl already running, an idempotency key in use, a project not ready).
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl -X POST "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/tracked-keywords/check" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"keyword":"string"}'
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "keyword": "string",
        "status": "queued",
        "date": "2026-10-01"
    }
}
```

## Read Search Console data

`GET /v1/projects/{project}/search-console`

Performance totals and daily series (from the linked property), and the
indexation inspection totals. `unconfigured` means no property is linked,
`pending` that it is linked but not synced yet.


- Required scopes: `search_console:read`
- Rate-limit cost: 5 unit(s)

### Parameters

- `project` (path, string (uuid), required)
- `period` (query, string, optional)

### Responses

- `200`: OK.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/search-console" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "status": "unconfigured",
        "property": "string",
        "performance": {
            "period": {
                "preset": "string",
                "from": "2026-10-01",
                "to": "2026-10-01"
            },
            "totals": {
                "clicks": 42,
                "impressions": 42,
                "ctr_percent": 0.5,
                "average_position": 0.5
            },
            "daily": [
                {
                    "date": "2026-10-01",
                    "clicks": 42,
                    "impressions": 42,
                    "average_position": 0.5
                }
            ]
        },
        "indexation": {
            "inspected": 42,
            "indexed": 42,
            "excluded": 42,
            "canonical_conflicts": 42,
            "last_inspected_at": "2026-10-01T09:30:00Z"
        }
    }
}
```

## Read the backlink profile summary

`GET /v1/projects/{project}/backlinks`

Latest daily measurement of the current target. Daily new and lost counters overlap and must not be summed.


- Required scopes: `backlinks:read`
- Rate-limit cost: 1 unit(s)

### Parameters

- `project` (path, string (uuid), required)

### Responses

- `200`: OK.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/backlinks" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "status": "unconfigured",
        "target": "string",
        "latest": {
            "date": "2026-10-01",
            "rank": 42,
            "backlinks": 42,
            "referring_domains": 42,
            "referring_main_domains": 42,
            "referring_pages": 42,
            "broken_backlinks": 42,
            "broken_pages": 42,
            "spam_score": 42,
            "new_backlinks": 42,
            "lost_backlinks": 42,
            "new_referring_domains": 42,
            "lost_referring_domains": 42
        }
    }
}
```

## Read security signals

`GET /v1/projects/{project}/security`

Passive security signals of the project site, with their status. An
incomplete scan (`scan.complete: false`) does not mean nothing was found;
`score: null` means unknown, never zero. Texts follow `Accept-Language`.


- Required scopes: `security:read`
- Rate-limit cost: 5 unit(s)

### Parameters

- `project` (path, string (uuid), required)

### Responses

- `200`: OK.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/security" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "status": "string",
        "target": "string",
        "scan": {
            "status": "string",
            "running": true,
            "complete": true,
            "results_truncated": true,
            "started_at": "2026-10-01T09:30:00Z",
            "finished_at": "2026-10-01T09:30:00Z"
        },
        "score": 42,
        "counts": {
            "problems": 42,
            "informational": 42,
            "fixed": 42,
            "masked": 42,
            "ignored": 42,
            "falsePositive": 42
        },
        "findings": [
            {
                "key": "string",
                "severity": "critical",
                "status": "string",
                "location": "string",
                "title": "string",
                "explanation": "string",
                "action": "string",
                "first_seen_at": "2026-10-01T09:30:00Z",
                "last_seen_at": "2026-10-01T09:30:00Z",
                "fixed_at": "2026-10-01T09:30:00Z"
            }
        ]
    }
}
```

## Read server log analysis

`GET /v1/projects/{project}/logs`

Hits by audience (humans, search bots, AI bots, others) over the period, with the days actually covered by logs.


- Required scopes: `logs:read`
- Rate-limit cost: 5 unit(s)

### Parameters

- `project` (path, string (uuid), required)
- `period` (query, string, optional)

### Responses

- `200`: OK.
- `401`: Missing, invalid or expired token, or a member who left the team.
- `403`: The plan does not include the API (`api.not_in_plan`) or this verb
(`api.write_not_in_plan`), or the token lacks a scope
(`token.missing_scope`).

- `404`: No such resource for the token's team. Resources of another team answer 404 as well, never 403.
- `422`: Invalid query or body values (`validation_failed`), with `errors` by field.
- `429`: The plan rate limit is exhausted for one window.

### Request example (cURL)

```bash
curl "https://api.nessflow.com/v1/projects/0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57/logs" \
  -H "Authorization: Bearer $NESSFLOW_TOKEN"
```

### Response example

```json
{
    "data": {
        "project_id": "0190f5a2-7c4e-7b1a-9d3e-2f6b8c1a4e57",
        "status": "pending",
        "period": {
            "preset": "string",
            "from": "2026-10-01",
            "to": "2026-10-01",
            "days": 42,
            "days_with_logs": 42
        },
        "totals": {
            "hits": 42,
            "bot_hits": 42,
            "human_hits": 42,
            "bytes": 42
        },
        "daily": [
            {
                "date": "2026-10-01",
                "humans": 42,
                "search": 42,
                "ai": 42,
                "others": 42
            }
        ],
        "gaps": [
            {
                "from": "2026-10-01",
                "to": "2026-10-01",
                "days": 42
            }
        ]
    }
}
```
