# API conventions (/en/api/conventions)

## Base URL and versions [#base-url-and-versions]

```txt
https://api.deepscout.ai/v1
```

The `v1` in the path is the version of the API. The OpenAPI specification of the version is at [`/v1/openapi.json`](https://api.deepscout.ai/v1/openapi.json).

## Requests and responses [#requests-and-responses]

- Send request bodies as JSON, with the header `Content-Type: application/json`.
- All responses are JSON, except file downloads.
- Field names are in `snake_case`. Enum values are in `UPPER_CASE`, except the sign-in `provider` of the account (`google` or `github`).

### Fields without a value [#fields-without-a-value]

The API leaves out the fields that have no value. It does not send `null`. For example, a job without a `settlement` has no `settlement` field. In your code, read optional fields with a default:

```python
percent = job.get("scrape_progress", {}).get("percent", 0)
```

Fields with a default value, for example `"sample": false`, are always in the response.

## IDs [#ids]

All IDs are strings. Use them as opaque values: do not build them, and do not parse them.

| ID | Example |
| --- | --- |
| `project_id` | `proj_01k7c2r9v8e3s0b1x6m4q5n2tz` |
| `customer_id` | `cust_01k7c2r9t2f8a5d3h7j9k1m6pw` |
| `job_id` | `jobreq_01k7c3a4b5c6d7e8f9g0h1j2k3` |
| `key_id` | `key_01k7c4m2n3p4q5r6s7t8v9w0x1` |
| `website_id` | `example-shop.cz` |

A website ID can contain a slash (`/`). You can put it in the path as it is.

## Timestamps [#timestamps]

Timestamps are strings in RFC 3339 format, in UTC, with milliseconds:

```txt
2026-10-13T09:00:00.000Z
```

## Money [#money]

Amounts of money are decimal strings, not numbers, with the currency in a separate `currency` field. This keeps the exact value of each amount.

```json
{ "currency": "EUR", "total": "12.88", "cap": "19.32" }
```

Amounts have two decimal places. Convert the strings to a decimal type, not to a float:

```python
from decimal import Decimal

cap = Decimal(job["estimate"]["cap"])
```

## Countries, currencies and languages [#countries-currencies-and-languages]

| Value | Format | Example |
| --- | --- | --- |
| Country | Two-letter country code (ISO 3166-1 alpha-2). The API accepts any letter case and returns lower case. | `cz` |
| Currency | ISO 4217, in upper case. | `CZK` |
| Language | ISO 639-1. | `cs` |

## Lists [#lists]

- The job lists ([setup](/en/api/reference/setup-jobs/list-setup-jobs.md), [scraping](/en/api/reference/scraping-jobs/list-scraping-jobs.md), [matching](/en/api/reference/matching-jobs/list-matching-jobs.md)) show the newest jobs first. `limit` sets the number of jobs, from 1 to 100. The default is 20.
- The other lists return all items.
- Lists are JSON arrays, except the invoices and the usage, which have an `items` field, and the exports, which have an `exports` field.

## Repeated requests [#repeated-requests]

Some create requests are safe to send again:

| Request | When you send it again |
| --- | --- |
| `POST /jobs/setup` | While a setup job for the same store, country and currency runs in the project, you get this job with `200 OK`. After it ends, a new request creates a new job and uses another analysis. |
| `POST /jobs/scraping` without `urls` | While the full scrape of the website is not finished, you get the same job with `200 OK`. |
| `POST /contact-requests` | For the same job, you get the first request with `200 OK`. |
| `POST /jobs/scraping` with `urls` | Each request creates a new job. |
| `POST /jobs/matching` | The API answers `409 MATCHING_BUSY` while the first matching is not finished. |

Approvals are also safe. A second approval of the same job answers `409 JOB_NOT_AWAITING_APPROVAL`, and the API holds the credit only one time.

## Long-running work [#long-running-work]

Jobs run in the background. The API has no webhooks yet. Read the job at an interval until it is in an end status. See [Polling](/en/api/jobs.md#polling).
