# Basics

> Pagination, compact responses, takedown status and error responses in the nebty API.

These rules apply to all endpoints in the [API reference](https://docs.nebty-id.com/api/reference/).

## Pagination

List endpoints return one page at a time:

```json
{
  "count": 3,
  "next": "https://app.nebty.de/core/api/brands/f5705d11-6786-4f60-b52c-59a882ecf305/domains/?page=2&page_size=2",
  "previous": null,
  "results": []
}
```

- `count` is the total number of items in all pages.
- `next` and `previous` are the full URLs of the neighbouring pages, or `null`.
- `results` holds the items of this page.

Control the page with two query parameters:

| Parameter | Default | Meaning |
| - | - | - |
| `page` | `1` | Page number, starting at 1 |
| `page_size` | `50` | Items per page, at most `100` |

A larger `page_size` is cut down to 100 on the brand, domain and scan lists. The takedown list instead returns `400` if `page_size` is outside `1`–`100`. To read a whole list, follow `next` until it is `null`.

List responses also carry the header `X-Total-Count` with the same value as `count`.

## Compact responses

Add `compact=true` to these list endpoints:

- `GET /brands/`
- `GET /brands/{id}/domains/`
- `GET /brands/{id}/scans/`

You then get short rows with only the main fields and without embedded scan history. We recommend `compact=true` for lists. To get the full evidence for one item, call its detail endpoint, for example `GET /brands/{id}/domains/{domain_id}/`.

## Takedown status

A new takedown starts with one of two statuses:

| Status | Meaning |
| - | - |
| `OPEN` | The nebty team starts work. |
| `REQUESTED` | nebty checks the request first, then accepts (`OPEN`) or rejects. |

For a brand of an account that nebty set up for you, a takedown with a `brand` that has active monitoring usually starts as `OPEN`. Without a `brand`, or for a brand without active monitoring, it starts as `REQUESTED`. If you signed up on your own, the status does not depend on monitoring, and the takedown can start as either one. The `status` in the `201` response tells you which one applies. After that, read the current status with `GET /takedowns/{id}/` and the history with `GET /takedowns/{id}/events/`. The reference lists all status values.

### Price of a takedown

If you signed up on your own at dash.nebty.de/register, a website takedown has a price per successful takedown (see [Price of a takedown](https://docs.nebty-id.com/features/takedowns/#price-of-a-takedown)). Send the price you accept with the request:

```json
{
  "target_website": "https://solara-login.example/",
  "brand": "f5705d11-6786-4f60-b52c-59a882ecf305",
  "accepted_price": "400.00",
  "accepted_currency": "EUR"
}
```

Send both fields or neither. The price must be the current price of the brand. The request form in the dashboard shows it. Without the two fields, the API answers `400` with `"code": "price_consent_required"`, and the body contains the current price in `price`. If the fields do not match the current price, you get `409` with `"code": "price_changed"` and the current price. If you are a member of a brand with a takedown price and send no `brand`, you get `400` with `"code": "brand_required"`. If you send the fields for a brand without a takedown price, you get `400` with `"code": "price_not_applicable"`.

The accepted price is saved on the takedown, and nebty bills it only if the case closes with a successful outcome.

### One active takedown per website

A brand can have only one active takedown (`REQUESTED`, `OPEN`, `IN_PROGRESS` or `PENDING_RESPONSE`) per website. A second request for the same host returns `409` with `"code": "duplicate"` and the ID of the existing case in `takedown_id`. Only the host counts. Upper and lower case, the path and a leading `www.` are ignored. A subdomain is a different host. Closed, failed and rejected takedowns do not count.

## Errors

Errors come back as JSON with a matching HTTP status code.

| Code | Meaning | Body |
| - | - | - |
| `400` | The request data is not valid. | One entry per field, for example `{"target_website": ["Enter a valid URL."]}` |
| `401` | The API key is missing, wrong or inactive. | `{"detail": "Invalid or inactive API key."}` |
| `404` | The item does not exist, or your user has no access to it. | `{"detail": "No Brand matches the given query."}` |
| `409` | The request conflicts with the current state. | `{"code": "duplicate", "detail": "...", "takedown_id": "..."}` |

A brand or takedown that belongs to another customer returns `404`, not `403`. When you request a takedown for a brand that your user is not a member of, you get `400` with an error on the `brand` field.
