# Expired Jobs

The expired endpoints (`expired-ats` and `expired-jb`) return arrays of internal job IDs for postings that have expired or been removed from their source. Match each ID against the `id` field in your stored copies of `ActiveAtsJob` / `ActiveJbJob` rows to invalidate stale records.

We re-check every job once per day until it is 6 months old, after which jobs are auto-expired.

## Coverage

| Endpoint | Sources tracked for expiration |
| --- | --- |
| `expired-ats` | All ATS sources (the full set listed under the `source` parameter on `active-ats`). |
| `expired-jb` | **LinkedIn only** (`source=linkedin`). `wellfound` and `ycombinator` listings are **not** re-checked for expiration and will never appear in this feed - if you ingest them you'll need your own freshness logic (e.g. age-out anything older than N days). |

## `time_frame` values

| Value | Type | Description |
| --- | --- | --- |
| `1h` | Rolling window | Listings that expired in the last hour. Refreshed continuously. Use this for high-frequency syncs (e.g. an hourly cron that mirrors the active feed). |
| `1d` | Daily snapshot | Expirations from the previous UTC day. Refreshed once per day at **01:00 UTC**. This is the most common choice for daily syncs - run your job after 01:00 UTC and you'll get a complete, stable snapshot of the previous day's expirations. **Note:** despite the name this is not a rolling 24-hour window. |
| `6m` | Full backfill | All expirations from the last 6 months in a single response. Very large; intented to use for multi-day outages, not daily polling. |


-- add note about how we remove expired jobs from endpoints