In this guide you build an automation that actually helps you: a job radar.
Every morning, it checks the Arbeitsagentur job board for new postings. AI reads each one and rates how well it fits you. You only get an email with the good matches.
[Manual trigger]βββ
ββ> Search settings -> Fetch jobs -> Split jobs -> Clean up jobs
[Every morning]ββββ -> Remember seen jobs -> Limit -> Score with AI (Gemini)
-> Job + score -> Good matches only -> Build email -> Send email
You build it with n8n, an automation tool like Make, with two big differences:
-
It runs on your own laptop, so your data stays with you.
-
It's free when you host it yourself. No credits, no monthly limit.
-
Goal: what you're building in this part.
-
Checkpoint: what you should see before you move on.
-
Stuck? Quick fixes for the usual problems.
[drag X] means: drag the field X from the left panel into this spot. Replace the whole [drag X], brackets included.
If you know Make, most of this will feel familiar. Only the words change:
| Make | n8n | What it means |
|---|---|---|
| Scenario | Workflow | The whole automation |
| Module | Node | One step |
| Bundle | Item | One piece of data, like one job posting |
| Operation | Execution | One run of the workflow |
| Connection | Credential | Your saved login or API key for a service |
| Mapping | Expression | Pulling data from earlier steps, written as {{ }} |
You won't write code yourself. At one point, AI writes a small piece of code for you, and this guide shows you how to ask for it.
Please have these ready:
- Docker Desktop installed and running.
- n8n downloaded. This saves the classroom Wi-Fi. In a terminal, run:
docker pull n8nio/n8n
- A Gemini API key: go to**https://aistudio.google.com/apikey** and clickCreate API key . Copy it somewhere safe.
- A Gmail app password: see the step-by-step section below.
- Your profile in 3β4 sentences: your background, what kind of job you want, languages, preferences. There's an example in Part 2.
n8n sends the email through your Gmail account. Gmail won't accept your normal password from an app like n8n, so you create a special 16-character "app password" just for n8n.
Use a personal Gmail account. Work and school accounts usually don't allow app passwords.
App passwords only exist once 2-Step Verification is on. Skip to part B if yours already is.
-
Check the profile picture at the top right: is it your personal Gmail? If not, click it and switch accounts.
-
Scroll to How you sign in to Google and click2-Step Verification .
-
Click Turn on 2-Step Verification and follow the steps. Usually you confirm your phone number and enter a code sent by SMS.
-
Back on the Security page, 2-Step Verification should now sayOn .
-
Open https://myaccount.google.com/apppasswords . Type the full address, including
https://.
- Google doesn't show a button for this on the Security page. If the link doesn't work, go to https://myaccount.google.com , click the search bar at the top, type
App passwords, and click the result.
- Google doesn't show a button for this on the Security page. If the link doesn't work, go to
- Sign in again if Google asks.
- In App name , type
n8nand clickCreate . - A box shows your app password: 16 letters in four groups, like
abcd efgh ijkl mnop. - Copy it now and save it somewhere safe. Google shows it only once. If you lose it, delete it and create a new one.
- Click Done .
That's all for now. You'll paste the app password into n8n in Part 8: Send the email, which walks you through every click. Until then, keep it somewhere easy to copy from, like a note on your laptop.
Work through these in order:
- 2-Step Verification is off. Do part A, then reload the page.
- Wrong account. If you're signed in to several Google accounts, the link opens your default one. Check the profile picture at the top right and switch accounts, or open the link in a private/incognito window and sign in with your personal Gmail only.
- Work or school account. Your organization has blocked app passwords. Use a personal Gmail.
- 2-Step Verification uses only a security key, or Advanced Protection is on. Add your phone number as a second step in part A, or use a different Gmail account.
Good to know: if you ever change your Google password, all app passwords stop working. Then just create a new one and update it in n8n.
Goal: n8n running on your laptop.
n8n runs inside Docker. Think of Docker as a box that has everything n8n needs, so you don't have to install anything else.
- Make sure Docker Desktop is running.
- Open a terminal (Mac: Terminal, Windows: PowerShell) and run this command. It creates a storage space, so your workflows survive when n8n stops:
docker volume create n8n_data
- Then run this command to start n8n. The timezone part makes sure "8 a.m." means 8 a.m. in Germany:
docker run -it --rm --name n8n -p 5678:5678 -e GENERIC_TIMEZONE="Europe/Berlin" -e TZ="Europe/Berlin" -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true -v n8n_data:/home/node/.n8n n8nio/n8n
- Wait until the terminal shows the editor URL (
http://localhost:5678). The first start can take a minute. - Open http://localhost:5678 in your browser.
- Create your owner account (email, name, password). It's only stored on your laptop.
- Skip the survey and the license offer.
Checkpoint: you see the n8n start screen.
Keep this terminal open. If you close it, n8n stops. To start n8n again later, run the command from step 3 again.
Stuck?
- "Cannot connect to the Docker daemon": Docker Desktop isn't running. Start it and wait until it says it's running.
- "Port is already allocated": n8n is already running somewhere. Check Docker Desktop >Containers .
- "Container name already in use": run
docker rm n8n, then the command from step 3 again.
Goal: all your settings in one place: job title, city, and your profile. That way, you can change the search later without touching the rest of the workflow.
A habit for this whole guide: name every node. It makes the workflow readable, and it helps later when you reference earlier steps.
- Click Create workflow . Name it
Job radar(top left). - Click Add first step >Trigger manually .
- Click the + on the right of the trigger, searchEdit Fields (Set) , and add it.
- Rename it
Search settings: click the title at the top of the panel. - Click Add Field five times. For each one, type the name, choose the type, and enter the value:
| Name | Type | Value |
|---|---|---|
jobTitle |
String | e.g. Marketing |
city |
String | e.g. Hannover |
radiusKm |
Number | 25 |
daysBack |
Number | 7 |
profile |
String | Your 3β4 sentence profile (example below) |
Example profile:
Former retail team lead with 5 years of experience in customer service and managing small teams. Currently finishing a course in AI tools and automation. Looking for junior roles in marketing, customer success or operations. Fluent in German and English, prefers hybrid or remote work.
- Click Execute step .
daysBack is 7 for now, so you have enough jobs to test with. In Part 10, you switch it to 1, because the workflow will run daily.
Checkpoint: the output on the right shows one item with your five fields.
Goal: a list of real job postings from the Arbeitsagentur.
The Arbeitsagentur has a free API: a door for programs instead of humans. The HTTP Request node is how n8n talks to any API, even ones that don't have their own node.
- Add an HTTP Request node after
Search settings. Rename itFetch jobs. - Method: GET
- URL:
https://rest.arbeitsagentur.de/jobboerse/jobsuche-service/pc/v6/jobs
- Turn on Send Query Parameters and clickAdd Parameter six times:
| Name | Value |
|---|---|
was |
drag jobTitle from the left panel |
wo |
drag city |
umkreis |
drag radiusKm |
veroeffentlichtseit |
drag daysBack |
angebotsart |
1 |
size |
25 |
- Turn on Send Headers and add one header:
- Name:
X-API-Key - Value:
jobboerse-jobsuche
- Click Execute step .
What's going on here:
- The parameter names are German because it's a German API.
wasis "what",wois "where",umkreisis the radius. angebotsart1 means regular jobs, not apprenticeships.- When you drag a field in, it turns into an expression, the part with curly braces. That's n8n saying: take this value from an earlier step.
- The header works like a visitor badge. This one is public, it's the same for everyone.
Checkpoint: one item that contains ergebnisliste, a list of job postings.
Stuck?
- No
ergebnislistein the output: no jobs were found. Try a bigger radius, more days, or a broader job title. - Error 401 or 403: check the URL, and the header name and value for typos.
Goal: one item per job, with only the fields you need.
Right now, all 25 jobs are packed into one item. But the AI should look at each job separately. So you split the list: one job becomes one item, and every node after this runs once per job.
- Add a Split Out node. Rename it
Split jobs. - Fields To Split Out:
ergebnisliste(type it or drag it in) - Click Execute step .
Checkpoint: the output now shows many items, one per job.
Each job has a lot of fields you don't need. Now keep only the useful ones and give them simple names.
- Add an Edit Fields (Set) node. Rename it
Clean up jobs. - For each row: click Add Field , type the name, choose the type from the table, then drag the value from the left panel.
| Name | Type | Value |
|---|---|---|
title |
String | drag stellenangebotsTitel |
occupation |
String | drag hauptberuf |
employer |
String | drag firma |
city |
String | open stellenlokationen >adresse , dragort |
jobId |
String | drag referenznummer |
link |
String | paste the link expression (see below) |
careerChangerFriendly |
Boolean | drag quereinstiegGeeignet |
remotePossible |
Boolean | drag homeofficemoeglich |
Link expression to paste into the link value:
{{ $json.externeURL || 'https://www.arbeitsagentur.de/jobsuche/jobdetail/' + $json.referenznummer }}
- Click Execute step .
What's going on here:
referenznummeris each job's unique ID. You call itjobId. You'll need it later, so the workflow remembers which jobs it already sent.- Many jobs on the Arbeitsagentur come from partner job boards. Those only exist on the partner's site, so the API gives you an
externeURLfor them. The link expression says: if there's an external link, use it. If not, use the Arbeitsagentur page. - A job can have several locations. For the city, you simply take the first one.
- Two fields are especially useful for you:
quereinstiegGeeignetmeans the job is suitable for career changers, andhomeofficemoeglichmeans remote work is possible. The AI will use both for the score.
Checkpoint: every item has exactly these eight fields. Open one link in the browser: it should show the job posting.
Stuck?
- No
stellenangebotsTitelfield: usehauptberuffor the title instead. - Link shows "Dieses Stellenangebot gibt es nicht oder nicht mehr": check that the
linkvalue is exactly the expression above. If it is, the posting was taken down in the meantime. Try another one.
Goal: AI reads each job, compares it to your profile, and gives a score from 1 to 10 with a short reason.
The AI makes a judgment call here, which a normal rule couldn't do. That's what makes this an AI automation.
First, a limit, so testing doesn't use up your free Gemini quota:
- Add a Limit node. Rename it
Limit. - Max Items:
5 - Click Execute step .
Now the AI node:
- Add an Information Extractor node. Rename it
Score with AI. - Click into Text . If there's already something in it, delete it.
- Type the labels below and drag in the matching fields. For
profile, scroll down in the left panel toSearch settings, where every earlier node is listed.
Candidate profile:
[drag profile from Search settings]
Job posting:
Title: [drag title]
Occupation: [drag occupation]
Employer: [drag employer]
Location: [drag city]
Suitable for career changers: [drag careerChangerFriendly]
Remote work possible: [drag remotePossible]
- Schema Type: From Attribute Descriptions
- Add two attributes:
| Name | Type | Required | Description |
|---|---|---|---|
score |
Number | Yes | see below |
reason |
String | Yes | One short sentence explaining the score. |
Description for score:
How well this job fits the candidate profile, from 1 (no fit) to 10 (perfect fit). Be strict: give 7 or more only if the job clearly matches the candidate's goals and background. If the candidate is changing careers, rate jobs suitable for career changers higher. If the candidate prefers remote or hybrid work, take into account whether remote work is possible.
- Close the node panel. On the canvas, below
Score with AI, click the**+** labeledModel (orChat Model ) and chooseGoogle Gemini Chat Model . - Credential:**Create new credential . Paste your Gemini API key and leaveHost** as it is. Save.
- Model: pick a Flash model.
- Close the panel, open
Score with AIagain, and clickExecute step .
What's going on here:
- The Information Extractor lets you describe what you want back, and gives you clean fields instead of a wall of text.
- The descriptions are your prompt. The clearer they are, the better the scores.
- The API only gives you the title, occupation, employer and the two flags, not the full job ad. So the AI is pre-screening. You still decide.
- Your profile goes to Google here, so don't put anything private in it.
Checkpoint: five items, each with an output containing score and reason. Read a few: do they make sense?
Stuck?
- Error about
Search settingsthat mentions "paired item": in the Text field, change.itemto.first()in the profile expression. That's safe here, because there's only oneSearch settingsitem. - Error 429 / quota: wait a minute and run again.
- Scores feel random: sharpen the score description or your profile.
Goal: each job together with its score, and only the good matches.
The AI node only outputs the score and the reason. The job title and link are gone. That's no problem: n8n remembers which item came from where, so you can reach back to earlier steps and put everything together.
- Add an Edit Fields (Set) node. Rename it
Job + score. - Add six fields with Add Field , each with the same name as the field you drag in:
- From
Clean up jobs(scroll down in the left panel):title,employer,city,link, all type String. - From
Score with AI(insideoutput):scorewith typeNumber , andreasonwith type String.
- From
- Click Execute step .
Checkpoint: five items with all six fields, and each job has its own title and score.
Stuck?
- "Paired item" error here: run the whole workflow once withExecute workflow (bottom of the canvas), then try again. Don't use
.first()here, or every job would get the first job's title.
Now the filter. This one is a plain rule, no AI: only jobs with a score of 7 or higher get through.
- Add a Filter node. Rename it
Good matches only. - Condition: drag
score> chooseNumber >is greater than or equal to >7 - Click Execute step .
Checkpoint: the output only has jobs with a score of 7 or more.
If nothing gets through, the workflow is working, there just wasn't a good match. For testing, lower the number to 5 so you have something to email.
Goal: one nicely formatted email with all matches, not one email per job.
n8n doesn't have a ready-made node for exactly this layout, so you use a Code node. You don't need to know how to code. You need to know how to ask.
A good code prompt has four parts: where the code runs, what goes in, what should come out, and how it should look.
Open ChatGPT, Claude or Gemini and paste this prompt:
I'm using n8n, the automation tool. Write JavaScript for a Code node
set to "Run Once for All Items".
Input: several items. Each item has these fields: title, employer,
city, link, score (a number from 1 to 10), reason.
Output: exactly ONE item with two fields:
- html: an HTML email listing all jobs, highest score first. For each
job show the score, the title as a clickable link, the employer and
city, and the reason in italics.
- count: how many jobs there are.
Keep the code short and simple, with comments explaining each part.
Notice that the prompt names the exact fields. The AI can't see your workflow, so you have to describe it.
- Add a Code node after
Good matches only. Rename itBuild email. - Mode: Run Once for All Items.Language: JavaScript.
- Delete the example code and paste in the AI's code.
- Click Execute step .
Checkpoint: one single item with html and count.
If no job passes the filter, n8n simply stops before this node, so you never get an empty email.
Stuck?
- The code throws an error: copy the error message back into the AI chat and ask it to fix the code. That's normal, even for developers.
- It still doesn't work: use this reference version:
// Get all jobs that passed the filter
const jobs = $input.all().map(item => item.json);
// Best matches first
jobs.sort((a, b) => b.score - a.score);
// One list entry per job
const listItems = jobs.map(job => `
<li style="margin-bottom:16px">
<strong>${job.score}/10: <a href="${job.link}">${job.title}</a></strong><br>
${job.employer}, ${job.city}<br>
<em>${job.reason}</em>
</li>`).join('');
// Put the whole email together
const html = `
<h2>Job radar: ${jobs.length} new ${jobs.length === 1 ? 'match' : 'matches'}</h2>
<ul style="padding-left:18px">${listItems}</ul>
<p style="color:#888">Sent by your n8n job radar</p>`;
// Return ONE item for the email node
return [{ json: { html, count: jobs.length } }];
Goal: the job radar email in your inbox.
Have your Gmail app password ready (see "Gmail app password, step by step").
Add the node
- On the canvas, click the + on the right side of
Build email. - In the search box, type
Send Emailand clickSend Email . - If n8n asks which action you want, choose Send email . The node's settings panel opens.
- Click the node title at the top of the panel and rename it
Send email.
Connect your Gmail (this is where the app password goes)
- At the top of the panel, find Credential to connect with . Click the dropdown and choose**+ Create new credential** .
- A new window opens, titled something like SMTP account . Fill it in:
| Field | What to enter |
|---|---|
| User | your full Gmail address, e.g. alex.example@gmail.com |
| Password | paste the app password here , exactly as Google shows it, including the spaces |
| Host | smtp.gmail.com |
| Port | 465 |
| SSL/TLS | switch on |
Leave everything else (Disable STARTTLS, Client Host Name) as it is. 7. Click Save at the top right of the window. n8n tests the connection right away. 8. Look for a green Connection tested successfully. Red error? Check the "Stuck?" tips below. 9. Close the credential window with the X. Back in the node, the dropdown now shows your new credential.
Fill in the email
- From Email: your Gmail address
- To Email: your Gmail address (or any inbox you check)
- Subject: type
Job radar:, then dragcountfrom the left panel after it, then typenew matches - Email Format: HTML
- HTML: drag
htmlfrom the left panel - Click Add option >Append n8n Attribution , and switch it off.
- Click Execute step .
Checkpoint: the email arrives within a minute. Check your spam folder if it doesn't.
Stuck?
- "Invalid login" / "Username and Password not accepted": it has to be the app password, not your normal Gmail password. Paste it exactly as Google shows it, including the spaces, and check thatUser is the same Gmail account you created the app password in.
- Can't create an app password: see "If it says 'The setting you are looking for is not available for your account'" in the Gmail section.
Goal: the workflow only lets through jobs it hasn't seen before.
Right now, if this runs every day, you'd get the same jobs again and again. So you give the workflow a memory: it notes every job ID it has seen and only lets new ones through.
- Hover over the line between
Clean up jobsandLimit, click**+** , and searchRemove Duplicates . n8n shows three options. ChooseRemove items processed in previous executions . Rename the nodeRemember seen jobs. - Check that Operation showsRemove Items Processed in Previous Executions .
- Keep Items Where: Value Is New
- Value to Dedupe On: drag
jobId - Click Execute workflow (bottom of the canvas) twice. Wait for the first run to finish before you start the second.
Checkpoint: on the second run, Remember seen jobs outputs 0 items, nothing after it runs, and no email is sent. That's exactly what you want: from now on, only new jobs get through.
Good to know: Remember seen jobs comes before Limit, so jobs cut off by the limit are also marked as seen and never scored. If you regularly get more new jobs per day than your limit, make your search more specific or raise the limit.
Stuck?
- You want to test again with the same jobs: select
Remember seen jobsand pressD to deactivate it. PressD again before publishing.
Goal: the workflow runs by itself every morning.
You add a second trigger, a schedule. The manual trigger stays, so you can still test by hand.
- Click the + at the top right of the canvas, searchSchedule Trigger , and add it. Rename it
Every morning. - Trigger Interval: Days.Days Between Triggers:
1.** Trigger at Hour:** 8am.Trigger at Minute:0. - Drag a line from the output dot of
Every morningto the input ofSearch settings. - Switch to daily mode:
- In
Search settings, changedaysBackto1. - In
Limit, changeMax Items to10. - In
Good matches only, set the number back to7if you lowered it. - Pressed D on
Remember seen jobsearlier? Press it again to switch it back on.
- In
- Click Publish (top right).
Checkpoint: the workflow shows as published.
Good to know:
- Published means the workflow now runs on its own.
- If you change anything later, click Publish again. Otherwise the schedule keeps using the old version.
- Running locally has one catch: your laptop, Docker and the n8n terminal have to be on at 8 a.m. That's the price of free and private.
- To check if it ran, open the Executions tab of your workflow.
- To it, open the three-dot menu at the top right and unpublish.
| Problem | Fix |
|---|---|
| localhost:5678 doesn't load | Is the terminal with n8n still open? Is Docker Desktop running? |
| Workflow gone after restart | The start command needs the -v n8n_data:... part. Run exactly the command from Part 1. |
HTTP Request: no ergebnisliste |
No results. Broaden the search: bigger radiusKm , moredaysBack , simplerjobTitle |
Field missing (e.g. stellenangebotsTitel ) |
Check the actual field names in the Fetch jobs output and drag those instead |
| Can't find a field from an earlier node | Scroll down in the left panel. Earlier nodes are listed further down. Run them first if they show no data. |
"Paired item" error with Search settings |
Change .item to.first() in that expression |
"Paired item" error in Job + score |
Run the whole workflow with Execute workflow. Don't use .first() there. |
| Gemini error 429 | Free quota reached. Wait a minute, and keep Limit at 5β10 |
| Nothing after the Filter | No job scored 7 or more. Lower the number to test, or broaden the search |
| Email not arriving | Check spam. A login error means the app password is wrong |
| Second test run sends nothing | That's Remember seen jobs working. Press D on it to test again |
| Schedule ignores recent changes | Changes only count after you publish again |
| Schedule didn't run | Laptop asleep, Docker closed, or the n8n terminal was closed at 8 a.m. Check the Executions tab. |
- Where does AI make a decision in this workflow, and where does it just follow rules?
- What would happen without the
Remember seen jobsnode? - What are the limits of the AI pre-screening?
- Why run n8n locally instead of in the cloud? What are the pros and cons?
- Several searches: duplicate the workflow with a different job title or city.
- Better scoring: fetch each job's full description from the Arbeitsagentur details endpoint and give it to the AI. Hint: ask AI how to base64-encode
jobIdin an n8n expression. Test the details endpoint first, since the API changes without notice. - Weekly summary: add a second schedule that sends a Friday overview instead of daily emails.