---
title: "Socials Homes API"
description: "The public, read-only Socials Homes REST API: supported cities, plans and prices in EUR, and live rental listing counts per city and budget. No key, no sign-up, OpenAPI 3.1."
canonical: https://www.socials.homes/developers
last-updated: 2026-10-14
api-version: "1"
openapi: https://www.socials.homes/openapi.json
---

# Socials Homes API

The public, read-only Socials Homes REST API: supported cities, plans and prices in EUR, and live rental listing counts per city and budget. No key, no sign-up, OpenAPI 3.1.

## What the API is

Socials Homes scans 2,000+ housing sites and sends new rental listings to people moving to a new city. The Socials Homes API gives agents and developers the same public facts the website shows: the 124 cities in 29 countries it covers, its plans and prices in EUR, and live listing counts per city and budget.

- Base URL: `https://www.socials.homes/api/v1`
- Format: JSON over HTTPS, errors as RFC 9457 problem details
- Authentication: none. Every endpoint is public and anonymous
- Methods: GET, HEAD and OPTIONS. Nothing in this API writes
- Version: v1 (contract 1.0.1, released 2026-10-14)
- Machine-readable description: [OpenAPI 3.1](https://www.socials.homes/openapi.json)

The API does not search or return individual listings, create accounts or start a search for anyone. To start a home search, send the person to the signup link each city carries; they set up their own alerts there.

## Quickstart

No key and no sign-up: each of these works as it is, from a terminal or any HTTP client.

The index: every endpoint, the spec and the limits:

```bash
curl https://www.socials.homes/api/v1
```

Cities in the Netherlands, five per page:

```bash
curl "https://www.socials.homes/api/v1/cities?country=NL&limit=5"
```

The next page: pass next_cursor back as cursor:

```bash
curl "https://www.socials.homes/api/v1/cities?country=NL&limit=5&cursor=NEXT_CURSOR"
```

One city by its slug:

```bash
curl https://www.socials.homes/api/v1/cities/the_hague
```

Live listings in Berlin, and how many are at or under 800 EUR a month:

```bash
curl "https://www.socials.homes/api/v1/cities/berlin/supply?budget_max_eur=800"
```

Plans, prices, renewal and the VIP guarantee:

```bash
curl https://www.socials.homes/api/v1/plans
```

An error, with its headers: a problem details object:

```bash
curl -i https://www.socials.homes/api/v1/cities/atlantis
```

A page of cities looks like this:

GET /api/v1/cities?country=NL&limit=2:

```json
{
  "data": [
    {
      "slug": "almere",
      "name": "Almere",
      "country": "NL",
      "country_name": "Netherlands",
      "links": {
        "self": "https://www.socials.homes/api/v1/cities/almere",
        "supply": "https://www.socials.homes/api/v1/cities/almere/supply",
        "page": "https://www.socials.homes/housing/almere",
        "signup": "https://www.socials.homes/signup?city=almere"
      }
    },
    {
      "slug": "amsterdam",
      "name": "Amsterdam",
      "country": "NL",
      "country_name": "Netherlands",
      "links": {
        "self": "https://www.socials.homes/api/v1/cities/amsterdam",
        "supply": "https://www.socials.homes/api/v1/cities/amsterdam/supply",
        "page": "https://www.socials.homes/housing/amsterdam",
        "signup": "https://www.socials.homes/signup?city=amsterdam"
      }
    }
  ],
  "next_cursor": "c1.eyJhZnRlciI6ImFtc3RlcmRhbSJ9"
}
```

In JavaScript, every city across all pages:

```js
const cities = [];
let cursor = null;
do {
  const url = new URL("https://www.socials.homes/api/v1/cities");
  url.searchParams.set("limit", "200");
  if (cursor) url.searchParams.set("cursor", cursor);
  const res = await fetch(url);
  if (!res.ok) throw new Error((await res.json()).detail);
  const page = await res.json();
  cities.push(...page.data);
  cursor = page.next_cursor;
} while (cursor);
```

In Python, the live count for a budget:

```python
import requests

r = requests.get(
    "https://www.socials.homes/api/v1/cities/berlin/supply",
    params={"budget_max_eur": 800},
    timeout=10,
)
r.raise_for_status()
supply = r.json()["data"]["supply"]
if supply["available"]:
    print(supply["listings_within_budget"], "listings at or under 800 EUR")
else:
    print(supply["reason"])
```

## Authentication

None. The API is public, anonymous and read-only: there are no accounts, API keys, OAuth clients or scopes, and nothing to sign up for or manage. Send no credentials; an Authorization header or cookie is ignored. The OpenAPI document says the same with an empty `security` list.

The API keeps nothing you send. Your IP address is held in memory only to count the rate limits, and requests appear only in the hosting provider's standard request logs, as the [privacy policy](https://www.socials.homes/privacy) describes.

## Sandbox and testing

There is no separate sandbox, and none is needed: nothing in this API writes. Every operation is a GET; POST, PUT, PATCH and DELETE answer 405 without reading the request. Build and test against `https://www.socials.homes` directly. The only data read is public: the city list, the plans, and an aggregated listing count per city from a cache refreshed about every 10 minutes.

## Endpoints

| Method | Path | Returns |
| --- | --- | --- |
| GET | `/api/v1` | API index: every endpoint, the OpenAPI document, the docs and the rate limits |
| GET | `/api/v1/cities` | List the cities Socials Homes covers |
| GET | `/api/v1/cities/{slug}` | Get one city by its slug |
| GET | `/api/v1/cities/{slug}/supply` | Count live rental listings in a city, optionally at or under a monthly budget |
| GET | `/api/v1/plans` | List the plans with EUR prices, billing, renewal, inclusions and the VIP guarantee |

- `slug` is the city's slug from `/api/v1/cities`: lowercase, digits and underscores, for example `the_hague`. A slug that is not covered answers 404 `city_not_found` with close matches in `suggestions`.
- `country` filters cities by ISO code (`NL`) or name (`Netherlands`, `Holland`, `UK`). A country with no covered city returns an empty page.
- `budget_max_eur` is a whole number of euros from 50 to 20,000. With it, the supply answer also counts listings at or under it, rates the budget (`critical`, `low` or `ok`) and, for a thin market, suggests a budget that reaches about 30 listings.
- When live counts are unavailable the supply answer says so with `available: false` and a reason. That never means zero homes: some housing sites are not counted, so alerts can bring more than the count.
- A client may reuse cities and plans for 5 minutes and live counts for 1 minute (Cache-Control: private, since each answer carries the caller's own rate limit headers). Unknown query parameters are ignored.

## Pagination

`/api/v1/cities` is cursor-paginated and ordered by slug. Each page is `{ "data": [...], "next_cursor": "..." }`. Pass `next_cursor` back as `cursor`, with the same `country` and `limit`, until it is `null`. The same next page is in the `Link` header as `rel="next"`. `limit` is 1 to 200, default 50. Cursors are opaque: use them as given, and a cursor this API did not issue answers 400.

## Rate limits

| Policy | Limit, per IP address | Counts |
| --- | --- | --- |
| `api` | 300 requests a minute | Every `/api/v1` request |
| `supply` | 600 requests an hour | `/api/v1/cities/{slug}/supply`, on top of `api`. The same size as the signup funnel's own live-count budget |

Every endpoint response says how much is left, in the IETF RateLimit header fields:

```http
RateLimit-Policy: "api";q=300;w=60
RateLimit: "api";r=299;t=60
RateLimit-Limit: 300
RateLimit-Remaining: 299
RateLimit-Reset: 60
```

Over a limit the answer is 429 `rate_limited` with a `Retry-After` header in seconds. Wait that long, then retry. Limits are counted per server instance, so treat them as ceilings to stay under.

## Errors

Every error is an RFC 9457 problem details object served as `application/problem+json`: `type` (a link to its section below), `title`, `status`, `detail`, `instance`, a machine-readable `code` and a `resolution` hint. Invalid parameters are listed in `errors`; an unknown city brings `suggestions`. No error ever carries a stack trace or internal detail.

GET /api/v1/cities/den_haag:

```json
{
  "type": "https://www.socials.homes/developers#errors-city_not_found",
  "title": "City not found",
  "status": 404,
  "detail": "\"den_haag\" is not a city Socials Homes covers. Close matches: the_hague. It covers 124 cities; GET https://www.socials.homes/api/v1/cities lists them.",
  "code": "city_not_found",
  "resolution": "Use a slug from GET https://www.socials.homes/api/v1/cities, for example amsterdam or the_hague. Slugs are lowercase and use underscores.",
  "instance": "/api/v1/cities/den_haag",
  "suggestions": [
    {
      "slug": "the_hague",
      "name": "The Hague",
      "country": "NL",
      "country_name": "Netherlands"
    }
  ]
}
```

### invalid_parameter, 400 Invalid parameter

A query or path parameter is missing its expected form: a limit outside 1 to 200, a cursor this API did not issue, a budget outside 50 to 20,000 EUR, or a slug with characters a slug never has.

What to do: Correct the parameters listed in errors and send the request again. Every parameter is described in the OpenAPI document at https://www.socials.homes/openapi.json.

### not_found, 404 Not found

No endpoint exists at this path.

What to do: Check the path. GET https://www.socials.homes/api/v1 lists every endpoint.

### city_not_found, 404 City not found

The slug is well formed but is not a city Socials Homes covers. The problem lists close matches in suggestions when there are any.

What to do: Use a slug from GET https://www.socials.homes/api/v1/cities, for example amsterdam or the_hague. Slugs are lowercase and use underscores.

### method_not_allowed, 405 Method not allowed

The request used a method other than GET, HEAD or OPTIONS. Nothing in this API writes.

What to do: This API is read-only. Send GET (or HEAD); the Allow header lists the methods this path accepts.

### rate_limited, 429 Too many requests

The caller's IP address used up a rate limit window.

What to do: Wait the number of seconds in the Retry-After header, then retry. The RateLimit headers on every endpoint response show how much of each budget is left.

### internal_error, 500 Internal error

Something failed on the Socials Homes side. No detail is exposed.

What to do: Retry after a short wait. If it keeps failing, email hello@socials.homes with the time of the request.

## Versioning and deprecation

- The major version is in the path (`/api/v1`) and in the `X-API-Version` header of every response.
- Within v1, changes are additive only: new endpoints, new optional parameters, new fields in a response. Clients must ignore fields they do not know.
- Nothing in v1 is renamed, removed or changed in meaning. A breaking change ships as a new version under its own path, `/api/v2`.
- A new major version is announced in the changelog below at least 90 days before v1 changes or stops. During that time v1 keeps working, and its responses carry `Deprecation` and `Sunset` headers with the dates.
- The OpenAPI `info.version` (1.0.1) follows SemVer: minor for additive changes, patch for corrections to the document.

## Changelog

### 1.0.1, 2026-10-14

- The official JavaScript SDK and CLI, the socials-homes package on npm: see SDKs and CLI
- The OpenAPI document writes every error response out on its operation, typed as Problem. No response changed

### 1.0.0, 2026-10-14

- First release of the public REST API: GET `/api/v1`, `/api/v1/cities`, `/api/v1/cities/{slug}`, `/api/v1/cities/{slug}/supply` and `/api/v1/plans`
- OpenAPI 3.1 document at `/openapi.json` and `/api/openapi.yaml`, and an RFC 9727 API catalog at `/.well-known/api-catalog`
- RFC 9457 problem details on every error, IETF RateLimit headers on every endpoint response

## MCP servers

The same data is available to MCP clients, public and read-only as well, with no sign-in. The REST API and the MCP tools read the same city list, the same plan table and the same cached live count, so they always agree.

- Product server: `https://www.socials.homes/mcp` (Streamable HTTP). Tools `list_cities`, `get_plans`, `check_city_supply` and `start_home_search`. [Server card](https://www.socials.homes/mcp/server-card)
- Docs server: `https://www.socials.homes/mcp/docs`. How it works, pricing, FAQ, a terms summary and the housing guides, with `search_docs`. [Server card](https://www.socials.homes/mcp/docs/server-card)
- [auth.md](https://www.socials.homes/auth.md): access and limits for both servers
- Both are in the [official MCP registry](https://registry.modelcontextprotocol.io/v0/servers?search=homes.socials): [homes.socials/socials-homes](https://registry.modelcontextprotocol.io/v0/servers/homes.socials%2Fsocials-homes/versions/latest) and [homes.socials/socials-homes-docs](https://registry.modelcontextprotocol.io/v0/servers/homes.socials%2Fsocials-homes-docs/versions/latest)

## Natural-language search (NLWeb)

`https://www.socials.homes/ask` answers a question in natural language with the matching pages on the site: the docs, the housing guides and the covered cities, as schema.org results. It follows NLWeb 0.55: GET with `query`, or POST with `{"query": {"text": "..."}}`. POST answers JSON unless `prefer.streaming` is true, or, when the body does not say, Accept prefers `text/event-stream`; GET streams `start`, `result` and `complete` events unless `streaming=false` or Accept prefers `application/json`. Form bodies and URL parameters on a POST work too, and a question with no match answers the overview pages. Failures use the spec's codes: `INVALID_QUERY`, `RATE_LIMITED`, `INTERNAL_ERROR`. Every result is a page with a snippet of its own text and nothing is generated, so `mode` is always `list`. Public and read-only, 120 requests a minute per IP address.

JSON:

```bash
curl "https://www.socials.homes/ask?query=cancel+my+subscription&streaming=false"
```

Streamed:

```bash
curl -N "https://www.socials.homes/ask?query=documents+to+rent+in+Germany"
```

Answer, first result:

```json
{
  "_meta": {
    "response_type": "answer",
    "response_format": "conversational_search",
    "version": "0.55",
    "mode": "list",
    "site": "socials.homes"
  },
  "query_id": "example",
  "query": "cancel my subscription",
  "results": [
    {
      "@type": "FAQPage",
      "url": "https://www.socials.homes/faq",
      "name": "Socials Homes FAQ, Can I cancel anytime",
      "site": "socials.homes",
      "score": 8,
      "description": "Yes. You can cancel your subscription at any time and you will not be billed again. Cancelling stops future renewals; subscriptions are not refundable, so your access runs to the end of the period you have already paid for. The VIP Keys Pass is the one exception: if you have not…",
      "schema_object": {
        "@context": "https://schema.org",
        "@type": "FAQPage",
        "name": "Socials Homes FAQ",
        "url": "https://www.socials.homes/faq",
        "description": "Answers to common questions: how it works, speed of alerts, cities, cost, discounts, cancelling, safety.",
        "inLanguage": "en",
        "isPartOf": {
          "@type": "WebSite",
          "name": "Socials Homes",
          "url": "https://www.socials.homes"
        },
        "publisher": {
          "@type": "Organization",
          "name": "Socials Homes",
          "url": "https://www.socials.homes"
        }
      }
    }
  ]
}
```

## OpenAPI and discovery

- [OpenAPI 3.1, JSON](https://www.socials.homes/openapi.json), also at `/api/openapi.json` and `/api/v1/openapi.json`
- [OpenAPI 3.1, YAML](https://www.socials.homes/api/openapi.yaml)
- [API catalog](https://www.socials.homes/.well-known/api-catalog) (RFC 9727)
- [These docs in markdown](https://www.socials.homes/developers.md)
- [llms.txt](https://www.socials.homes/llms.txt) and the [agent catalog](https://www.socials.homes/.well-known/ard.json) for every other agent resource

## SDKs and CLI

The official JavaScript SDK and command-line tool are one npm package, [socials-homes](https://www.npmjs.com/package/socials-homes). It has no runtime dependencies, sends only GET requests and needs no key; it retries 429 and 5xx answers safely, throws the API's problem details as typed errors, and its types follow this OpenAPI document. Node 20 or later, browsers, Deno and Bun. Source and issues: [github.com/yosocials/socials-homes-js](https://github.com/yosocials/socials-homes-js).

CLI, no install:

```bash
npx socials-homes supply amsterdam --budget 900
npx socials-homes cities --country NL
npx socials-homes plans --json
```

SDK, after npm install socials-homes:

```js
import { SocialsHomes, SocialsHomesError } from "socials-homes";

const sh = new SocialsHomes();
const { supply } = await sh.getCitySupply("amsterdam", { budgetMaxEur: 900 });
if (supply.available) console.log(supply.listings_within_budget);

for await (const city of sh.cities({ country: "NL" })) console.log(city.slug);

try {
  await sh.getCity("den_haag");
} catch (err) {
  if (err instanceof SocialsHomesError) console.log(err.code, err.problem?.suggestions);
}
```

In another language, the OpenAPI document builds a typed client with a generator such as openapi-generator, and every endpoint works with plain curl or fetch.

## Support

Questions, problems or a use case the API does not cover yet: [hello@socials.homes](mailto:hello@socials.homes). Use of the API is subject to the [terms of service](https://www.socials.homes/terms) and the [privacy policy](https://www.socials.homes/privacy).
