> ## Documentation Index
> Fetch the complete documentation index at: https://docs.recruitier.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Active Jobs

> A dedicated page for managing active job listings with filtering, sorting, and deactivation.

## What Are Active Jobs?

An "active" job in Recruitier is a listing that is still live on the company's career
page and presumably still accepting applications. Active jobs have not been detected as
expired, removed, or filled. Focusing your outreach on active jobs gives you the best
chance of reaching companies that are still in hiring mode.

Recruitier tracks the status of every job listing in its database. When a job is first
scraped, it is considered active (`is_active = true`, `is_expired = false`). Over time,
the platform monitors whether the listing remains available, and when it disappears, it
marks the job as expired.

<Tip>
  The Active Jobs page is your primary workspace when preparing outreach. It shows only
  active listings across all your searches, so every job you see is still open and
  actionable.
</Tip>

## The Active Jobs Page

The Active Jobs page is a **standalone page** accessible from the main navigation. Unlike
the all-jobs view (which shows all jobs including expired ones), this page exclusively
displays active listings from across all your searches in a single, dedicated table.

### Table Layout

The Active Jobs table displays one row per active job with the following columns:

| Column          | Details                                               |
| --------------- | ----------------------------------------------------- |
| **Title**       | The job title, clickable to open the detail page      |
| **Company**     | Hiring company name                                   |
| **Location**    | City/region where the job is based                    |
| **Date**        | When the job was posted or first discovered           |
| **Experience**  | Experience level (junior, medior, senior, lead)       |
| **Flexibility** | Remote, hybrid, or on-site                            |
| **Job Type**    | Full-time, part-time, contract, temporary, internship |
| **Employees**   | Company employee count                                |
| **Contacts**    | Number of discovered contacts for this job            |
| **Search**      | Which search produced this job                        |
| **Tags**        | Color-coded tag chips applied to the job              |

### Search Bar

At the top of the page, a **search bar** lets you filter jobs by typing keywords. The
search matches against job titles, company names, and locations, narrowing the table in
real time as you type.

### Filtering

The Active Jobs page provides several filter mechanisms in the toolbar:

* **Tag filter** -- Select one or more tags to show only jobs with those tags applied
* **Experience level** -- Filter by junior, medior, senior, or lead
* **Job type** -- Filter by full-time, part-time, contract, temporary, or internship
* **Flexibility** -- Filter by remote, hybrid, or on-site

These filters appear as dropdown selectors in the toolbar. Only filters that have
available options in your current data set are shown. A **Reset** button clears all
active filters when you want to return to the full view.

The page header shows the total number of active jobs and, when filters are applied,
the number of jobs matching your current filters.

### Sorting

A sort dropdown lets you sort the table by any sortable column. Click the direction
button (arrow up/down) to toggle between ascending and descending order. Sortable
columns include title, company name, location, posting date, and employee count.

### Deactivating Jobs

Each job row has a **Deactivate** action. Deactivating a job marks it as no longer
active in your collection, removing it from the Active Jobs page. This is different
from deleting a job -- the job remains in your overall collection and search results
but is no longer treated as an active opportunity.

<Steps>
  <Step title="Click Deactivate">
    Click the deactivate action on the job row you want to deactivate.
  </Step>

  <Step title="Confirm">
    A confirmation dialog appears explaining the action. Click **Deactivate** to
    confirm or **Cancel** to keep the job active.
  </Step>
</Steps>

<Warning>
  Deactivating a job removes it from the Active Jobs page. Use this when you know a
  position has been filled or is no longer relevant, even if Recruitier's automated
  checks have not yet detected the change.
</Warning>

## How Active Status Is Determined

Recruitier uses a multi-signal approach to determine whether a job is active:

### Liveliness Checks

Recruitier's scrapers periodically revisit the URLs of indexed job listings to verify
they are still live. If a listing returns a 404 error, redirects to a "position filled"
page, or is no longer present on the company's career page, it is flagged for expiration.

The liveliness check uses the `url` field stored on the ScrapedJob entity (which is
unique per listing) to verify the listing's continued availability.

### Expiration Detection

When a job fails a liveliness check, Recruitier marks it with:

* **`is_expired` = true** -- The listing is no longer available
* **`is_active` = false** -- The job is no longer considered active
* **`expired_at`** -- A timestamp recording when the expiration was detected

Once marked as expired, the job retains its full data (description, company, contacts)
but is no longer considered active in searches and filters.

### Last Seen Timestamp

Each scraped job has a **last\_seen** timestamp that updates every time the scraper
confirms the listing is still live. A job with a recent last\_seen date is almost
certainly still active. A job that has not been seen for weeks is more likely to have
been filled.

<Note>
  There can be a delay between a company removing a listing and Recruitier detecting the
  removal. Liveliness checks run periodically, not in real time. A job marked as active
  might have been filled hours ago but not yet checked. For time-sensitive outreach,
  consider opening the original listing URL to verify availability.
</Note>

## Using the Active Filter Elsewhere

In addition to the dedicated Active Jobs page, an "Active Only" filter is available in
other views throughout Recruitier.

### In Search Results

When viewing a search's results, use the **Active Only** toggle in the filter bar to
hide expired jobs. This immediately narrows your view to only those listings that
Recruitier believes are still accepting applications.

This filter is particularly valuable for:

* **Outreach preparation** -- Only contact companies about jobs that are still open
* **Candidate presentations** -- Only show your candidate jobs they can actually apply for
* **Pipeline focus** -- Concentrate your time on actionable opportunities
* **Batch operations** -- Select all visible (active) jobs for batch tagging

### In the All-Jobs View

The active filter is also available on the all-jobs page, where it works across your
entire collection regardless of which search produced the results. Combined with tag
filters, this creates a powerful "outreach-ready" view.

<Tip>
  Create a standard outreach workflow: filter by "Active only" + your "Shortlist" tag +
  "Senior" experience level. This gives you a focused list of your best, still-open
  opportunities for senior candidates -- ready for immediate outreach.
</Tip>

## Why Active Jobs Matter

Focusing on active jobs has a direct impact on your placement success rate:

### Higher Response Rates

Companies actively hiring are far more likely to respond to recruiter outreach. When you
contact a company about an active job, your message is timely and relevant -- the hiring
manager is thinking about this role right now.

### Better Candidate Experience

Presenting your candidate with jobs that are still open maintains trust. Nothing
undermines a recruiter-candidate relationship faster than recommending positions that
have already been filled.

### Efficient Time Management

Recruitment is a time-sensitive business. Every minute spent on an expired listing is a
minute not spent on a live opportunity. The active filter eliminates this waste.

### Competitive Advantage

Fresh listings -- especially those posted in the last few days -- represent opportunities
where the company has not yet been inundated with applications. Being among the first
recruiters to reach out about a newly posted role gives you a significant edge.

<Info>
  Studies in the Dutch recruitment market consistently show that response rates are
  highest in the first 48-72 hours after a job is posted. The monitoring feature combined
  with the active filter helps you capitalize on this window.
</Info>

## Expired Job Indicators

Even when you are not using the active filter, expired jobs are clearly marked:

| Indicator                | Location        | Description                                                                                |
| ------------------------ | --------------- | ------------------------------------------------------------------------------------------ |
| **Expired badge**        | Results table   | A visual badge on the job row in search results and all-jobs view                          |
| **Expired at timestamp** | Job detail page | The exact date and time when the listing was detected as expired (`expired_at`)            |
| **`is_expired` flag**    | Underlying data | A boolean field on ScrapedJob that frontend components use to render expiration indicators |
| **`is_active` flag**     | Underlying data | A boolean field that is the inverse of is\_expired, used for filtering                     |

The expired badge makes it easy to identify outdated listings even in an unfiltered view.
You can scan your results and mentally skip expired entries, or use the active filter to
remove them entirely.

## Recency as a Quality Signal

Beyond the active/expired distinction, Recruitier uses job recency as a quality signal
in its search ranking:

### Recency Bonus in Scoring

Recently posted jobs receive up to a **5% scoring boost** (RECENCY\_BONUS\_MAX) in search
results. This bonus decays linearly over 180 days:

| Days Since Posted | Recency Bonus |
| ----------------- | ------------- |
| 0 (today)         | +5.0%         |
| 30 days           | +4.2%         |
| 60 days           | +3.3%         |
| 90 days           | +2.5%         |
| 120 days          | +1.7%         |
| 150 days          | +0.8%         |
| 180+ days         | +0.0%         |

The bonus is additive -- it cannot replace a strong match score, but it gives recently
posted jobs a slight edge over older listings with similar relevance. This means your
results naturally favor fresh opportunities without sacrificing match quality.

### Posted Within Filter

During search creation (Step 4 of the wizard), you can limit results to jobs posted
within specific time windows:

* Past 24 hours
* Past week
* Past month
* Past 6 months
* Anytime (no restriction)

This filter is applied server-side and reduces the result set before scoring.

### Date Sorting

Sort your results by posting date to see the freshest listings first, regardless of
match score. This is useful after your initial review of top-scoring results to discover
recently posted jobs that may not have scored highest but represent fresh opportunities.

<Note>
  The posting date comes from the most reliable available source. Recruitier checks
  multiple sources in order of reliability: JSON-LD structured data, LLM extraction,
  HTML metadata, visible text, URL patterns, sitemaps, and finally the date Recruitier
  first discovered the listing. The source is tracked so the most accurate date is
  always used.
</Note>

## Monitoring for New Active Jobs

When you enable monitoring on a saved search, Recruitier continuously checks for new
job listings that match your criteria. When new active jobs are found, they are:

1. Added to your search results with a **"New"** badge (`is_new = true`)
2. Counted in the **new jobs counter** (`new_jobs_count`) on the search card
3. Delivered as a **notification** through the platform's real-time SSE notification
   system

This monitoring system ensures you are always aware of fresh opportunities without
manually re-running searches. See [Saved Searches](/jobs/search/saved-searches) for
more on search monitoring.

<Tip>
  For maximum responsiveness, keep the Recruitier interface open in a browser tab. SSE
  notifications update the new jobs counter in real time without requiring a page refresh.
  When you see a notification, open the search immediately to review the new matches
  while the listings are still fresh.
</Tip>

## Best Practices for Working with Active Jobs

* **Enable the active filter** as your default when reviewing results for outreach
* **Check posting dates** -- even active jobs that are several months old may have
  received many applications already
* **Act quickly on new matches** -- when monitoring alerts you to a new job, review it
  promptly to stay ahead of competing recruiters
* **Periodically review your collection** -- jobs that were active when you saved them
  may have expired since. Use the active filter to clean up your view.
* **Use the "posted within" filter** during search creation to focus on the freshest
  listings from the start
* **Do not discard expired jobs entirely** -- they can still provide valuable intelligence
  about a company's hiring patterns, team structure, and technology stack. Keep them for
  reference but deprioritize them for outreach.
* **Verify before outreach** -- For high-priority opportunities, open the original listing
  URL to confirm it is still live before investing time in a personalized outreach message

## Advanced

### Active Status in the Data Model

The active/expired status is tracked on the **ScrapedJob entity** (not the Job entity),
which means the status is shared across all users who have a Job linked to that ScrapedJob:

| Field        | Entity     | Type            | Description                                                    |
| ------------ | ---------- | --------------- | -------------------------------------------------------------- |
| `is_active`  | ScrapedJob | Boolean         | True when the listing is considered currently available        |
| `is_expired` | ScrapedJob | Boolean         | True when the listing has been detected as no longer available |
| `expired_at` | ScrapedJob | Timestamp       | When the expiration was detected                               |
| `url`        | ScrapedJob | String (unique) | The listing URL used for liveliness checks                     |

The Job entity accesses these fields via `@property` proxies, so `job.is_active` actually
reads `job.scraped_job.is_active`. This proxy pattern ensures that when a ScrapedJob's
status changes, all linked Job records automatically reflect the update without any
synchronization needed.

### How Liveliness Checking Works

The liveliness monitoring pipeline operates as follows:

1. **URL queue** -- Active ScrapedJob URLs are queued for periodic revisiting
2. **HTTP check** -- The scraper visits each URL and checks the HTTP response:
   * 200 OK with job content still present: job remains active
   * 404 Not Found: flagged for expiration
   * 301/302 redirect to a "position filled" or careers page: flagged for expiration
   * Job content removed from the page (URL exists but listing is gone): flagged for
     expiration
3. **Expiration marking** -- Flagged jobs have `is_active` set to false, `is_expired`
   set to true, and `expired_at` set to the current timestamp
4. **Cascade to searches** -- The updated status is immediately visible in all search
   results and job detail pages because of the proxy property architecture

### The Recency Bonus Formula

The recency bonus is calculated as:

```
days_old = (current_date - list_date).days
if days_old >= 180:
    bonus = 0
else:
    bonus = RECENCY_BONUS_MAX * (1 - days_old / 180)
```

Where `RECENCY_BONUS_MAX = 5%` (0.05 in decimal). This produces a linear decay from 5%
to 0% over 180 days.

The bonus is **additive** and is applied after the base score calculation. It can only
increase a job's score, never decrease it. This ensures that a 6-month-old excellent match
still scores higher than a brand-new poor match.

### Active Status and Search Filters

The `is_active` field is available as a **Qdrant-native filter**, which means it is
applied directly in the vector database query at search time. This is the fastest filter
path -- it reduces the candidate set before any scoring happens. When you create a search
with the "Active only" filter enabled, expired jobs are excluded at the vector search
level, not as a post-processing step.

In contrast, the "Active only" toggle on the results page operates as a client-side or
post-search filter on the already-returned results.

### Power-User Tips

<Accordion title="Using expired jobs for market intelligence">
  Do not ignore expired jobs entirely. They provide valuable intelligence:

  * **Hiring patterns**: A company that posted 3 Python Developer roles in the last 6
    months (now expired) is clearly building out their Python team. They may be open to
    hearing about candidates even without an active listing.
  * **Technology stack**: Expired listings reveal what technologies the company uses,
    helping you tailor future outreach.
  * **Salary benchmarks**: If expired listings included salary data, use it to set
    expectations for current negotiations with that company.
  * **Contact recycling**: Contacts discovered on expired listings are still valid. The
    hiring manager who posted the expired role is likely still the right person to
    contact for future roles.
</Accordion>

<Accordion title="Timing your outreach for maximum impact">
  The 48-72 hour window after a job is posted is your prime outreach window. Here is how
  to maximize it:

  1. Enable monitoring on your most important searches
  2. Keep Recruitier open in a browser tab for real-time SSE notifications
  3. When a new match notification arrives, review it immediately
  4. If it is a strong match, initiate outreach the same day
  5. Mention that you noticed the role was recently posted -- this shows responsiveness
</Accordion>

<Accordion title="Distinguishing truly active from technically active">
  A job can be technically active (still on the career page) but practically filled. Signs
  to watch for:

  * Posted more than 3 months ago with no updates
  * The company has posted a similar but not identical role recently (may indicate the
    original was filled and a new position opened)
  * The listing URL is live but the content has changed to indicate the process is in its
    final stages
    For these borderline cases, reach out but manage expectations -- mention that you
    noticed the role and ask whether it is still open.
</Accordion>

### Business Rules

* **Active is the default**: All newly scraped jobs start as active (`is_active = true`,
  `is_expired = false`)
* **Expiration is permanent**: Once a job is marked as expired, it does not revert to
  active, even if the URL becomes accessible again. This prevents false reactivations
  from temporary website issues.
* **Shared status**: Active/expired status is on the ScrapedJob, not the Job. If a listing
  expires, it appears expired for all users who have it in their collection.
* **Manual deactivation**: Users can deactivate a job from the Active Jobs page,
  removing it from the active view. This is useful when you know a position has been
  filled before the automated checks detect the change.

## Related

* [Saved Searches](/jobs/search/saved-searches) -- Monitoring searches for new active jobs
* [Search Results](/jobs/search/search-results) -- Using the active filter in results
* [Job Details](/jobs/job-details) -- Expired status indicators on the detail page
* [Saving & Managing Jobs](/jobs/saving-and-managing) -- Managing active and expired jobs
  in your collection
