# Otodom Count API Documentation

Scrappa's `GET /api/otodom/count` endpoint returns how many Otodom listings match a filter set, without paging through them.

## What the response contains

A `total` count, a `facets` block echoing the resolved filters, and `point_in_time: true`. Otodom's totals shift slightly between calls as listings are published and removed, so treat every count as a reading at a moment in time rather than a stable number to assert against.

## Filter validity

Otodom reports an invalid filter through the response type name, not the HTTP status — a bad location id still returns 200. This endpoint checks that type name and returns an `INVALID_PARAMETERS` error when the filters were rejected, so an invalid request is never billed and never returns a misleading total.

## Commercial premises

Otodom counts commercial premises under `estate=COMMERCIAL_PROPERTY`. That is also the only estate `use_types` narrows: with any other estate, or with none, Otodom ignores `use_types` and returns the estate's unfiltered total behind a 200. This endpoint therefore requires `estate=COMMERCIAL_PROPERTY` whenever `use_types` is supplied and returns a 422 otherwise, so a caller is never billed for a commercial sub-count that was actually an all-premises count.

## Related Otodom endpoints

- `GET /api/otodom/search` — the listings themselves
- `GET /api/otodom/locations` — to resolve a `geo_path`

- **Documentation:** [https://scrappa.co/docs/otodom-api/otodom_count](https://scrappa.co/docs/otodom-api/otodom_count)
- **API group:** Otodom API
- **Endpoint:** `GET https://scrappa.co/api/otodom/count`

## Authentication

Send your Scrappa API key in the `X-API-KEY` request header. Paid endpoints also support accountless x402 payments when called without an API key.

## Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `transaction` | string | Yes | sprzedaz (sale) or wynajem (rent). |
| `geo_path` | string | Yes | 2 to 6 slash-separated segments from the locations endpoint. |
| `estate` | string | No | Estate facet: FLAT, HOUSE, GARAGE, HALL, INVESTMENT, TERRAIN, COMMERCIAL_PROPERTY, OFFICE, BUILDING or ROOM. Counts apartments when omitted. The results-page estate_slug is not accepted here. |
| `market` | string | No | PRIMARY or SECONDARY. |
| `use_types[]` | array | No | Commercial sub-slicing: OFFICE, RETAIL, HOTEL, INDUSTRIAL or SERVICES. Requires estate=COMMERCIAL_PROPERTY, and is rejected with a 422 otherwise because Otodom silently ignores it elsewhere. |
| `rooms_number[]` | array | No | Room counts: ONE, TWO, THREE, FOUR or FIVE. Repeat for several values. SIX_OR_MORE is not accepted by the count operation. Amenities (extras) are not counted here — use GET /api/otodom/search for those. |
| `owner_type` | string | No | DEVELOPER, PRIVATE or AGENCY. |
| `price_min` | integer | No | Minimum price. |
| `price_max` | integer | No | Maximum price. Must be at least price_min. |
| `area_min` | integer | No | Minimum floor area in square meters. |
| `area_max` | integer | No | Maximum floor area in square meters. Must be at least area_min. |
| `has_discount` | boolean | No | true for listings with a discount, false for those without. Omit it to count both. |

## Example request

```bash
#!/bin/bash

curl -X GET \
    -H "x-api-key: YOUR_API_KEY_HERE" \
    "https://scrappa.co/api/otodom/count?transaction=sprzedaz&geo_path=mazowieckie%2Fwarszawa%2Fwarszawa%2Fwarszawa&estate=FLAT"
```

## Example response

```json
{
    "success": true,
    "data": {
        "total": 20226,
        "facets": {
            "location": "mazowieckie/warszawa/warszawa/warszawa",
            "estate": "FLAT",
            "transaction": "SELL",
            "market": "ALL"
        },
        "point_in_time": true
    }
}
```

## Errors

| Status | Error | Description |
| --- | --- | --- |
|  | Error | Otodom rejected these filters. This response is not billed. |
|  | Error | Otodom rotated its internal query hash. This response is not billed. |

## Frequently asked questions

### Why does my count differ slightly between calls?

Listings are published and removed continuously. Counts are point-in-time readings.

### What does COUNT_HASH_ROTATED mean?

Otodom changed the internal query this endpoint uses. It affects only the count endpoint and is resolved by re-capturing the hash.

### Do I get charged for a rejected filter?

No. Rejected filters return an error and are never billed.

## More Scrappa resources

- [API documentation](https://scrappa.co/docs)
- [Full LLM-readable API reference](https://scrappa.co/llms-full.txt)
- [OpenAPI specification](https://scrappa.co/docs/api.json)
- [Pricing](https://scrappa.co/pricing)
