Skip to content

Headless Interface API Reference

PLUS The headless interface is part of the WordPress REST API and responds under the namespace immomakler-headless/v1. The base URL is therefore

https://your-wordpress-address.com/wp-json/immomakler-headless/v1/

All endpoints only respond once you have enabled the interface in the back end of your website (wp-admin) under WP-ImmoMakler → Integrations → Headless; see Headless mode with your own front end.

Read endpoints are public by default. If you have enabled the option Require an API key for read requests too, every request must send the API key, either in the dedicated header or as a bearer token:

Terminal window
curl -H "X-ImmoMakler-Key: YOUR-API-KEY" https://your-wordpress-address.com/wp-json/immomakler-headless/v1/properties
Terminal window
curl -H "Authorization: Bearer YOUR-API-KEY" https://your-wordpress-address.com/wp-json/immomakler-headless/v1/properties

If the key is missing or wrong, the interface responds with 401 and the error code headless_unauthorized.

ParameterMeaning
page, per_pagePage and page size of a list. per_page is limited to 100, and to 1000 for the overview /properties/manifest.
fieldssummary returns the short form for lists (list view data, featured image, status, address, links), full the complete property for the detail view. Lists return summary by default, the single request full.
statusopen shows the properties of the property list, reference only reference properties, all both. Lists return open by default, the single request all.
modified_sinceOnly properties changed since this point in time (ISO 8601, for example 2026-10-11T08:00:00Z).
localeLanguage of the labels and category names, for example de_DE or en. On multilingual websites with WPML or Polylang, the parameter also switches the language of the properties.

Lists respond with an envelope:

{
"items": [],
"total": 42,
"total_pages": 3,
"page": 1,
"per_page": 20,
"generated_at": "2026-10-11T08:15:00+00:00"
}

In addition, lists carry the headers X-WP-Total, X-WP-TotalPages and a Link header with rel="next" and rel="prev".

Read responses carry Cache-Control: public, max-age=60, s-maxage=300, stale-while-revalidate=600 and an ETag. If you send the ETag in the If-None-Match header on the next request, the interface responds with 304 Not Modified as long as the data has not changed. In the browser, the browser’s HTTP cache handles this by itself; if a front end sends If-None-Match itself, its code receives an empty response when the data is unchanged and must then use the data it loaded earlier. After every transfer from your CRM software and after every change to a property, WP-ImmoMakler discards its cache, so the next request returns the current data.

Describes the website once per build: the address of WordPress and the front end, the path patterns, the available languages (locales, and under languages the code, locale, name, default language and front-end directory prefix of each), currency, feature set (PDF, watch list, property alerts, radius search), all categories with query variable and URL path, the data fields chosen in your settings (keys.archive, keys.single, keys.single_areas, keys.single_prices, keys.epass, keys.contact), the registered image sizes and, under search, the structure of your search form: the categories shown, the ranges with slug, unit, step size and maximum value, the sort orders with labels, the default sort order, the radius search with its radii and, under search.places, the address autocomplete of the radius search if you have set up a provider for it (see below).

The property list, most recently changed first. Supports page, per_page, fields, status, modified_since and locale.

The compact overview of all visible properties for reconciliation when building a static front end. Each entry states objektnr_extern, post_id, slug, language, modified_gmt, public_since, is_reference, is_project, is_project_unit and frontend_url. Supports page, per_page (up to 1000), status and locale.

One property, addressed by its object number. Pass the object number URL-encoded (A%2F1 for A/1); upper and lower case do not matter. A property that the property list does not show responds with 404 and the error code headless_not_found. Supports fields, status and locale.

The property search with the filters of your website. The parameters are named as in the search of your WordPress website, so a filtered address of the property list and a search request mean the same thing:

  • Categories via their query variable (typ, vermarktungsart, nutzungsart, ort, land, ausstattung) with term slugs; the status category is called objektstatus here, because status selects between open properties, references and all, as with /properties. Separate several terms with a comma if any one of them should match (typ=wohnung,haus), or with a plus if all must match (ausstattung=balkon+garten). For features, a list (ausstattung[]=balkon&ausstattung[]=garten) also means “all”.
  • Ranges via von-{slug} and bis-{slug}, for example von-kaufpreis=200000&bis-kaufpreis=500000 or von-zimmer=3. Which ranges exist and how far they reach is stated in site.search.ranges. The ranges apply exactly as in the search of your website; a purchase price of 0 (on request) is not excluded, just as there.
  • objekt-id: object number or its beginning.
  • center and radius (km) for the radius search (PLUS only, with radius search enabled); radius is optional. A ort filter is then ignored, as on the website. Under radius, the response gives the address found or the error if the place could not be determined.
  • bounds=south,west,north,east for the visible map section in decimal degrees.
  • order with a key from site.search.orders; without it, the default sort order of your website applies.
  • facets=1 adds, under facets, the terms of the result for each category with their count, most frequent first, for counters next to the filters.
  • In addition page, per_page, fields, status and locale as with /properties.

Besides the properties, the response contains under applied the filters as the interface understood them, so a front end can restore its form state from them. The same visibility rules apply as for the property list.

If a provider for address autocomplete is set up under WP-ImmoMakler > Integrations > Geo Services, site.search.places describes how a front end fetches suggestions while a visitor types in the radius search field; without a provider, places is null. The flow is the same as in the search form of your website, and the provider’s key stays on your server:

  1. GET on places.token_url returns a token valid for 15 minutes and, under proxy_url, the address of the Fast-API proxy, which responds without WordPress; proxy_url is empty if the proxy is switched off or the provider is not Google.
  2. Each input is sent as a POST with the JSON body { "q": "Hambu", "session": "…", "languageCode": "de" } and the header X-ImmoMakler-Sign: <token> to proxy_url, or alternatively to places.suggest_url. The response is { "suggestions": [ { "label": "Hamburg, Germany", "placeId": "…" } ] }.
  3. The front end passes the chosen label to /search as center.

Both endpoints are also listed in the OpenAPI document and in llms.txt, with their own namespace immomakler/v1 as the server. sessions indicates whether the provider bills per typing sequence; in that case a front end uses the same random session value for a sequence of inputs and starts a new one after a selection or a three-minute pause. min_length, max_results and debounce_ms are the values of your website. Call the endpoints from the browser, not from a server: the limits (90 requests per minute and visitor at the proxy, 600 requests per hour that are not answered from the cache) apply per visitor. The proxy accepts requests from the address of your front end and the allowed origins; it picks up a change to these settings on the next save.

Similar properties, as your website shows them below a property: same property type and marketing type nearby, gradually relaxed until something matches, in random order. count limits the number (default: your setting, at most 12). Units of the same construction project are left out if your website shows them in their own table.

The units of a construction project, as the “Other units in this project” table shows them: all units by object number, including sold and rented ones that the property list no longer carries. For a property that is not a construction project, the interface responds with 404.

All categories (property type, marketing type, usage type, location, country, features, status) with their terms: term_id, slug, name, parent, count, description and source_slug, the language-independent slug of the term. With hide_empty=false, terms without properties also appear. The count includes all properties of a term, even those that the property list hides.

The published contact persons with name, description, portrait, the contact fields from your settings and the number of their properties.

GET /content/pages and GET /content/pages/{path}

Section titled “GET /content/pages and GET /content/pages/{path}”

The published pages of your website, excluding the WP-ImmoMakler template pages. For each page the list gives the hierarchical path (about-us/team), the parent page, the order, the page template, whether it is the front page and its address on the front end. The single request by path returns the page as a block structure: each block carries its name (core/paragraph, immomakler/single-data, …), its attributes, its inner HTML, its inner blocks and, under rendered, the HTML that WordPress would output for it. WP-ImmoMakler blocks have no rendered; a front end displays them from the property data. In addition there are content_html, the entire content as one HTML fragment, and seo with the title and description from Yoast SEO or Rank Math, if maintained there. Links to your WordPress website already point to the front end in both versions; images and documents stay on WordPress.

GET /content/posts and GET /content/posts/{slug}

Section titled “GET /content/posts and GET /content/posts/{slug}”

The published posts, newest first, with categories, tags, excerpt and featured image; supports modified_since. The single request returns the same block structure as for pages.

GET /spam/token, POST /enquiries and POST /widerruf

Section titled “GET /spam/token, POST /enquiries and POST /widerruf”

The form routes: the token for spam protection, contact enquiries and withdrawals from your own front end. They accept requests only from the allowed origins or with the API key and are never cached. The page Forms via the headless interface describes the structure of the documents, the error codes and how it works together with spam protection; site.forms lists the fields, mandatory fields and consent texts of your forms.

The interface reports changes to your properties to your front end on its own; the page Webhook, links and redirect describes the structure, signature and retry of the message.

site.menus contains the three menu locations that WP-ImmoMakler registers for your front end and that you assign in the backend of your website (wp-admin) under Appearance → Menus: primary (main navigation), footer and legal (legal notice, privacy policy). Each entry carries the title, the address stored in WordPress, the address on the front end under frontend_url, the target, CSS classes, the type of the target and its sub-entries.

site.branding delivers the design settings from the Branding and Astro starter sections of the headless settings: the design preset (preset), name and tagline of the website, logo and default thumbnail with all image sizes, primary and accent colour as hex values, the font combination (sans, serif-headlines, humanist, system) and the corner rounding (none, small, medium, large). A front end writes these values into its design tokens, so you can change the appearance in WordPress without touching the front end.

The full form (fields=full) of a property, abridged:

{
"objektnr_extern": "A-1234",
"openimmo_obid": "7f3c…",
"post_id": 4711,
"slug": "wohnung-kaufen-in-hamburg-a-1234",
"language": "de",
"published_gmt": "2026-09-01T10:00:00+00:00",
"modified_gmt": "2026-10-10T08:15:00+00:00",
"title": "3-Zimmer-Wohnung in Hamburg-Altona, 85 m²",
"object_title": "Helle Stadtwohnung mit Blick aufs Wasser",
"subtitle": "Hamburg-Altona · Wohnung · Kauf",
"excerpt": "…",
"status": {
"terms": [ { "slug": "offen", "name": "offen" } ],
"is_open": true,
"is_reserved": false,
"is_reference": false,
"archived": false,
"is_project": false,
"is_project_unit": false,
"is_hidden_project": false,
"project_id": null,
"public_since": "2026-09-01T10:00:00+00:00"
},
"marketing": { "vermarktungsart": "kauf", "nutzungsart": "wohnen", "objektart": [ "wohnung" ] },
"taxonomies": { "immomakler_object_type": [ { "term_id": 12, "slug": "wohnung", "name": "Wohnung", "parent": 0, "source_slug": "wohnung" } ] },
"address": { "street": "Elbchaussee", "house_number": "1", "zip": "22763", "city": "Hamburg", "district": "Altona", "state": "Hamburg", "country": "DEU", "country_name": "Deutschland" },
"geo": { "lat": 53.55, "lng": 9.93, "exact": true, "radius_m": null },
"fields": {
"property": [ { "key": "rooms", "source_key": "anzahl_zimmer", "label": "Zimmer", "value": "3", "text": "3 Zimmer", "raw": 3 } ],
"areas": [ { "key": "living_area", "source_key": "wohnflaeche", "label": "Wohnfläche ca.", "value": "85 m²", "text": "Wohnfläche ca. 85 m²", "raw": 85 } ],
"prices": [ { "key": "purchase_price", "source_key": "kaufpreis", "label": "Kaufpreis", "value": "350.000 €", "text": "Kaufpreis 350.000 €", "raw": 350000 } ],
"epass": []
},
"descriptions": {
"objektbeschreibung": { "label": "Beschreibung", "html": "<p>…</p>", "text": "…" }
},
"media": {
"featured": { "id": 99, "alt": "", "title": "Wohnzimmer", "mime": "image/jpeg", "sizes": { "immomakler-archive-thumb": { "url": "…", "width": 400, "height": 267 }, "full": { "url": "…", "width": 2000, "height": 1333 } } },
"images_count": 12,
"floorplans_count": 1,
"images": [],
"floorplans": [],
"downloads": [ { "id": 101, "title": "Exposé", "url": "…", "filename": "expose.pdf", "is_pdf": true, "is_floorplan": false } ],
"links": [],
"videos": [ { "type": "embed", "url": "https://www.youtube.com/watch?v=…", "embeddable": true } ],
"virtual_tours": []
},
"contact": { "post_id": 7, "name": "Erika Mustermann", "slug": "erika-mustermann", "wp_url": "…", "photo": { "…": "…" }, "fields": [ { "key": "kontaktperson_fullname", "label": "Ansprechpartner", "value": "Erika Mustermann" } ] },
"seo": { "title": "…", "description": "…", "canonical": "https://ihr-frontend.de/immobilien/wohnung-kaufen-in-hamburg-a-1234/", "og_image_url": "…" },
"json_ld": [ { "@context": "https://schema.org", "@type": [ "Product" ] } ],
"links": {
"frontend_url": "https://ihr-frontend.de/immobilien/wohnung-kaufen-in-hamburg-a-1234/",
"wp_url": "https://ihre-wordpress-adresse.de/immobilien/wohnung-kaufen-in-hamburg-a-1234/",
"archive_frontend_url": "https://ihr-frontend.de/immobilien/",
"og_image_url": "…/og-image/1/",
"pdf_url": "…?pdf",
"qr_url": "…?qrcode"
}
}

seo contains the property’s SEO title and description, as WP-ImmoMakler generated them during the import or as the AI texts wrote them; if you have entered your own title or description for the property in Yoast SEO or Rank Math, these take precedence. Templates with placeholders such as %%title%% are left to the SEO plugin and are not taken into account here.

Each data field carries an English key that is the same on every website, and under source_key the name that WordPress and OpenImmo use. It also includes the label (label) and the display text (value) in the language of the website, both combined as a phrase (text) as in the text form of the Elementor tags, for example “3.5 rooms” or “2 parking spaces”, as well as the raw value (raw) as a number or string. If a display text contains HTML, such as a link, html additionally provides the unaltered version. The short form (fields=summary) replaces the groups property, areas, prices and epass with archive and archive_prices, that is, the fields of the list view, and omits descriptions, json_ld and the image, document and video lists.

The interface describes itself. Both endpoints are always public, even if you require the API key for read requests, because they do not output any property data.

An OpenAPI 3.1 document with all endpoints, parameters, response schemas and both authentication methods. Development tools such as Postman, Insomnia or generators for typed clients read it directly; the parameters come from the registered routes, so the document keeps pace with every extension of the interface. Under webhooks it describes the messages your website sends to the front end, with headers, signature and structure; the introduction lists the error codes and the rule for forms that a server passes on.

A short description in text format following the llms.txt convention: the purpose of the interface, base URL, authentication, the rule to address properties by object number, the error codes, the webhook and the list of endpoints with links. The llms-full.txt variant adds the parameters and the response fields for each endpoint.

AI-assisted tools that generate code from a description can work with these two files without further explanation:

  • Lovable, Bolt, v0, Replit: Give the address of llms.txt or openapi.json in the prompt, for example “Build a property list with the API at https://your-wordpress-address.com/wp-json/immomakler-headless/v1/llms.txt”. Read requests work directly from the browser, because WordPress allows the REST API for all origins.
  • Base44: Store the address of openapi.json as a custom OpenAPI integration. If you require the API key for read requests, store it there as a secret; Base44 then calls the interface from a backend function.

Generated code should always address properties by objektnr_extern and use the links from links.frontend_url; both are stated that way in llms.txt.

HTTP statusCodeMeaning
401headless_unauthorizedThe API key is missing or wrong although it is required for read requests.
404headless_not_foundThe property or contact person does not exist or is not shown publicly.
404rest_no_routeThe interface is not enabled.
400headless_invalid_modified_sinceThe modified_since parameter is not a valid date.
403headless_forbiddenA form route was called neither from an allowed origin nor with the API key.
422headless_validation_failedMandatory fields are missing or invalid; data.errors lists them.
422headless_unknown_propertyAn object number in properties does not belong to any published property.
422headless_spam_rejectedThe submission was rejected as spam.
429headless_rate_limitedToo many requests in this hour.
502headless_delivery_failedThe enquiry is saved but could not be delivered to all recipients.
  • immomakler_headless_frontend_url changes a URL that the interface outputs for the front end. Parameters: the URL, the WordPress ID of the property (0 for the property list) and the kind (property or archive).
  • immomakler_headless_property changes the JSON document of a property before it is output and cached. Parameters: the document, the WordPress ID and the form (summary or full).
  • immomakler_headless_presets determines which design presets the Astro starter section offers for selection. Parameter: an array of preset slug and label.
  • immomakler_headless_webhook_payload changes the webhook message before it is signed and sent. Parameters: the message as an array and the name of the event (properties.changed or ping).
  • immomakler_contact_property_url changes the address of a property in the email of a contact enquiry. With the interface enabled and a front end address set, it points to the front end. Parameters: the URL and the WordPress ID of the property.