Skip to content
Scrappa Get API key
OhneMakler API 1 credit/request

OhneMakler Search API Documentation

GET https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1

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.

OhneMakler Search API Documentation 1 credit/request

Endpoint

Request preview
GET
https://scrappa.co/api/ohne-makler/search?type=wohnung&transaction=mieten&state=hamburg&city=hamburg&page=1
Auth header
x-api-key
Cost
1 credit/request
Response preview
200 OK
{
    "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",
...

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.

q 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.

Example value coffee shops
type 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.

Example value wohnung
transaction string Optional

kaufen (buy) or mieten (rent). Required unless q is used.

Example value mieten
state string Optional

Bundesland URL slug, transliterated (e.g. hamburg, bayern, baden-wurttemberg). Omit for a Germany-wide search.

Example value hamburg
city string Optional

City or Kreis URL slug; requires state (e.g. hamburg/hamburg).

Example value hamburg
page integer Optional

Result page, 1-based. Pages beyond the last page return a non-billable 404.

Example value 1
sort string Optional

price_asc, price_desc, date_asc, or date_desc.

Example value relevance
price_min integer Optional

Minimum price in EUR.

Example value 10
price_max integer Optional

Maximum price in EUR.

Example value 10
area_min integer Optional

Minimum living area in m².

Example value 10
area_max integer Optional

Maximum living area in m².

Example value 10
radius 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).

Example value 10

Request Examples

PHP
<?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
<?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();
}
JavaScript
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));
JavaScript
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);
}
Ruby
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
Python
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()
Python
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}")
Java
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());
        }
    }
}
Go
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))
}
Terminal
#!/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"
C#
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}");
        }
    }
}
TypeScript
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();
RUST
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
JSON Response
200 OK
{
    "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.

404

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
}
422

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."
}
404

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
}
422

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
}
503

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.