# immoscout24-kleinanzeigen-apply-bot (`beige_quaff/immoscout24-kleinanzeigen-apply-bot`) Actor

Monitors ImmoScout24 and Kleinanzeigen for new rental listings, filters them by area, price per m² and keywords, then sends a personalized application or message from your own account. Includes deduplication, listing snapshots and email alerts.

- **URL**: https://apify.com/beige\_quaff/immoscout24-kleinanzeigen-apply-bot.md
- **Developed by:** [Vincent](https://apify.com/beige_quaff) (community)
- **Categories:** Real estate, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $150.00 / 1,000 application sents

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## ImmoScout24 & Kleinanzeigen Apply Bot - Automated Rental Applications

Finding a flat in Germany is a race you lose by being asleep. This Actor watches **ImmoScout24** and **Kleinanzeigen** for new rental listings, filters out the ones you would never take anyway, and sends a **personalized application from your own account** within minutes of a listing going online - while you are at work, commuting, or in bed.

Built for the German rental market: Berlin, Munich, Hamburg, Cologne and beyond. Every application is written per listing, mentioning the actual room count, size, district and move-in date, so it does not read like a template.

### What it does

- **Monitors** your saved searches on ImmoScout24 or Kleinanzeigen and picks up only listings that are genuinely new
- **Filters** by rent per m2, keywords (furnished, sublet, swap, WBS) and, for Berlin, by postal code and radius
- **Writes a personalized application** per listing from your template, filling in rooms, size, district, period features and move-in date
- **Sends it from your own account** through the official apply and message Actors
- **Never applies twice** to the same listing, across runs and across schedules
- **Archives every listing** you applied to as an HTML snapshot with photos, so you still have it after the ad disappears
- **Emails you** a summary with the text that was sent, and keeps a filterable dashboard of every run

### Before you start: required Actors

This Actor is the brain, not the hands. It orchestrates existing scraper and apply Actors, which **you need access to as well**. Please check these costs before you start, they are not included in this Actor's price:

| What | Actor | Cost (August 2026) |
|------|-------|--------------------|
| ImmoScout24 listings | [`clearpath/immoscout24-api-pro`](https://apify.com/clearpath/immoscout24-api-pro) | pay per result |
| ImmoScout24 applications | [`clearpath/immoscout24-apply-bot`](https://apify.com/clearpath/immoscout24-apply-bot) | ~$79 / month |
| Kleinanzeigen listings | [`clearpath/kleinanzeigen-immobilien-api-pro`](https://apify.com/clearpath/kleinanzeigen-immobilien-api-pro) | ~$45 / month |
| Kleinanzeigen messages | [`clearpath/kleinanzeigen-message-api`](https://apify.com/clearpath/kleinanzeigen-message-api) | ~$0.49 per message |
| Email notifications | [`apify/send-mail`](https://apify.com/apify/send-mail) | free |

You only need the Actors for the portal you actually use. Running Kleinanzeigen only is the cheaper entry point.

#### The two monthly fees are going away

Apify retires the rental pricing model on **1 October 2026**. The two Actors above that currently cost a flat monthly fee have to move to a usage-based model, so from October there is no monthly fee to pay before your first application.

Whether your total drops depends on how you use them. Flat fees are replaced by per-use pricing, which is a clear win if you run a search a few times a day, and less so if you monitor many cities around the clock. `clearpath/immoscout24-api-pro` already switched to pay-per-result, which is a good hint at where the others are heading.

Prices in the table are from August 2026 and are set by their authors, not by this Actor. **Check the linked pages for what they cost today** before you budget.

If you already use different scrapers, you can point this Actor at them with the override fields under *Underlying Actors*.

### Quick start

#### Kleinanzeigen

1. Run [`clearpath/kleinanzeigen-message-api`](https://apify.com/clearpath/kleinanzeigen-message-api) once with `operation: login` and your account details. If Kleinanzeigen asks for a code, run it again with `operation: verify2fa`. This is a one-time step.
2. Configure this Actor:

```json
{
  "platform": "kleinanzeigen",
  "startUrls": [
    "https://www.kleinanzeigen.de/s-wohnung-mieten/berlin/anzeige:angebote/c203l3331+wohnung_mieten.qm_d:70.00%2C+wohnung_mieten.zimmer_d:3.0%2C"
  ],
  "kaEmail": "you@example.com",
  "emailTo": "you@example.com",
  "monitorMode": true,
  "runApply": false,
  "maxAppliesPerRun": 3,
  "maxColdPricePerSqm": 25,
  "allowedZipCodes": ["10119", "10179", "10249", "10405", "10997"],
  "locationRadiusKm": 1.5,
  "excludedListingKeywords": ["möbliert", "befristet", "Untermiete", "WBS"],
  "applicationTemplate": "{{anrede}}\n\nIhre {{roomsStr}}-Zimmer{{altbauSuffix}} Wohnung{{locationSuffix}} hat mich sofort angesprochen.{{highlightsSuffix}} {{vacantPhrase}}\n\nZu mir: berufstätig, Nichtraucher, keine Haustiere. Bewerbungsmappe sende ich gerne sofort zu.\n\nViele Grüße"
}
```

3. Start with `runApply: false`. Nothing is sent, but you see exactly which listings would qualify.
4. Happy with the result? Set `runApply: true` and schedule the task every 15 to 30 minutes.

#### ImmoScout24

Same, but set `platform: "immoscout"`, use ImmoScout24 search URLs and provide `username` plus `password` for your account.

### Output

One dataset record per listing, with the decision and why:

```json
{
  "status": "applied",
  "platform": "kleinanzeigen",
  "exposeId": "3452915683",
  "listingUrl": "https://www.kleinanzeigen.de/s-anzeige/...",
  "title": "Helle 3-Zimmer-Altbauwohnung mit Balkon",
  "cityDistrict": "Friedrichshain",
  "zipCode": "10245",
  "rooms": 3,
  "size": 105,
  "rentCold": 1450,
  "rentTotal": 1785,
  "pricePerSqmCold": 13.81,
  "filterReasons": [],
  "locationStage": "geo",
  "locationDistanceKm": 0.64,
  "applicationMessage": "Hallo,\n\nIhre 3-Zimmer Altbau Wohnung in Friedrichshain ...",
  "conversationId": "2dk9x:3cc4mpn:2pm0lqzj3",
  "snapshotHtmlUrl": "https://api.apify.com/v2/key-value-stores/.../records/...",
  "processedAt": "2026-08-06T10:43:22.119Z"
}
```

Rejected listings arrive with `status: "filtered"` and a populated `filterReasons`, so you can see whether your filters are too tight.

### Pricing

You pay per sent application. Collecting and filtering listings is billed per dataset record, which is a fraction of a cent.

A realistic scheduled run in monitor mode looks at a handful of new listings and sends one or two applications, so a normal day costs the price of a coffee - plus the underlying Actors listed above, which are the larger part of the bill today and get cheaper for light users once their monthly fees end in October 2026.

Use **Max applications per run** to cap your spend, and the run-level spending limit in Apify Console for a hard ceiling. The Actor stops sending once that limit is reached.

### Features in detail

#### Personalized applications

Placeholders are filled from the listing data, so every message is specific:

| Placeholder | Becomes |
|-------------|---------|
| `{{anrede}}` | Salutation |
| `{{roomsStr}}` | `3` |
| `{{sizeStr}}` | `105` |
| `{{altbauSuffix}}` | ` Altbau` for pre-1950 buildings |
| `{{locationSuffix}}` | ` in Friedrichshain (Boxhagener Str.)` |
| `{{highlightsSuffix}}` | Condition and balcony sentence |
| `{{vacantPhrase}}` | Move-in date sentence |
| `{{abloeseSentence}}` | Added when the ad mentions a handover fee |
| `{{aiParagraph}}` | Optional AI sentence, needs an OpenAI key |
| `(pictures)...(/pictures)` | Included only when the listing has photos. Aliases: `(photos)`, `(fotos)`, `(bilder)` |

Example: `Die Lage gefällt mir.(pictures) Die Fotos der Wohnung haben mich sofort überzeugt.(/pictures) {{vacantPhrase}}`

District names are normalized for Berlin, Munich, Hamburg, Cologne, Stuttgart and Düsseldorf, so your text says "in Prenzlauer Berg" rather than a raw data field.

#### Area filter (Berlin)

Instead of one search URL per postal code, use one broad city search and list the postal codes you care about. Neighbouring postal codes, coordinates and district names are derived automatically:

```
Location filter: 8 core ZIP(s) -> 31 ZIP(s) within 1.5 km
Location filter results: geo=34 zip=0 district=0 unresolved=0 skipped=161
```

Each listing is matched on the first location signal it actually has - coordinates, then postal code, then district. Listings with none of those are **kept**, never silently dropped, and counted as `unresolved` so you can see how often it happens.

This matters for cost: each search URL is a separate proxied scrape. One city-wide search instead of eight postal code searches cuts that part of the bill by roughly eight.

#### No duplicate applications

Every listing you applied to is remembered permanently by its listing ID. Re-running, rescheduling or widening your search never produces a second application to the same landlord. Use a separate **Queue namespace** per search so parallel schedules do not consume each other's queue.

#### Run dashboard

Each run writes a filterable HTML dashboard: how many listings were found, queued, filtered and applied to, with the reason for every rejection. Useful when you wonder why a listing you saw on the portal never got an application.

### Limitations

- The **postal code area filter currently supports Berlin only**. Everywhere else, use the portal's own location filter in your search URL; the rest of the Actor works normally.
- The Kleinanzeigen scraper processes **one search URL per call**, so this Actor runs it once per URL. Prefer one broad search plus the area filter.
- Kleinanzeigen requires a **one-time login** through the message Actor before anything can be sent.
- In monitor mode the **first run only records a baseline** and returns nothing. That is expected.

### FAQ

**Do I need my own account?**
Yes. Applications are sent from your own ImmoScout24 or Kleinanzeigen account, not from a shared one. Credentials are stored encrypted by Apify.

**Why do I need those other Actors?**
This Actor does the deciding: monitoring, filtering, deduplication, personalization and reporting. The scraping and the actual sending are done by specialized Actors that maintain their own logins and anti-bot handling. You can substitute your own via the override fields.

**Can I use it outside Berlin?**
Yes. Only the postal code area filter is Berlin-specific. Set your area in the portal search URL instead.

**Will landlords notice it is automated?**
The text is generated from your own template and references concrete details of each listing. Write it as you would write it yourself, keep the pacing delay enabled, and keep the volume reasonable.

**How do I stop it applying to the wrong listings?**
Run with **Send applications** off first. You get the full list of what would have been sent, with reasons, and can tune your filters before anything goes out.

### Legal

This Actor acts through your own account on portals you already have a contract with. You are responsible for complying with the terms of service of ImmoScout24 and Kleinanzeigen, and with applicable data protection law including GDPR. Listing texts and landlord names are personal data - handle them accordingly. The author provides no warranty and accepts no liability for how the Actor is used.

***

### Operating notes

The sections below are reference material for day-to-day operation.

#### Queue namespaces

The queue is scoped by `kvNamespace`. Use a distinct value per search, for example `is_berlin` and `ka_berlin`. Changing it starts with an empty queue, which is the simplest way to discard a backlog of stale listings.

If left empty, a scope is derived from the search profile ID or a hash of the search URLs.

#### Cost control

Proxy traffic and the underlying Actors dominate the bill.

- Replace per-postal-code search URLs with one broad city search plus the area filter
- Keep monitor mode on so only new listings are fetched in detail
- Lower the schedule frequency; the queue still drains at your per-run application limit
- Keep photo quality on hybrid, or web resolution if you do not need full-size images
- Leave the dashboard retention at its defaults; the log is rewritten in full on every flush

#### Run dashboard settings

`opsLogEnabled` (default on), `opsLogMaxEvents` (1000), `opsLogMaxRuns` (500), `opsLogMaxFilteredListings` (2000), `opsLogMaxApplications` (2000).

The dashboard link is printed at the start of every run. Higher retention means larger key-value writes on every flush, so raise these only if you need the depth.

#### Troubleshooting

| Symptom | Cause |
|---------|-------|
| First run returns nothing | Monitor mode is recording its baseline. Expected. |
| Everything filtered by location | Raise `locationRadiusKm` or add postal codes. Check the `Location filter results` line. |
| High `unresolved` count | Listings carry no location data. Leave `skipListingsWithUnknownLocation` off so they are kept. |
| Kleinanzeigen send fails | The account is not logged in. Run the message Actor with `operation: login`. |
| Duplicate applications | Two schedules share a queue namespace. Give each its own. |
| Search URLs rejected | The URLs do not match the selected platform. |

#### Development

```bash
npm install
npm test
npm run build:zipdata   # regenerate the Berlin postal code table (~4 min, uses Nominatim)
```

`src/lib/berlin-zip-data.js` holds 190 Berlin postal codes with centroid, district and borough, generated from OpenStreetMap data (ODbL) and committed so runs need no network access.

# Actor input Schema

## `platform` (type: `string`):

Which portal this run searches. Run one scheduled task per platform, each with its own queue namespace below.

## `startUrls` (type: `array`):

Search result URLs from the portal selected above. Set your filters on the website, then copy the URL from the address bar. For Kleinanzeigen prefer one broad city search plus the postal code filter below, because each URL is scraped separately and costs extra proxy traffic.

## `monitorMode` (type: `boolean`):

Recommended. Returns only listings that appeared since the previous run, which keeps scheduled runs cheap. The very first run just records a baseline and returns nothing.

## `pageLimit` (type: `integer`):

How many result pages the ImmoScout24 scraper reads. 0 means no explicit limit. Ignored for Kleinanzeigen.

## `kaMaxResults` (type: `integer`):

Caps how many listings the Kleinanzeigen scraper returns per search. 0 means unlimited. Ignored for ImmoScout24.

## `kvNamespace` (type: `string`):

Keeps the listing queue of this task separate from your other tasks. Use a unique value per search, for example is\_berlin or ka\_berlin. Change it to start over with an empty queue.

## `searchProfileId` (type: `string`):

Alternative way to scope the queue namespace when the field above is left empty.

## `scraperActorId` (type: `string`):

Advanced. Replaces the default scraper clearpath/immoscout24-api-pro. The replacement must return the same fields.

## `applyActorId` (type: `string`):

Advanced. Replaces the default apply Actor clearpath/immoscout24-apply-bot.

## `kaScraperActorId` (type: `string`):

Advanced. Replaces the default scraper clearpath/kleinanzeigen-immobilien-api-pro.

## `kaMessageActorId` (type: `string`):

Advanced. Replaces the default message Actor clearpath/kleinanzeigen-message-api.

## `username` (type: `string`):

The email address of your own ImmoScout24 account. Required when searching ImmoScout24 with applications enabled.

## `password` (type: `string`):

Only needed when the apply Actor has no stored session yet. Stored encrypted.

## `useStoredCredentials` (type: `boolean`):

Recommended. Lets the apply Actor reuse the session it already stored instead of logging in on every run.

## `kaEmail` (type: `string`):

The email address of your own Kleinanzeigen account. Required when searching Kleinanzeigen with messaging enabled. Log the account in once beforehand, see the README.

## `kaPassword` (type: `string`):

Only needed for the one-time login of the Kleinanzeigen message Actor. Normal runs do not use it. Stored encrypted.

## `applicationTemplate` (type: `string`):

The message sent to the landlord. Placeholders are filled per listing: {{anrede}} salutation, {{roomsStr}} rooms, {{sizeStr}} size in m2, {{altbauSuffix}} adds ' Altbau' for pre-1950 buildings, {{locationSuffix}} district and street, {{highlightsSuffix}} condition and balcony sentence, {{vacantPhrase}} move-in date sentence, {{abloeseSentence}} added when the listing mentions a handover fee, {{aiParagraph}} optional AI sentence. Wrap a sentence in (pictures)...(/pictures) to include it only when the listing has photos. Aliases: (photos), (fotos), (bilder).

## `openaiEnabled` (type: `boolean`):

If enabled, OpenAI can generate a short personalized paragraph based on listing text (description, furnishing\_description, location\_description). Note: AI generation runs only when your applicationTemplate contains {{aiParagraph}}.

## `openaiApiKey` (type: `string`):

OpenAI API key used for AI personalization. Stored as a secret in Apify.

## `openaiModel` (type: `string`):

Model to use for AI personalization (e.g. gpt-4o-mini).

## `openaiTemperature` (type: `number`):

Higher = more creative, lower = more deterministic.

## `openaiMaxTokens` (type: `integer`):

Token cap for the AI response.

## `openaiMaxChars` (type: `integer`):

Per-field character cap before sending listing text to AI (applies to description, furnishing\_description, and location\_description).

## `openaiMinListingChars` (type: `integer`):

Only call AI if combined listing text length is at least this many characters.

## `openaiMaxListingChars` (type: `integer`):

Combined character cap for listing text sent to AI after per-field truncation.

## `openaiDebug` (type: `boolean`):

If enabled, logs the AI prompt/response summary to the console.

## `maxAppliesPerRun` (type: `integer`):

Upper limit of applications sent in a single run. Each sent application is billed, so keep this low while testing. Remaining listings stay queued for the next run.

## `excludedListingKeywords` (type: `array`):

Listings whose text contains any of these words are skipped. Matching ignores case and simple plural forms, and covers title, description, furnishing, location and contact fields.

## `excludedListingKeywordsText` (type: `string`):

Use this if the list field above does not save your entries. One keyword per line, or separated by commas or semicolons.

## `debugListingKeywordFilter` (type: `boolean`):

Logs why each listing matched or missed a keyword. Useful when a filter behaves unexpectedly.

## `maxColdPricePerSqm` (type: `number`):

Skips listings above this cold rent per square metre. Cold rent excludes ancillary and heating costs.

## `maxWarmPricePerSqm` (type: `number`):

Skips listings above this total rent per square metre, including ancillary and heating costs.

## `skipExposesByPricePerSqm` (type: `boolean`):

Turn off to ignore both rent per m2 limits above without clearing them.

## `allowedZipCodes` (type: `array`):

List the Berlin postal codes you want to live near. Neighbouring postal codes, map coordinates and district names are derived automatically using the radius below, so one broad city search can replace one search URL per postal code. Leave empty to keep every listing the search returns. Currently only Berlin postal codes are supported.

## `allowedZipCodesText` (type: `string`):

Use this if the list field above does not save your entries. One postal code per line, or separated by commas, semicolons or spaces.

## `locationRadiusKm` (type: `number`):

How far from each postal code centre a listing may be. Also decides which neighbouring postal codes count as in range. 1.5 works well because the portals report approximate coordinates.

## `allowedDistricts` (type: `array`):

Only used for listings that have neither coordinates nor a postal code. Leave empty to use the districts derived from your postal codes, which is recommended.

## `allowedDistrictsText` (type: `string`):

Use this if the list field above does not save your entries. One district per line, or separated by commas or semicolons.

## `skipListingsWithUnknownLocation` (type: `boolean`):

Off by default so nothing is lost. Listings with no coordinates, postal code or district are kept and counted as unresolved in the run summary. Only turn this on once you have confirmed that count stays near zero.

## `debugLocationFilter` (type: `boolean`):

Logs which signal decided each listing and how far it was from your postal codes. Useful for a first dry run.

## `delaySecondsBetweenApplies` (type: `integer`):

Pause between two applications in the same run, to look less automated to the portal.

## `runApply` (type: `boolean`):

Off by default. While off, the Actor only collects and filters listings so you can verify the results first. Turn it on to actually send applications from your account.

## `sendEmail` (type: `boolean`):

Emails you a summary with photos and the sent text after each successful application.

## `emailTo` (type: `string`):

Where notifications are sent. Use an inbox you actually monitor.

## `mailTimeoutMs` (type: `integer`):

How long to wait for the email Actor before giving up on a notification.

## `storeSnapshots` (type: `boolean`):

Saves each listing you applied to as a self-contained HTML page plus the raw data, so you still have it after the ad is taken down.

## `storeMedia` (type: `boolean`):

Downloads listing photos and PDF documents into the snapshot. Turn off to reduce storage and transfer costs.

## `maxPhotosToStore` (type: `integer`):

How many photos to save per listing. Documents are not limited by this.

## `photoResolutionMode` (type: `string`):

Full resolution looks best but costs the most transfer. Hybrid saves the first few photos at full resolution and the rest at web resolution.

## `fullResPhotoCount` (type: `integer`):

How many of the first photos are saved at full resolution when photo quality is set to hybrid.

## `maxBytesTotal` (type: `integer`):

Media downloads for a listing stop once this total is exceeded.

## `perFileMaxBytes` (type: `integer`):

Individual photos or documents larger than this are skipped.

## `downloadTimeoutMs` (type: `integer`):

How long to wait for a single photo or document before skipping it.

## `opsLogEnabled` (type: `boolean`):

If enabled, writes a cross-run JSON log and filterable HTML dashboard to the persistent STATE key-value store.

## `opsLogMaxEvents` (type: `integer`):

Maximum number of recent detail events retained in the persistent ops JSON log and dashboard. Run history is controlled separately.

## `opsLogMaxRuns` (type: `integer`):

Maximum number of compact run summaries retained in the persistent ops JSON log and dashboard. The whole log is rewritten on every flush, so higher values increase key-value writes and data transfer costs.

## `opsLogMaxFilteredListings` (type: `integer`):

Maximum number of compact filtered-listing records retained in the persistent ops JSON log and dashboard. Higher values increase key-value writes and data transfer costs.

## `opsLogMaxApplications` (type: `integer`):

Maximum number of compact application attempt/result records retained in the persistent ops JSON log and dashboard. Higher values increase key-value writes and data transfer costs.

## `enqueueLimitPerRun` (type: `integer`):

Caps how many new listings enter the queue in one run. Leave empty for no limit.

## `debugEmailOnly` (type: `boolean`):

Renders the application text for a few listings into the log and sends nothing. Useful for checking your template.

## `debugQueueCount` (type: `integer`):

Number of application texts rendered when the preview option above is enabled.

## `debugPickIndex` (type: `integer`):

Which listing from the search results the preview starts at.

## `debugNoQueueWrites` (type: `boolean`):

Runs without touching the stored queue, so you can try a new search without affecting a running task.

## `debugApplicationMessage` (type: `string`):

Used in preview mode when no application text is set.

## `proxyConfiguration` (type: `object`):

Proxy used by the ImmoScout24 scraper. Apify Proxy is recommended. The Kleinanzeigen scraper manages its own proxy.

## `useApifyProxy` (type: `boolean`):

Routes the ImmoScout24 apply Actor through Apify Proxy.

## Actor input object example

```json
{
  "platform": "immoscout",
  "startUrls": [
    "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"
  ],
  "monitorMode": true,
  "pageLimit": 0,
  "kaMaxResults": 0,
  "kvNamespace": "is_berlin",
  "useStoredCredentials": true,
  "applicationTemplate": "{{anrede}}\n\nIhre {{roomsStr}}-Zimmer{{altbauSuffix}} Wohnung{{locationSuffix}} hat mich sofort angesprochen.{{highlightsSuffix}} {{vacantPhrase}}{{abloeseSentence}}\n\nZu mir: Ich bin berufstätig, Nichtraucher, habe keine Haustiere und suche ein langfristiges Zuhause. Eine vollständige Bewerbungsmappe mit Gehaltsnachweisen und SCHUFA sende ich Ihnen gerne sofort zu.\n\nÜber eine Einladung zur Besichtigung würde ich mich sehr freuen.\n\nViele Grüße",
  "openaiEnabled": false,
  "openaiModel": "gpt-4o-mini",
  "openaiTemperature": 0.3,
  "openaiMaxTokens": 160,
  "openaiMaxChars": 1800,
  "openaiMinListingChars": 80,
  "openaiMaxListingChars": 3000,
  "openaiDebug": false,
  "maxAppliesPerRun": 1,
  "excludedListingKeywords": [
    "möbliert",
    "furnished",
    "befristet",
    "Untermiete",
    "WBS",
    "Tauschwohnung"
  ],
  "debugListingKeywordFilter": false,
  "skipExposesByPricePerSqm": true,
  "allowedZipCodes": [
    "10119",
    "10179",
    "10249",
    "10405",
    "10997"
  ],
  "locationRadiusKm": 1.5,
  "allowedDistricts": [],
  "skipListingsWithUnknownLocation": false,
  "debugLocationFilter": false,
  "delaySecondsBetweenApplies": 30,
  "runApply": false,
  "sendEmail": true,
  "emailTo": "you@example.com",
  "mailTimeoutMs": 60000,
  "storeSnapshots": true,
  "storeMedia": true,
  "maxPhotosToStore": 10,
  "photoResolutionMode": "hybrid",
  "fullResPhotoCount": 2,
  "maxBytesTotal": 30000000,
  "perFileMaxBytes": 8000000,
  "downloadTimeoutMs": 15000,
  "opsLogEnabled": true,
  "opsLogMaxEvents": 1000,
  "opsLogMaxRuns": 500,
  "opsLogMaxFilteredListings": 2000,
  "opsLogMaxApplications": 2000,
  "debugEmailOnly": false,
  "debugQueueCount": 1,
  "debugPickIndex": 0,
  "debugNoQueueWrites": false,
  "debugApplicationMessage": "(preview mode - no application was sent)",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "useApifyProxy": true
}
```

# Actor output Schema

## `listings` (type: `string`):

All listings processed in this run, shown with the overview dataset view.

## `applications` (type: `string`):

Listings this run applied to or messaged.

## `filtered` (type: `string`):

Listings skipped by filters, with reasons.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"
    ],
    "kvNamespace": "is_berlin",
    "applicationTemplate": `{{anrede}}

Ihre {{roomsStr}}-Zimmer{{altbauSuffix}} Wohnung{{locationSuffix}} hat mich sofort angesprochen.{{highlightsSuffix}} {{vacantPhrase}}{{abloeseSentence}}

Zu mir: Ich bin berufstätig, Nichtraucher, habe keine Haustiere und suche ein langfristiges Zuhause. Eine vollständige Bewerbungsmappe mit Gehaltsnachweisen und SCHUFA sende ich Ihnen gerne sofort zu.

Über eine Einladung zur Besichtigung würde ich mich sehr freuen.

Viele Grüße`,
    "excludedListingKeywords": [
        "möbliert",
        "furnished",
        "befristet",
        "Untermiete",
        "WBS",
        "Tauschwohnung"
    ],
    "allowedZipCodes": [
        "10119",
        "10179",
        "10249",
        "10405",
        "10997"
    ],
    "emailTo": "you@example.com"
};

// Run the Actor and wait for it to finish
const run = await client.actor("beige_quaff/immoscout24-kleinanzeigen-apply-bot").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": ["https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"],
    "kvNamespace": "is_berlin",
    "applicationTemplate": """{{anrede}}

Ihre {{roomsStr}}-Zimmer{{altbauSuffix}} Wohnung{{locationSuffix}} hat mich sofort angesprochen.{{highlightsSuffix}} {{vacantPhrase}}{{abloeseSentence}}

Zu mir: Ich bin berufstätig, Nichtraucher, habe keine Haustiere und suche ein langfristiges Zuhause. Eine vollständige Bewerbungsmappe mit Gehaltsnachweisen und SCHUFA sende ich Ihnen gerne sofort zu.

Über eine Einladung zur Besichtigung würde ich mich sehr freuen.

Viele Grüße""",
    "excludedListingKeywords": [
        "möbliert",
        "furnished",
        "befristet",
        "Untermiete",
        "WBS",
        "Tauschwohnung",
    ],
    "allowedZipCodes": [
        "10119",
        "10179",
        "10249",
        "10405",
        "10997",
    ],
    "emailTo": "you@example.com",
}

# Run the Actor and wait for it to finish
run = client.actor("beige_quaff/immoscout24-kleinanzeigen-apply-bot").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://www.immobilienscout24.de/Suche/de/berlin/berlin/wohnung-mieten"
  ],
  "kvNamespace": "is_berlin",
  "applicationTemplate": "{{anrede}}\\n\\nIhre {{roomsStr}}-Zimmer{{altbauSuffix}} Wohnung{{locationSuffix}} hat mich sofort angesprochen.{{highlightsSuffix}} {{vacantPhrase}}{{abloeseSentence}}\\n\\nZu mir: Ich bin berufstätig, Nichtraucher, habe keine Haustiere und suche ein langfristiges Zuhause. Eine vollständige Bewerbungsmappe mit Gehaltsnachweisen und SCHUFA sende ich Ihnen gerne sofort zu.\\n\\nÜber eine Einladung zur Besichtigung würde ich mich sehr freuen.\\n\\nViele Grüße",
  "excludedListingKeywords": [
    "möbliert",
    "furnished",
    "befristet",
    "Untermiete",
    "WBS",
    "Tauschwohnung"
  ],
  "allowedZipCodes": [
    "10119",
    "10179",
    "10249",
    "10405",
    "10997"
  ],
  "emailTo": "you@example.com"
}' |
apify call beige_quaff/immoscout24-kleinanzeigen-apply-bot --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,beige_quaff/immoscout24-kleinanzeigen-apply-bot"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ygQQrIrsFbr4787ba/builds/ZaCKV98lL0RVxwuBH/openapi.json
