Scrappa's GET /api/ohne-makler/search endpoint returns provisionsfrei (no-agent) German property listings from ohne-makler.net as structured JSON instead of HTML that you would otherwise have to scrape.
Endpoint reference for ohne-makler.net listing search
One request performs one upstream category-page read and returns up to 24 listing cards plus the page's full embedded state. Structured searches cover 14 property types (wohnung, haus, grundstueck, zimmer, buero, einzelhandel, gastronomie, lagerhalle, landwirtschaftliches-objekt, wohnen-auf-zeit, gewerbliche-freizeitimmobilie, zinshaus-rendite, sonstiges, immobilie) crossed with transaction=kaufen|mieten, addressed through state and optional city URL segments (German Bundesland and city/Kreis URL slugs exactly as published — examples: baden-wurttemberg, buero, grundstueck, munchen, kreis-augsburg-stadt). Both segments are optional: state-only searches return the whole state, no segments return a Germany-wide class, and an unknown or rotated slug returns a non-billable 404. price_min/price_max, area_min/area_max, sort, and page (24 results per page) filter and paginate the same single read.
Free-text search
q takes a postal code (80331), city (München), Kreis (Landkreis München), or Bundesland (Bayern). It is resolved exactly like the site's own autocomplete (an exact name match wins, otherwise the first suggestion) and the search then runs through the target's search channel, so results are limited to that place: q=80331&type=wohnung&transaction=kaufen returns only listings in 80331. The resolved place is returned as location (model is ZIPCODE, CITY, REGION, or STATE). type and transaction are optional with q; without them every property class is searched. Price/area bounds, sort, and page apply as usual. Location segments (state/city) do not apply to free-text queries and are rejected with 422.
radius (10/25/50/100 km Umkreis) works when q resolves to a city, and on state + city category searches. The site offers no Umkreis for postal codes, Kreise, or states, so radius with such a q returns a non-billable 422 radius_requires_city; search the city name instead. A q that matches nothing returns a non-billable 404 location_not_found, and a listing object number (OM-…) returns a non-billable 422 object_number_query pointing to the listing endpoint.
What the search response returns
Each result carries id, object_number (OM-…), title, absolute url, price plus the price_on_request flag (Kaufpreis: auf Anfrage becomes price: null, price_on_request: true — a genuine absence, not an error), postal_code, city, district, street (published only), rooms, living_area_m2, plot_area_m2, preview_image, the seller_type badge ("Privatangebot" when present — null never means "not private", the badge simply is not rendered on every listing), and latitude/longitude. Pagination fields are page, page_size (24), total_results, last_page, and next_page; requesting a page beyond the last page is a non-billable 404. sort accepts price_asc, price_desc, date_asc, date_desc. price_min/price_max and area_min/area_max are echoed back in query, and both price_histogram and area_histogram cover the whole result set, not just the page.
The response also ships the page's embedded state: price_histogram and area_histogram as structured buckets (x, count, label — count: null means the source published no listing for that bucket), the complete-result geojson FeatureCollection with one feature per listing across the whole result set (capped by the source at roughly 1 210 features), breadcrumbs, the resolved search_form state, and the page's JSON-LD aggregate_rating (there is no public review list on this platform — the aggregate rating is the only ratings surface).
An empty result set (0 cards, 0 geojson features) is a valid 200 success, not an error.
What this API does not cover
This endpoint is read-only listing data. There is no account or watchlist access, no messaging or contact-form submission, no listing creation, and no payments. The platform publishes no price history and no source maps, exposes no GraphQL API, and offers no public review list - the JSON-LD aggregate rating is the only ratings surface. The site financing widget is not exposed through this API. The English mirror, new-build project pages, map tiles, and content/guide pages are deferred and not part of this version.
Run this endpoint
Endpoint
Parameters
Start with the required fields, then add optional filters only when your use case needs them.
Runnable path
This endpoint has no required query parameters.
12 optional filters available.
string
Optional
Free-text location: postal code (80331), city (München), Kreis, or Bundesland. Results are limited to the resolved place, returned as location. state/city segments are not accepted alongside q.
coffee shops
string
Optional
Property type: wohnung, haus, grundstueck, zimmer, buero, einzelhandel, gastronomie, lagerhalle, landwirtschaftliches-objekt, wohnen-auf-zeit, gewerbliche-freizeitimmobilie, zinshaus-rendite, sonstiges, immobilie. Required unless q is used.
wohnung
string
Optional
kaufen (buy) or mieten (rent). Required unless q is used.
mieten
string
Optional
Bundesland URL slug, transliterated (e.g. hamburg, bayern, baden-wurttemberg). Omit for a Germany-wide search.
hamburg
string
Optional
City or Kreis URL slug; requires state (e.g. hamburg/hamburg).
hamburg
integer
Optional
Result page, 1-based. Pages beyond the last page return a non-billable 404.
1
string
Optional
price_asc, price_desc, date_asc, or date_desc.
relevance
integer
Optional
Minimum price in EUR.
10
integer
Optional
Maximum price in EUR.
10
integer
Optional
Minimum living area in m².
10
integer
Optional
Maximum living area in m².
10
integer
Optional
Umkreis in km: 0, 10, 25, 50, or 100. Applies when q resolves to a city or with state + city. Postal codes, Kreise, and states have no Umkreis on the site (422 radius_requires_city).
10
Request Examples
<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"x-api-key: YOUR_API_KEY_HERE"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
<?php
use Illuminate\Support\Facades\Http;
$response = Http::timeout(30)
->withHeaders(['x-api-key' => 'YOUR_API_KEY_HERE'])
->get('https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1');
if ($response->successful()) {
echo $response->body();
} else {
echo "Error: " . $response->status();
}
const options = {
method: 'GET',
headers: {
'x-api-key': 'YOUR_API_KEY_HERE'
}
};
fetch('https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1', options)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
const axios = require('axios');
const options = {
method: 'GET',
url: 'https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1',
headers: {
x-api-key: 'YOUR_API_KEY_HERE',
}
};
try {
const response = await axios(options);
console.log(response.data);
} catch (error) {
console.error('Error:', error.message);
}
require 'net/http'
require 'uri'
uri = URI.parse("https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Get.new(uri.request_uri)
request['x-api-key'] = 'YOUR_API_KEY_HERE'
begin
response = http.request(request)
puts response.body
rescue => e
puts "Error: #{e.message}"
end
import http.client
import json
conn = http.client.HTTPSConnection("scrappa.co")
headers = {
'x-api-key': 'YOUR_API_KEY_HERE',
}
try:
conn.request("GET", "/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1", headers=headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
except Exception as e:
print(f"Error: {e}")
finally:
conn.close()
import requests
headers = {
'x-api-key': 'YOUR_API_KEY_HERE',
}
try:
response = requests.get('https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1', headers=headers)
response.raise_for_status()
print(response.text)
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class ApiExample {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1")
.addHeader("x-api-key", "YOUR_API_KEY_HERE")
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
System.out.println(response.body().string());
} else {
System.out.println("Error: " + response.code());
}
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("x-api-key", "YOUR_API_KEY_HERE")
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response:", err)
return
}
fmt.Println(string(body))
}
#!/bin/bash
curl -X GET \
-H "x-api-key: YOUR_API_KEY_HERE" \
"https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1"
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY_HERE");
try
{
var response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, "https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1"));
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
import axios from 'axios';
async function run(): Promise<void> {
try {
const response = await axios({
method: 'GET',
url: 'https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1',
headers: {
'x-api-key': 'YOUR_API_KEY_HERE',
},
});
console.log(response.data);
} catch (error) {
console.error('Error:', error);
}
}
void run();
use reqwest::Client;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let response = client
.get("https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1")
.header("x-api-key", "YOUR_API_KEY_HERE")
.send()
.await?;
println!("{}", response.text().await?);
Ok(())
}
Response Schema
Example response fields are illustrative; inspect the JSON before integrating.
Example response fields
Scan these fields before integrating.
success
mode
page
page_size
total_results
last_page
next_page
sort
+7 more
Common results fields
id
object_number
title
url
{
"success": true,
"mode": "category",
"page": 1,
"page_size": 24,
"total_results": 92,
"last_page": 4,
"next_page": 2,
"sort": null,
"results": [
{
"id": 499372,
"object_number": "OM-499372",
"title": "Helle 2-Zimmer-Dachgeschosswohnung mit Balkon in Hamburg",
"url": "https://www.ohne-makler.net/immobilie/499372/",
"price": 1200,
"price_on_request": false,
"postal_code": "22179",
"city": "Hamburg",
"district": "Bramfeld",
"street": null,
"rooms": 2,
"living_area_m2": 55,
"plot_area_m2": null,
"preview_image": "https://media.ohne-makler.net/...",
"seller_type": null,
"latitude": 53.60283,
"longitude": 10.09564
}
],
"price_histogram": {
"min": 0,
"max": 2600,
"buckets": [
{
"x": 500,
"count": 3,
"label": "400 - 500 \u20ac"
}
]
},
"area_histogram": {
"min": 0,
"max": 650,
"buckets": [
{
"x": 50,
"count": 13,
"label": "25 - 50 m\u00b2"
}
]
},
"breadcrumbs": [
{
"name": "Immobilien",
"url": "/immobilien/"
}
],
"search_form": {
"location": {
"model": "CITY",
"id": 109316,
"name": "Hamburg"
}
},
"aggregate_rating": {
"rating_value": "4.65",
"best_rating": "5",
"rating_count": 4993
},
"geojson": {
"type": "FeatureCollection",
"features": []
}
}
Errors
Handle these documented responses before retrying or showing customer-facing failures.
End Of Pagination / Unknown Segment
The requested page is beyond the last page (`end_of_pagination`) or the state/city slug does not exist (`not_found`). Non-billable.
{
"meta": {
"billable": false,
"retryable": false,
"endpoint_family": "search"
},
"error": {
"code": "end_of_pagination",
"message": "The requested page is beyond the last page for this query."
},
"success": false
}Validation Error
A query parameter failed validation. Non-billable.
{
"errors": {
"type": [
"The type field is required when transaction is present."
]
},
"message": "The type field is required when transaction is present."
}Location Not Found
`q` matched no German postal code, city, Kreis, or state (`location_not_found`). Non-billable.
{
"meta": {
"billable": false,
"retryable": false,
"endpoint_family": "search"
},
"error": {
"code": "location_not_found",
"message": "No German city, postal code, Kreis, or state matches q."
},
"success": false
}Radius Needs A City
`radius` was combined with a `q` that resolved to a postal code, Kreis, or state (`radius_requires_city`), or `q` is a listing object number (`object_number_query`). Non-billable.
{
"meta": {
"billable": false,
"retryable": false,
"endpoint_family": "search"
},
"error": {
"code": "radius_requires_city",
"message": "radius only applies when q resolves to a city (not a postal code, Kreis, or state). Search the city name with radius, or omit radius."
},
"success": false
}Upstream Unavailable
The upstream request failed or could not be parsed after retries. `error.code` is one of `upstream_unavailable`, `parser_drift`, `upstream_blocked`, or `transport_error`. Retryable and never billed. Clients are never rate limited.
{
"meta": {
"attempts": 4,
"billable": false,
"retryable": true,
"last_status": 503,
"endpoint_family": "search"
},
"error": {
"code": "upstream_unavailable",
"message": "OhneMakler search is temporarily unavailable. Please retry."
},
"success": false
}Generate Code with AI
Copy a ready-made prompt with all the endpoint details, parameters, and example responses. Paste it into ChatGPT, Claude, or any AI assistant to instantly generate working code.
Related Endpoints
OhneMakler API FAQ
Does ohne-makler.net offer an official API?
No. ohne-makler.net has no public developer API. Scrappa provides a documented read-only API over its public listing surfaces and returns normalized JSON with API key authentication.
What is the difference between structured and free-text search?
Structured search reads one category URL built from type, transaction, state, and city segments. Free-text search (q) first resolves the postal code or place name through the site's location autocomplete, then runs the platform's own search for exactly that place. Either way one request costs one credit and listing details are never fetched in the background.
Are empty results charged?
A 200 response with zero listings is a valid answer and is billed. Failures — out-of-range pages, unknown slugs, validation errors, and upstream outages — never consume credits.
Try It Live
Test this endpoint in our interactive playground with real data.