---
title: How requests work
description: Authentication, response shapes, paging, retries, long-running work, and rate limits in the Zaraftis v1 API.
sidebar:
  label: How requests work
  order: 2
---

Every route in the [API reference](/api) follows the rules on this page.

## Base URL and authentication

Send every request to `https://api.zaraftis.com/v1` with your API key in the `Authorization` header:

```bash
curl https://api.zaraftis.com/v1/me \
  --header "Authorization: Bearer $ZARAFTIS_API_KEY"
```

A key belongs to one workspace. It has scopes, such as `scans:read` or `scans:write`, and can be limited to some brands. A request outside its scopes answers `403 forbidden`. A key can read the wallet but cannot buy credits; that happens in the dashboard.

## Response shape

Every response is JSON with `success`, `data`, and `request_id`:

```json
{
  "success": true,
  "data": { "scan_id": "4f0d2c7e-1b5a-4f7e-9a51-0c6f3d2b8e14", "status": "completed" },
  "request_id": "d3b0e28f-89a4-4c1c-a68b-11b0d5886f45"
}
```

A failed request has `success: false` and an `error` object instead of `data`. See [Errors](/errors) for every code and what to do about it.

An id you cannot see always answers `404 not_found`: an unknown id, an id in another workspace, and an id outside your key's brands look the same.

## Request IDs

Every response has a `request_id` in the body and an `X-Request-ID` header. To use your own id, send an `X-Request-ID` header of 1 to 64 letters, digits, `-`, `.`, or `_`. Any other value is replaced with a new id. Log the `request_id` and quote it when you report a problem.

## Paging through lists

List routes return `items` and `pagination`:

```json
{
  "success": true,
  "data": {
    "items": [],
    "pagination": { "next_page_token": "c29tZQ", "has_more": true }
  },
  "request_id": "d3b0e28f-89a4-4c1c-a68b-11b0d5886f45"
}
```

Pass `next_page_token` as `page_token` to get the next page, with the same filters and `limit` as the first request. `limit` defaults to 25 and goes up to 100; a route can set lower values, shown on its reference page. When `has_more` is `false`, there is no `next_page_token`. Lists are sorted newest first.

Treat the token as an opaque string. A token that is changed, too old, or sent with different filters answers `400 invalid_page_token`; start the list again without a token.

## Safe retries with Idempotency-Key

Every `POST` that creates something needs an `Idempotency-Key` header: 1 to 64 printable characters with no spaces. Use a new value, such as a UUID, for each new request.

```bash
curl https://api.zaraftis.com/v1/scans \
  --request POST \
  --header "Authorization: Bearer $ZARAFTIS_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: 7b1e4a2c-5d3f-4e8a-9c0b-2f6d8e1a3b5c" \
  --data @scan.json
```

If the request times out or fails on the network, send it again with the same key and body. You get the first response back, and the work runs and is charged only once. If the first request is still running, the retry answers `202`. Sending the same key with a different body answers `409 idempotency_conflict`.

## Long-running work

Scans, site audits, scan reports, exports, and connection syncs run in the background. Their `POST` answers `202 Accepted` with a `task_id` and `task_url`. Poll `GET /v1/tasks/{task_id}` until `status` is `completed`, `partial`, `failed`, or `cancelled`; the task then links to what it made, such as `scan_url`. Wait a few seconds between polls.

`POST /v1/visibility/checks` waits for the result and answers `200` when every query finishes in time. Otherwise it answers `202` with a task, like a scan.

A `partial` scan finished, but some of its queries failed. Its results use only the queries that succeeded, and failed queries are not charged.

## Compact and detailed views

Some routes return a short view by default. Add `detail=detailed` to a `GET`, or `"response_detail": "detailed"` to a check request, to get more fields; the route's reference page says which. Scan reports use `detail=summary`, `evidence`, or `full`. Asking for another view of a resource never runs or charges the work again.

## Rate limits

Each API key has three limits per 60 seconds:

| Kind of request | Limit |
| --- | ---: |
| Reads (`GET`) | 120 |
| Paid work (`POST /v1/scans`, `POST /v1/visibility/checks`, `POST /v1/site-audits`) | 10 |
| Other writes | 30 |

Every response to a valid key shows where you are:

```text
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1790470860000
Retry-After: 12
```

`X-RateLimit-Reset` is a Unix time in milliseconds. Over the limit, the API answers `429 rate_limited` with `Retry-After` in seconds; wait that long before you try again. The numbers can change, so read the headers instead of hard-coding them.

A workspace can also run only a set number of scans at the same time. `GET /v1/capabilities` shows the number in `limits.concurrent_work`. A scan over that number answers `503 capacity_unavailable`; retry after a running scan finishes.

## Versions

The version is in the URL (`/v1`). New fields and new routes can appear in `/v1` at any time, so ignore fields you do not know. A change that breaks existing code gets a new version and a notice in the [changelog](/changelog).
